diff --git a/.changeset/oxlint-ignorer-config-debut.md b/.changeset/oxlint-ignorer-config-debut.md
new file mode 100644
index 000000000..eb91da36e
--- /dev/null
+++ b/.changeset/oxlint-ignorer-config-debut.md
@@ -0,0 +1,11 @@
+---
+"@systemfsoftware/oxlint-ignorer-config": minor
+---
+
+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.
diff --git a/.changeset/stryker-ignorer-effect-schema-declarations-debut.md b/.changeset/stryker-ignorer-effect-schema-declarations-debut.md
new file mode 100644
index 000000000..ce41a84bb
--- /dev/null
+++ b/.changeset/stryker-ignorer-effect-schema-declarations-debut.md
@@ -0,0 +1,10 @@
+---
+"@systemfsoftware/stryker-ignorer-effect-schema-declarations": minor
+---
+
+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.
diff --git a/.changeset/stryker-ignorer-in-source-vitest-block-debut.md b/.changeset/stryker-ignorer-in-source-vitest-block-debut.md
new file mode 100644
index 000000000..d5ee5581a
--- /dev/null
+++ b/.changeset/stryker-ignorer-in-source-vitest-block-debut.md
@@ -0,0 +1,10 @@
+---
+"@systemfsoftware/stryker-ignorer-in-source-vitest-block": minor
+---
+
+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.
diff --git a/.changeset/stryker-ignorer-interface-ancestors.md b/.changeset/stryker-ignorer-interface-ancestors.md
new file mode 100644
index 000000000..740987bff
--- /dev/null
+++ b/.changeset/stryker-ignorer-interface-ancestors.md
@@ -0,0 +1,9 @@
+---
+"@systemfsoftware/stryker-ignorer-interface": minor
+---
+
+An ignorer's path is now `{ node, ancestors }`: the ancestors arrive as data, nearest first, so a
+`shouldIgnore` implementation no longer walks a parent chain itself. The parent-path member is gone.
+
+The AST vocabulary is now re-exported from `@oxc-project/types` under that package's own names and
+bundled into this package's declarations, so nothing extra needs installing.
diff --git a/.changeset/stryker-ignorer-interface-debut.md b/.changeset/stryker-ignorer-interface-debut.md
new file mode 100644
index 000000000..aa200dfb0
--- /dev/null
+++ b/.changeset/stryker-ignorer-interface-debut.md
@@ -0,0 +1,11 @@
+---
+"@systemfsoftware/stryker-ignorer-interface": minor
+---
+
+First release. This is the contract an ignorer is written against: the
+`PlainIgnorer` descriptor (`{ name, shouldIgnore }`), the structural `NodePath`
+an entry is called with, `UnknownNode`, `AstNodeType`, and the canonical AST
+node vocabulary, aliased from the parser that produces those nodes.
+
+The package ships no runtime value and no runtime dependencies, so adopting it
+costs nothing beyond the types.
diff --git a/.changeset/stryker-js-engine-plain-ignorer-loader.md b/.changeset/stryker-js-engine-plain-ignorer-loader.md
new file mode 100644
index 000000000..0fbc61d48
--- /dev/null
+++ b/.changeset/stryker-js-engine-plain-ignorer-loader.md
@@ -0,0 +1,5 @@
+---
+"@systemfsoftware/stryker-js-engine": minor
+---
+
+the plugin loader accepts modules exporting `strykerIgnorers` (`{ name, shouldIgnore }`, each entry registered as an `Ignore` contribution) — no `effect` peer, no plugin-interface import needed for an ignorer module; a plain entry whose `name` is not a string or whose `shouldIgnore` is not callable is rejected at load with `PluginLoadFailedError` naming the module; native `strykerPlugins` modules load unchanged, and the default `plugins:` preset now names `@systemfsoftware/stryker-ignorer-effect-schema-declarations` and `@systemfsoftware/stryker-ignorer-workflow-make-boundary` instead of the `@systemfsoftware/stryker-plugins` subpaths, so configs using the old names must move to the new packages
diff --git a/.changeset/stryker-plugins-home-migration.md b/.changeset/stryker-plugins-home-migration.md
deleted file mode 100644
index a12c1b838..000000000
--- a/.changeset/stryker-plugins-home-migration.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"@systemfsoftware/stryker-plugins": patch
----
-
-the types condition of every export subpath now points at the emitted dist/index.d.ts (the api-extractor rollup filename is gone), the effect peer range widens to ^4.0.0-rc.112, and repository metadata now points at stryker-js-effect
diff --git a/.gitattributes b/.gitattributes
index d65f86214..b311c290b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -13,3 +13,11 @@
*.dll binary
*.so binary
*.dylib binary
+
+# API Extractor writes its reports with CRLF. Keep the bytes it produces: a
+# checkout that normalises them to LF makes `api:check` see a spurious diff.
+**/etc/*.api.md -text
+
+# The preset's rolled-up declaration is written by API Extractor with CRLF, and
+# it is committed (unlike the other packages' dist declarations). Keep its bytes.
+packages/toolchain/oxlint-ignorer-config/lib/base.d.ts -text
diff --git a/.gitignore b/.gitignore
index cc57584af..1cb2d49c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ coverage/
.direnv/
result
result-*
+temp/
diff --git a/README.md b/README.md
index 4165c5235..d19c76e5a 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Code coverage only tells you which lines ran during tests; it cannot tell you if
| **Interrupted Runs** | `Ctrl+C` / cancel yields 0 reports | Emits partial report up to last completed mutant |
| **Incremental State** | `stryker-incremental.json` frequently goes stale | Reliable state validation surviving aborted runs |
| **Agent / CI Mode** | Generic CLI exit codes (`0` or `1`) | Machine mode auto-detection (`AGENT`, `CLAUDECODE`, `CODEX_SANDBOX`) + classified exit codes |
-| **Effect-TS Ecosystem** | Equivalent mutant noise on Schema & Brand types | Dedicated `@systemfsoftware/stryker-plugins` ignorers |
+| **Effect-TS Ecosystem** | Equivalent mutant noise on Schema & Brand types | Dedicated `@systemfsoftware/stryker-ignorer-*` ignorers |
| **Runtime Engine** | Procedural JavaScript with mutable state | Pure functional Effect 4 architecture with typed errors |
---
@@ -131,18 +131,18 @@ pnpm exec stryker run
This monorepo publishes a modular ecosystem of packages under the `@systemfsoftware` scope:
-| Package | Role |
-| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
-| [`@systemfsoftware/stryker-js-cli`](packages/stryker-js-cli) | Terminal & CI runner binary with NDJSON streaming output |
-| [`@systemfsoftware/stryker-js-language`](packages/stryker-js-language) | Pure domain core: mutant models, schemas, and exit classifications |
-| [`@systemfsoftware/stryker-js-plugin-interface`](packages/stryker-js-plugin-interface) | Shared plugin contracts (`declarePlugin`, `composePlugins`) |
-| [`@systemfsoftware/stryker-js-engine`](packages/stryker-js-engine) | Mutation run lifecycle engine and test orchestration |
-| [`@systemfsoftware/stryker-js-instrumenter`](packages/stryker-js-instrumenter) | AST mutation engine powered by OXC parser |
-| [`@systemfsoftware/stryker-js-vitest-runner`](packages/stryker-js-vitest-runner) | Vitest runner integration for mutation sandboxes |
-| [`@systemfsoftware/stryker-js-typescript-checker`](packages/stryker-js-typescript-checker) | TypeScript type-checker plugin validating mutants pre-execution |
-| [`@systemfsoftware/stryker-js-html-reporter`](packages/stryker-js-html-reporter) | Interactive HTML mutation report generator |
-| [`@systemfsoftware/stryker-plugins`](packages/stryker-plugins) | Domain ignorers for Effect Schema brands & tagged unions |
-| [`@systemfsoftware/stryker-test-contribution`](packages/stryker-test-contribution) | Suite hygiene plugin enforcing unique mutant kills per test file |
+| Package | Role |
+| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
+| [`@systemfsoftware/stryker-js-cli`](packages/stryker-js-cli) | Terminal & CI runner binary with NDJSON streaming output |
+| [`@systemfsoftware/stryker-js-language`](packages/stryker-js-language) | Pure domain core: mutant models, schemas, and exit classifications |
+| [`@systemfsoftware/stryker-js-plugin-interface`](packages/stryker-js-plugin-interface) | Shared plugin contracts (`declarePlugin`, `composePlugins`) |
+| [`@systemfsoftware/stryker-js-engine`](packages/stryker-js-engine) | Mutation run lifecycle engine and test orchestration |
+| [`@systemfsoftware/stryker-js-instrumenter`](packages/stryker-js-instrumenter) | AST mutation engine powered by OXC parser |
+| [`@systemfsoftware/stryker-js-vitest-runner`](packages/stryker-js-vitest-runner) | Vitest runner integration for mutation sandboxes |
+| [`@systemfsoftware/stryker-js-typescript-checker`](packages/stryker-js-typescript-checker) | TypeScript type-checker plugin validating mutants pre-execution |
+| [`@systemfsoftware/stryker-js-html-reporter`](packages/stryker-js-html-reporter) | Interactive HTML mutation report generator |
+| [`@systemfsoftware/stryker-ignorer-*`](packages/ignorers) | Decoupled domain ignorers (Schema declarations, vitest blocks, Workflow.make boundaries) |
+| [`@systemfsoftware/stryker-test-contribution`](packages/stryker-test-contribution) | Suite hygiene plugin enforcing unique mutant kills per test file |
---
@@ -158,7 +158,7 @@ This monorepo publishes a modular ecosystem of packages under the `@systemfsoftw
Why do mutants survive on my Effect Schema definitions?
-Schema definitions and branded type markers often produce equivalent mutants that cannot be observed or failed at runtime. Install `@systemfsoftware/stryker-plugins` and enable `effect-schema-ignorer` in your `stryker.config.json` to filter them out automatically.
+Schema definitions and branded type markers often produce equivalent mutants that cannot be observed or failed at runtime. Install `@systemfsoftware/stryker-ignorer-effect-schema-declarations` and enable `effect-schema-declarations` in your `stryker.config.json` to filter them out automatically.
diff --git a/docs/plans/2026-09-14-0520-refactor-ignorer-plain-entry-plan.md b/docs/plans/2026-09-14-0520-refactor-ignorer-plain-entry-plan.md
new file mode 100644
index 000000000..7d9f62951
--- /dev/null
+++ b/docs/plans/2026-09-14-0520-refactor-ignorer-plain-entry-plan.md
@@ -0,0 +1,60 @@
+---
+title: Ignorer Plain Entry - Corrected Contract
+type: refactor
+date: 2026-09-14
+artifact_contract: ce-unified-plan/v1
+artifact_readiness: implementation-ready
+execution: code
+supersedes: docs/plans/2026-09-14-0150-refactor-ignorer-standard-schema-plan.md
+---
+
+# Ignorer Plain Entry - Corrected Contract
+
+## Goal Capsule
+
+**Objective.** A plain ignorer module exports `strykerIgnorers: PlainIgnorer[]` where
+`PlainIgnorer = { name, shouldIgnore(path: NodePath): string | undefined }`. The host
+registers each entry as an `Ignore` contribution and fails the load by name when an entry's
+`name` is not a string or its `shouldIgnore` is not callable.
+
+**Correction.** The earlier plan required a `schema` member on every entry and had the engine
+re-declare that member's shape in `effect/Schema`. Both are removed at the user's direction:
+the descriptor carries no schema, and no shape is mirrored across the boundary.
+
+**Authority.** User direction in session (2026-09-14), then `AGENTS.md` and `CONSTITUTION.md`.
+
+**Verification.** `pnpm check:ci`; each package's `pnpm typecheck`, `pnpm run build`,
+`pnpm exec vitest run`, `pnpm run lint`; `attw` per package; engine loader suite proves the
+rejection path.
+
+## Requirements
+
+- R1. `PlainIgnorer` is `{ name, shouldIgnore }`; the interface publishes no descriptor schema.
+- R2. The engine validates exactly that shape (`S.Struct({ name: S.String, shouldIgnore: S.declare(isShouldIgnore) })`)
+ and registers entries as `Ignore` contributions.
+- R3. Standard Schema types come from `@standard-schema/spec` (jsr, catalog-pinned), imported
+ as a devDependency and inlined into the published declarations — no runtime dependency, no
+ hand-copied spec source.
+- R4. The interface keeps the AST vocabulary, `NodePath`, `ancestorsOf`, and the toolkit as the
+ ignorers' internal implementation.
+- R5. Docs, changesets, and the carried case tables state this contract.
+
+## Unit Map
+
+| Unit | Change | Verification |
+| ---- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
+| C1 | Interface: drop `PlainIgnorerSchema` and the `schema` field; import spec types from the package | interface typecheck/build/tests/lint; `dist/*.d.ts` free of `standard-schema/spec` |
+| C2 | Two ignorers: entries are `{ name, shouldIgnore }` | each package's suite (48/14 cases) |
+| C3 | Engine: `PlainIgnorerSchema` = name + callable `shouldIgnore`; fixture set re-aimed | engine suite; the invalid-entry fixture fails the load by name |
+| C4 | Docs/changesets | prose states the two-field contract |
+
+## Destructive Review
+
+**Assumptions.** (1) A host can meaningfully validate an ignorer without a declared schema;
+(2) the interface's AST types suffice for authoring; (3) the toolkit remains worth shipping
+internally. **Lens (inversion).** How would this be gamed? An entry could ignore nothing and
+load green — accepted: ignoring nothing is visible as an unmutated run, and the case tables
+pin each ignorer's real decisions. **Radical alternative.** Have the engine pass its own
+Effect schema converted with `Schema.toStandardSchemaV1`. Rejected: it makes the host dictate
+the input contract and re-couples authors to the host's toolkit. **Reconciliation.** No delta;
+the corrected contract stands.
diff --git a/docs/solutions/tooling-decisions/plain-entry-contract-without-a-declared-schema.md b/docs/solutions/tooling-decisions/plain-entry-contract-without-a-declared-schema.md
new file mode 100644
index 000000000..af42e318a
--- /dev/null
+++ b/docs/solutions/tooling-decisions/plain-entry-contract-without-a-declared-schema.md
@@ -0,0 +1,67 @@
+---
+title: A boundary contract needs the function it calls, not a schema it cannot use
+date: 2026-09-14
+category: tooling-decisions
+module: ignorer-family
+problem_type: tooling_decision
+component: tooling
+severity: medium
+applies_when:
+ - "Defining the descriptor a host loads from a plugin module"
+ - "Tempted to re-declare a foreign spec's types so a package can stay dependency-free"
+ - "Debating whether a loaded entry should carry a validator the host already calls"
+tags: [standard-schema, boundary, plugin-contract, inlining, types]
+---
+
+# A boundary contract needs the function it calls, not a schema it cannot use
+
+## Context
+
+The plain-ignorer family had the host decode every loaded entry against a `schema` member the
+ignorer declared, described as a Standard Schema validator (KTD1/KTD5 of the superseded plan).
+Two costs surfaced in review:
+
+- the host re-declared the member's shape in its own toolkit (`effect/Schema`), so the same
+ contract lived twice and could drift;
+- the spec's types were hand-copied into the interface package, which then had to stay in step
+ with `@standard-schema/spec` by hand.
+
+The host never used that validator for anything the decision function did not already do: a
+loaded entry is registered as an `Ignore` contribution and `shouldIgnore(path)` is called.
+
+## Guidance
+
+Ship the smallest contract the host actually consumes — `{ name, shouldIgnore }` — and let the
+package that owns the AST supply its types:
+
+```ts
+export interface PlainIgnorer {
+ readonly name: string
+ shouldIgnore(path: NodePath): string | undefined
+}
+```
+
+The host then owns its own load check and mirrors nothing:
+
+```ts
+export const PlainIgnorerSchema = S.Struct({
+ name: S.String,
+ shouldIgnore: S.declare(isShouldIgnore),
+})
+```
+
+Take the spec's types from the spec package, never from a hand copy, and inline them so the
+published declarations stay dependency-free: `@standard-schema/spec` (jsr, catalog-pinned) as a
+devDependency, resolved into the package's emitted declarations by the build (build output, not
+tracked). Verified after `pnpm run build` in `packages/ignorers/interface` by reading its emitted
+declarations — no `@standard-schema/spec` reference survives there.
+
+The corollary for a host that wants a validator: convert the host's own schema where it needs
+one (`Schema.toStandardSchemaV1`), rather than pushing a validator requirement onto the plugin
+author.
+
+## Applicability
+
+Applies to any host/plugin boundary where the host validates an entry and then calls a function
+on it. Where the host genuinely decides on the declared validator (filtering, generating, or
+composing from it), keep the member — and keep it sourced from the spec package, inlined.
diff --git a/dprint.json b/dprint.json
index edd9b321a..c99b20785 100644
--- a/dprint.json
+++ b/dprint.json
@@ -16,10 +16,12 @@
"**/dist",
"**/*-lock.json",
"pnpm-lock.yaml",
+ "**/etc/*.api.md",
"coverage/**",
"**/snapshots/*.md",
"repos/**",
- "packages/stryker-js-typescript-checker/testResources/**"
+ "packages/stryker-js-typescript-checker/testResources/**",
+ "packages/toolchain/oxlint-ignorer-config/lib/base.d.ts"
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.96.1.wasm",
diff --git a/package.json b/package.json
index 553533482..259f42562 100644
--- a/package.json
+++ b/package.json
@@ -23,8 +23,8 @@
"clean": "turbo clean",
"format": "./bin/dprint fmt",
"format:check": "./bin/dprint check",
- "gate:dist": "turbo --concurrency=${TURBO_CONCURRENCY:-100%} build",
- "gate:tasks": "turbo --concurrency=${TURBO_CONCURRENCY:-50%} --continue lint typecheck typecheck:node test",
+ "gate:dist": "turbo --concurrency=${TURBO_CONCURRENCY:-100%} build ${TURBO_BASE:+--filter=...[$TURBO_BASE]}",
+ "gate:tasks": "turbo --concurrency=${TURBO_CONCURRENCY:-50%} --continue lint typecheck test ${TURBO_BASE:+--filter=...[$TURBO_BASE]}",
"lint": "turbo --concurrency=${TURBO_CONCURRENCY:-50%} lint",
"precommit": "pnpm exec lint-staged --no-revert --config .lintstagedrc.js",
"prepare": "husky",
diff --git a/packages/stryker-plugins/.attw.json b/packages/ignorers/effect-schema-declarations/.attw.json
similarity index 100%
rename from packages/stryker-plugins/.attw.json
rename to packages/ignorers/effect-schema-declarations/.attw.json
diff --git a/packages/ignorers/effect-schema-declarations/.gitignore b/packages/ignorers/effect-schema-declarations/.gitignore
new file mode 100644
index 000000000..09c81c214
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/.gitignore
@@ -0,0 +1,4 @@
+dist
+temp
+coverage
+reports
diff --git a/packages/ignorers/effect-schema-declarations/AGENTS.md b/packages/ignorers/effect-schema-declarations/AGENTS.md
new file mode 100644
index 000000000..1024ced4c
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/AGENTS.md
@@ -0,0 +1,32 @@
+# AGENTS.md — `@systemfsoftware/stryker-ignorer-effect-schema-declarations`
+
+Stryker ignorer for Effect Schema declarations: removes proven-equivalent mutants on brands, `TaggedClass`/`TaggedError` tags and field schemas, `optionalWith` defaults, and documentation annotations. The AST guards are this package's own predicates over the node shapes, whose types come from `@systemfsoftware/stryker-ignorer-interface` — the package's one dependency, and it ships types only; no Effect is imported, bundled, or depended on. Root `AGENTS.md` governs.
+
+## Rules
+
+| ID | Rule | Gate |
+| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
+| **SP1** | An ignored mutant is proven-equivalent: mutating the tag/brand field produces identical behavior. | `pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations test` |
+| **SP2** | Every new ignore pattern arrives with a test demonstrating the equivalent mutant. | `pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations test` |
+| **SP3** | Register through the plain ignorer protocol only; never bypass other mutation stages. | `review` |
+| **SP4** | No `effect` or `@effect/*` import in any source or built file, and no `effect` or `@effect/*` key in any dependency block — the preset's `no-restricted-imports` ban is the import gate. | `pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations lint` |
+| **SP5** | Every decision is pinned by a case table — a committed test carries no FastCheck generator. | `pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations lint` |
+| **SP6** | A committed test asserts intended behavior, never a stored-output snapshot. | `review` |
+
+SP3's reviewer decides one thing: whether the module reaches mutation through the plain ignorer protocol alone.
+
+- `wrong:` exporting a StrykerJS plugin object, a reporter, or a mutator.
+- `right:` exporting `strykerIgnorers`, the protocol the loader decodes.
+
+SP6's reviewer decides one thing: whether every expected value is an oracle the decision under test did not produce.
+
+- `wrong:` `expect(shouldIgnore(path)).toMatchSnapshot()` — the snapshot re-records whatever the rule currently answers.
+- `right:` `expect(shouldIgnore(path)).toBe(SYMBOL_DESCRIPTION_IGNORED)` — the exported reason constant, written from the rule's intent.
+
+## Verification
+
+```bash
+pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations typecheck
+pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations test
+pnpm --filter @systemfsoftware/stryker-ignorer-effect-schema-declarations lint
+```
diff --git a/packages/stryker-plugins/LICENSE b/packages/ignorers/effect-schema-declarations/LICENSE
similarity index 100%
rename from packages/stryker-plugins/LICENSE
rename to packages/ignorers/effect-schema-declarations/LICENSE
diff --git a/packages/stryker-plugins/README.md b/packages/ignorers/effect-schema-declarations/README.md
similarity index 55%
rename from packages/stryker-plugins/README.md
rename to packages/ignorers/effect-schema-declarations/README.md
index 43830a6d3..5967180b4 100644
--- a/packages/stryker-plugins/README.md
+++ b/packages/ignorers/effect-schema-declarations/README.md
@@ -1,26 +1,31 @@
-# @systemfsoftware/stryker-plugins
+# @systemfsoftware/stryker-ignorer-effect-schema-declarations
-
-
+
+
> Stop Effect `Schema` declarations from dragging your Stryker score below 100%.
-A brand description, a `_tag`, a `title` — mutate any of them and the source changes but the behaviour does not, so no test can ever kill the mutant. Those unkillable mutants sit in your report forever, indistinguishable from real coverage gaps. This [Stryker](https://stryker-mutator.io) Ignore plugin removes them, so the score that remains is behaviour.
+A brand description, a `_tag`, a `title` — mutate any of them and the source changes but the behaviour does not, so no test can ever kill the mutant. Those unkillable mutants sit in your report forever, indistinguishable from real coverage gaps. This [Stryker](https://stryker-mutator.io) ignorer removes them, so the score that remains is behaviour.
## Install
```bash
-pnpm add -D @systemfsoftware/stryker-plugins
+pnpm add -D @systemfsoftware/stryker-ignorer-effect-schema-declarations
```
-In `stryker.config.json`:
+In `stryker.config.json`, the plugin module and the ignorer name travel as a pair:
+
+| Config key | Value |
+| ---------- | ------------------------------------------------------------- |
+| `plugins` | `@systemfsoftware/stryker-ignorer-effect-schema-declarations` |
+| `ignorers` | `effect-schema-declarations` |
```jsonc
{
"plugins": [
"@systemfsoftware/stryker-js-vitest-runner",
"@systemfsoftware/stryker-js-typescript-checker",
- "@systemfsoftware/stryker-plugins"
+ "@systemfsoftware/stryker-ignorer-effect-schema-declarations"
],
"ignorers": ["effect-schema-declarations"]
}
@@ -28,8 +33,18 @@ In `stryker.config.json`:
Mutants it recognizes are reported as `Ignored`, each carrying the reason it was safe to skip.
+> [!WARNING]
+> A name in `ignorers` that no loaded plugin answers is silently skipped — the run proceeds and the mutants stay in your score. Copy the name from the table, not from memory.
+
> [!NOTE]
-> `@systemfsoftware/stryker-js-plugin-api` is a peer dependency — your Stryker install provides it. `effect` is a direct dependency (the plugin decodes AST nodes with `Schema`).
+> Requires `@systemfsoftware/stryker-js-engine` 4.1.0 or later (the first version whose plugin loader speaks the plain ignorer protocol). The package has no peer dependencies, exactly one dependency — `@systemfsoftware/stryker-ignorer-interface`, which supplies AST node types only — and no Effect anywhere, not in `dependencies`, not in `devDependencies`, and not on its published surface. The guards are this package's own predicates over those node shapes.
+
+## Migrating from `@systemfsoftware/stryker-plugins`
+
+| Before | After |
+| ------------------------------------------------- | ---------------------------------------------------------------------------- |
+| `"plugins": ["@systemfsoftware/stryker-plugins"]` | `"plugins": ["@systemfsoftware/stryker-ignorer-effect-schema-declarations"]` |
+| `"ignorers": ["effect-schema-declarations"]` | unchanged |
## What it ignores
diff --git a/packages/ignorers/effect-schema-declarations/api-extractor.json b/packages/ignorers/effect-schema-declarations/api-extractor.json
new file mode 100644
index 000000000..c2811a0b5
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/api-extractor.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/index.d.ts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/main/",
+ "reportFileName": "stryker-ignorer-effect-schema-declarations.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/index.d.ts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/ignorers/effect-schema-declarations/etc/stryker-ignorer-effect-schema-declarations.api.md b/packages/ignorers/effect-schema-declarations/etc/stryker-ignorer-effect-schema-declarations.api.md
new file mode 100644
index 000000000..ef601e671
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/etc/stryker-ignorer-effect-schema-declarations.api.md
@@ -0,0 +1,50 @@
+## API Report File for "@systemfsoftware/stryker-ignorer-effect-schema-declarations"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import { NodePath } from '@systemfsoftware/stryker-ignorer-interface';
+
+// @public (undocumented)
+export const ANNOTATION_OBJECT_IGNORED: 'annotations object holding only documentation is a declaration, not behaviour';
+
+// @public (undocumented)
+export const ANNOTATION_TEXT_IGNORED: 'annotation documentation value is declaration data, not behaviour';
+
+// @public (undocumented)
+export const BRAND_NAME_IGNORED: 'Schema.brand name is identity-only data, not behaviour';
+
+// @public (undocumented)
+export const CLASS_FIELDS_IGNORED: 'Schema.Class field schema is a declaration, not behaviour';
+
+// @public (undocumented)
+export const CLASS_ID_IGNORED: 'Schema.Class identifier is a declaration name, not behaviour';
+
+// @public (undocumented)
+export const decideSchemaDeclarationIgnore: (node: unknown, parent: unknown, grandparent?: unknown, ancestor?: unknown) => string | undefined;
+
+// @public (undocumented)
+export const firstIgnoreReason: (path: NodePath) => string | undefined;
+
+// @public (undocumented)
+export const OPTIONAL_DEFAULT_IGNORED: 'optionalWith default value is config, not behaviour';
+
+// @public (undocumented)
+export const strykerIgnorers: {
+ name: string;
+ shouldIgnore: typeof firstIgnoreReason;
+}[];
+
+// @public (undocumented)
+export const SYMBOL_DESCRIPTION_IGNORED: 'Symbol.for() brand description is identity-only data, not behaviour';
+
+// @public (undocumented)
+export const TAGGED_FIELDS_IGNORED: 'TaggedClass/TaggedError field schema is a declaration, not behaviour';
+
+// @public (undocumented)
+export const TAGGED_TAG_IGNORED: 'TaggedClass/TaggedError _tag is a declaration discriminant, not behaviour';
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/ignorers/effect-schema-declarations/oxlint.config.ts b/packages/ignorers/effect-schema-declarations/oxlint.config.ts
new file mode 100644
index 000000000..879f8a966
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/oxlint.config.ts
@@ -0,0 +1,6 @@
+import preset from '@systemfsoftware/oxlint-ignorer-config'
+import { defineConfig } from 'oxlint'
+
+export default defineConfig({
+ extends: [preset],
+})
diff --git a/packages/ignorers/effect-schema-declarations/package.json b/packages/ignorers/effect-schema-declarations/package.json
new file mode 100644
index 000000000..584ef370a
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/package.json
@@ -0,0 +1,71 @@
+{
+ "name": "@systemfsoftware/stryker-ignorer-effect-schema-declarations",
+ "license": "Apache-2.0",
+ "version": "0.0.0",
+ "author": "Ryan Lee ",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
+ "directory": "packages/ignorers/effect-schema-declarations"
+ },
+ "homepage": "https://github.com/systemfsoftware/stryker-js-effect/tree/main/packages/ignorers/effect-schema-declarations#readme",
+ "bugs": {
+ "url": "https://github.com/systemfsoftware/stryker-js-effect/issues"
+ },
+ "description": "Stryker ignorer that removes proven-equivalent mutants on Effect Schema declarations — brands, tags, field schemas, defaults, and documentation annotations.",
+ "keywords": [
+ "stryker",
+ "stryker-mutator",
+ "mutation-testing",
+ "effect-schema",
+ "ignorer"
+ ],
+ "type": "module",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "build": "tsdown && pnpm api:check",
+ "clean": "rimraf dist",
+ "typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
+ "test": "vitest run",
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
+ "attw": "attw --pack .",
+ "api:check": "tsdown && api-extractor run",
+ "api:update": "api-extractor run --local"
+ },
+ "dependencies": {
+ "@systemfsoftware/stryker-ignorer-interface": "workspace:^"
+ },
+ "devDependencies": {
+ "@microsoft/api-extractor": "catalog:",
+ "@systemfsoftware/arethetypeswrong-cli": "catalog:",
+ "@systemfsoftware/oxlint-ignorer-config": "workspace:^",
+ "@systemfsoftware/tsconfig": "catalog:",
+ "@systemfsoftware/vitest-config": "workspace:^",
+ "@types/node": "catalog:",
+ "oxlint": "catalog:",
+ "rimraf": "catalog:",
+ "tsdown": "catalog:",
+ "typescript": "catalog:",
+ "vitest": "catalog:"
+ },
+ "publishConfig": {
+ "access": "public",
+ "provenance": true,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ }
+ }
+}
diff --git a/packages/ignorers/effect-schema-declarations/src/SchemaDeclarationIgnore.ts b/packages/ignorers/effect-schema-declarations/src/SchemaDeclarationIgnore.ts
new file mode 100644
index 000000000..1c14ff1ba
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/src/SchemaDeclarationIgnore.ts
@@ -0,0 +1,225 @@
+import type {
+ ArrowFunctionExpression,
+ CallExpression,
+ Expression,
+ IdentifierReference,
+ MemberExpression,
+ ObjectExpression,
+ ObjectProperty,
+ StringLiteral,
+} from '@systemfsoftware/stryker-ignorer-interface'
+
+const DOCUMENTATION_KEYS: Record = {
+ identifier: true,
+ description: true,
+ title: true,
+ documentation: true,
+ examples: true,
+}
+
+export type DocumentationKey = 'identifier' | 'description' | 'title' | 'documentation' | 'examples'
+
+interface TypedNode {
+ readonly type: string
+}
+
+const isObject = (value: unknown): value is object => typeof value === 'object' && value !== null
+
+const hasStringType = (value: object): value is TypedNode => 'type' in value && typeof value.type === 'string'
+
+const isAstNode = (value: unknown): value is TypedNode => isObject(value) && hasStringType(value)
+
+const isNodeOfType = (value: unknown, type: string): value is TypedNode => isAstNode(value) && value.type === type
+
+const hasStringName = (value: TypedNode): boolean => 'name' in value && typeof value.name === 'string'
+
+const hasStringValue = (value: TypedNode): boolean => 'value' in value && typeof value.value === 'string'
+
+const isDocumentationKey = (value: unknown): value is DocumentationKey =>
+ typeof value === 'string' && DOCUMENTATION_KEYS[value] === true
+
+const hasDocumentationName = (value: TypedNode): boolean => 'name' in value && isDocumentationKey(value.name)
+
+const hasDocumentationValue = (value: TypedNode): boolean => 'value' in value && isDocumentationKey(value.value)
+
+const isIdentifierKeyNode = (value: unknown): boolean =>
+ isNodeOfType(value, 'Identifier') && hasDocumentationName(value)
+
+const isLiteralKeyNode = (value: unknown): boolean => isNodeOfType(value, 'Literal') && hasDocumentationValue(value)
+
+const isDocumentationKeyNode = (value: unknown): boolean => isIdentifierKeyNode(value) || isLiteralKeyNode(value)
+
+const hasObjectNode = (value: TypedNode): boolean => 'object' in value && isAstNode(value.object)
+
+const hasPropertyNode = (value: TypedNode): boolean => 'property' in value && isAstNode(value.property)
+
+const hasMemberEnds = (value: TypedNode): boolean => hasObjectNode(value) && hasPropertyNode(value)
+
+const isAstNodeArray = (value: unknown): value is ReadonlyArray =>
+ Array.isArray(value) && value.every(isAstNode)
+
+const hasCalleeNode = (value: TypedNode): boolean => 'callee' in value && isAstNode(value.callee)
+
+const hasArgumentNodes = (value: TypedNode): boolean => 'arguments' in value && isAstNodeArray(value.arguments)
+
+const hasCallEnds = (value: TypedNode): boolean => hasCalleeNode(value) && hasArgumentNodes(value)
+
+const hasDocumentationKeyNode = (value: TypedNode): boolean => 'key' in value && isDocumentationKeyNode(value.key)
+
+const hasComputedFalse = (value: TypedNode): boolean => 'computed' in value && value.computed === false
+
+const hasDocumentationPropertyFields = (value: TypedNode): boolean =>
+ hasComputedFalse(value) && hasDocumentationKeyNode(value)
+
+const hasDocumentationEntries = (value: ReadonlyArray): boolean =>
+ value.length > 0 && value.every(isDocumentationProperty)
+
+const isDocumentationArray = (value: unknown): value is ReadonlyArray =>
+ Array.isArray(value) && hasDocumentationEntries(value)
+
+const hasPropertiesField = (value: TypedNode): boolean =>
+ 'properties' in value && isDocumentationArray(value.properties)
+
+export const isIdentifier = (value: unknown): value is IdentifierReference =>
+ isNodeOfType(value, 'Identifier') && hasStringName(value)
+
+export const isStringLiteral = (value: unknown): value is StringLiteral =>
+ isNodeOfType(value, 'Literal') && hasStringValue(value)
+
+export const isObjectExpression = (value: unknown): value is ObjectExpression => isNodeOfType(value, 'ObjectExpression')
+
+export const isArrowFunctionExpression = (value: unknown): value is ArrowFunctionExpression =>
+ isNodeOfType(value, 'ArrowFunctionExpression')
+
+export const isMemberExpression = (value: unknown): value is MemberExpression =>
+ isNodeOfType(value, 'MemberExpression') && hasMemberEnds(value)
+
+export const isCallExpression = (value: unknown): value is CallExpression =>
+ isNodeOfType(value, 'CallExpression') && hasCallEnds(value)
+
+export const isDocumentationProperty = (value: unknown): value is ObjectProperty =>
+ isNodeOfType(value, 'Property') && hasDocumentationPropertyFields(value)
+
+export const isDocumentationObject = (value: unknown): value is ObjectExpression =>
+ isNodeOfType(value, 'ObjectExpression') && hasPropertiesField(value)
+
+export const SYMBOL_DESCRIPTION_IGNORED = 'Symbol.for() brand description is identity-only data, not behaviour' as const
+export const TAGGED_TAG_IGNORED = 'TaggedClass/TaggedError _tag is a declaration discriminant, not behaviour' as const
+export const TAGGED_FIELDS_IGNORED = 'TaggedClass/TaggedError field schema is a declaration, not behaviour' as const
+export const CLASS_ID_IGNORED = 'Schema.Class identifier is a declaration name, not behaviour' as const
+export const CLASS_FIELDS_IGNORED = 'Schema.Class field schema is a declaration, not behaviour' as const
+export const BRAND_NAME_IGNORED = 'Schema.brand name is identity-only data, not behaviour' as const
+export const OPTIONAL_DEFAULT_IGNORED = 'optionalWith default value is config, not behaviour' as const
+export const ANNOTATION_OBJECT_IGNORED =
+ 'annotations object holding only documentation is a declaration, not behaviour' as const
+export const ANNOTATION_TEXT_IGNORED = 'annotation documentation value is declaration data, not behaviour' as const
+
+const TAGGED_FACTORIES: readonly string[] = ['TaggedClass', 'TaggedError']
+
+/**
+ * `Schema.Class` is curried the other way round from `Schema.TaggedClass`.
+ *
+ * `S.TaggedClass()('tag', fields)` puts both the discriminant and the fields on the outer
+ * call, so one callee predicate reaches both. `S.Class('Id')(fields)` puts the identifier on
+ * the *inner* call and the fields on the outer one, so the same declaration data needs two
+ * predicates. Missing that shape is why a class-shaped schema kept fourteen mutants a
+ * tag-shaped one never had.
+ */
+const CLASS_FACTORY = 'Class'
+
+const isIdentifierNamed = (node: TypedNode, name: string): boolean => isIdentifier(node) && node.name === name
+
+const isIdentifierIn = (node: TypedNode, names: readonly string[]): boolean =>
+ isIdentifier(node) && names.includes(node.name)
+
+const isMemberNamed = (member: MemberExpression, object: string, property: string): boolean =>
+ isIdentifierNamed(member.object, object) && isIdentifierNamed(member.property, property)
+
+const isNamedMember = (node: Expression, object: string, property: string): boolean =>
+ isMemberExpression(node) && isMemberNamed(node, object, property)
+
+const isSymbolForCallee = (callee: Expression): boolean => isNamedMember(callee, 'Symbol', 'for')
+
+const isNamedFactoryReference = (reference: Expression, names: readonly string[]): boolean =>
+ isMemberExpression(reference) && isIdentifierIn(reference.property, names)
+
+const isTaggedFactoryReference = (reference: Expression): boolean =>
+ isNamedFactoryReference(reference, TAGGED_FACTORIES)
+
+const isClassFactoryReference = (reference: Expression): boolean => isNamedFactoryReference(reference, [CLASS_FACTORY])
+
+const isBrandCallee = (callee: Expression): boolean =>
+ isMemberExpression(callee) && isIdentifierNamed(callee.property, 'brand')
+
+const isTaggedFactoryCallee = (callee: Expression): boolean =>
+ isCallExpression(callee) && isTaggedFactoryReference(callee.callee)
+
+const isArgumentAt = (
+ node: unknown,
+ call: CallExpression,
+ index: number,
+ calleeMatches: (callee: Expression) => boolean,
+): boolean => calleeMatches(call.callee) && call.arguments[index] === node
+
+const isArgumentOf = (
+ node: unknown,
+ parent: unknown,
+ index: number,
+ calleeMatches: (callee: Expression) => boolean,
+): boolean => isCallExpression(parent) && isArgumentAt(node, parent, index, calleeMatches)
+
+interface IgnoreRule {
+ readonly matches: (node: unknown, parent: unknown, grandparent: unknown, ancestor: unknown) => boolean
+ readonly reason: string
+}
+
+const isOptionalWithCallee = (callee: Expression): boolean => isNamedMember(callee, 'S', 'optionalWith')
+
+const isAnnotationsCallee = (callee: Expression): boolean =>
+ isMemberExpression(callee) && isIdentifierNamed(callee.property, 'annotations')
+
+const argumentRule = (
+ is: (node: unknown) => node is TypedNode,
+ argumentIndex: number,
+ calleeMatches: (callee: Expression) => boolean,
+ reason: string,
+): IgnoreRule => ({
+ matches: (node, parent) => is(node) && isArgumentOf(node, parent, argumentIndex, calleeMatches),
+ reason,
+})
+
+/**
+ * A documentation-keyed entry of an `annotations` call. Unlike the object rule
+ * this does not care what sits beside it: `title` is documentation whether or
+ * not an `arbitrary` shares the object, because replacing the title cannot
+ * change what the schema does. Emptying the whole object could, which is why
+ * that rule is the stricter of the two.
+ */
+const isDocumentationValue = (node: unknown, parent: ObjectProperty): boolean => parent.value === node
+
+const isDocumentationPropertyValue = (node: unknown, parent: unknown): boolean =>
+ isDocumentationProperty(parent) && isDocumentationValue(node, parent)
+
+const documentationValueRule: IgnoreRule = {
+ matches: (node, parent, grandparent, ancestor) =>
+ isDocumentationPropertyValue(node, parent) && isArgumentOf(grandparent, ancestor, 0, isAnnotationsCallee),
+ reason: ANNOTATION_TEXT_IGNORED,
+}
+
+const RULES: readonly IgnoreRule[] = [
+ argumentRule(isStringLiteral, 0, isSymbolForCallee, SYMBOL_DESCRIPTION_IGNORED),
+ argumentRule(isStringLiteral, 0, isTaggedFactoryCallee, TAGGED_TAG_IGNORED),
+ argumentRule(isObjectExpression, 1, isTaggedFactoryCallee, TAGGED_FIELDS_IGNORED),
+ argumentRule(isStringLiteral, 0, isClassFactoryReference, CLASS_ID_IGNORED),
+ argumentRule(isStringLiteral, 0, isBrandCallee, BRAND_NAME_IGNORED),
+ argumentRule(isArrowFunctionExpression, 1, isOptionalWithCallee, OPTIONAL_DEFAULT_IGNORED),
+ argumentRule(isDocumentationObject, 0, isAnnotationsCallee, ANNOTATION_OBJECT_IGNORED),
+ documentationValueRule,
+]
+
+export const decideSchemaDeclarationIgnore = (
+ node: unknown,
+ parent: unknown,
+ grandparent?: unknown,
+ ancestor?: unknown,
+): string | undefined => RULES.find((rule) => rule.matches(node, parent, grandparent, ancestor))?.reason
diff --git a/packages/ignorers/effect-schema-declarations/src/mod.ts b/packages/ignorers/effect-schema-declarations/src/mod.ts
new file mode 100644
index 000000000..8c78fba33
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/src/mod.ts
@@ -0,0 +1,28 @@
+import type { NodePath } from '@systemfsoftware/stryker-ignorer-interface'
+
+import { decideSchemaDeclarationIgnore } from './SchemaDeclarationIgnore.js'
+
+const decisionAt = (chain: readonly unknown[], position: number): string | undefined =>
+ decideSchemaDeclarationIgnore(chain[position], chain[position + 1], chain[position + 2], chain[position + 3])
+
+const firstIgnoreReason = (path: NodePath): string | undefined => {
+ const chain = [path.node, ...path.ancestors]
+ return chain.reduce((found, _, position) => found ?? decisionAt(chain, position), undefined)
+}
+
+export const strykerIgnorers = [
+ { name: 'effect-schema-declarations', shouldIgnore: firstIgnoreReason },
+]
+
+export {
+ ANNOTATION_OBJECT_IGNORED,
+ ANNOTATION_TEXT_IGNORED,
+ BRAND_NAME_IGNORED,
+ CLASS_FIELDS_IGNORED,
+ CLASS_ID_IGNORED,
+ decideSchemaDeclarationIgnore,
+ OPTIONAL_DEFAULT_IGNORED,
+ SYMBOL_DESCRIPTION_IGNORED,
+ TAGGED_FIELDS_IGNORED,
+ TAGGED_TAG_IGNORED,
+} from './SchemaDeclarationIgnore.js'
diff --git a/packages/stryker-plugins/tests/__fixtures__/EffectSchemaAst.fixtures.ts b/packages/ignorers/effect-schema-declarations/tests/__fixtures__/EffectSchemaAst.fixtures.ts
similarity index 67%
rename from packages/stryker-plugins/tests/__fixtures__/EffectSchemaAst.fixtures.ts
rename to packages/ignorers/effect-schema-declarations/tests/__fixtures__/EffectSchemaAst.fixtures.ts
index 760d3cc5d..c1f5e56b4 100644
--- a/packages/stryker-plugins/tests/__fixtures__/EffectSchemaAst.fixtures.ts
+++ b/packages/ignorers/effect-schema-declarations/tests/__fixtures__/EffectSchemaAst.fixtures.ts
@@ -1,5 +1,3 @@
-import { FastCheck as fc } from 'effect/testing'
-
interface Identifier {
readonly type: 'Identifier'
readonly name: string
@@ -46,27 +44,11 @@ export const taggedCall = (factory: string, tag: AstNode, fields: AstNode): Call
export const bareFactoryCall = (factory: string, tag: AstNode, fields: AstNode): CallExpression =>
callOf(callOf(identifier(factory), []), [tag, fields])
-/**
- * `Schema.Class('Id')(fields)` - the identifier rides the inner call and the fields the
- * outer one, which is the opposite of `taggedCall`'s arrangement and the shape the ignorer
- * missed until the class rules were added.
- */
export const classCall = (id: AstNode, fields: AstNode): CallExpression =>
callOf(callOf(memberOf('Schema', 'Class'), [id]), [fields])
-/** `X.pipe(S.brand('Name'))`'s inner call - the brand name is identity data. */
export const brandCall = (name: AstNode): CallExpression => callOf(memberOf('S', 'brand'), [name])
-export const taggedFactory = fc.constantFrom('TaggedClass', 'TaggedError')
-
-export const nonTaggedFactory = fc.constantFrom('Struct', 'Class', 'Union', 'TaggedRequest', 'tag', 'Literal')
-
-export const nonSymbolForMember = fc.oneof(
- fc.tuple(fc.constant('Symbol'), fc.constantFrom('iterator', 'keyFor', 'description')),
- fc.tuple(fc.constantFrom('Reflect', 'Object', 'globalThis'), fc.constant('for')),
- fc.tuple(fc.constantFrom('Reflect', 'Match', 'Effect'), fc.constantFrom('tag', 'gen', 'sync')),
-)
-
export interface PropertyNode {
readonly type: 'Property'
readonly computed: boolean
@@ -101,9 +83,3 @@ export const objectExpression = (properties: readonly PropertyNode[] = []): Obje
})
export const annotationsCall = (argument: AstNode): CallExpression => callOf(memberOf('S', 'annotations'), [argument])
-
-export const documentationKey = fc.constantFrom('identifier', 'description', 'title', 'documentation', 'examples')
-
-export const behaviourKey = fc.constantFrom('arbitrary', 'pretty', 'equivalence', 'message', 'jsonSchema')
-
-export const nonAnnotationsMethod = fc.constantFrom('filter', 'transform', 'pipe', 'brand', 'annotate')
diff --git a/packages/ignorers/effect-schema-declarations/tests/effect-schema-declarations.test.ts b/packages/ignorers/effect-schema-declarations/tests/effect-schema-declarations.test.ts
new file mode 100644
index 000000000..be4632f8f
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/tests/effect-schema-declarations.test.ts
@@ -0,0 +1,408 @@
+import { type NodePath } from '@systemfsoftware/stryker-ignorer-interface'
+import { describe, expect, it } from 'vitest'
+
+import {
+ ANNOTATION_OBJECT_IGNORED,
+ ANNOTATION_TEXT_IGNORED,
+ BRAND_NAME_IGNORED,
+ CLASS_ID_IGNORED,
+ OPTIONAL_DEFAULT_IGNORED,
+ strykerIgnorers,
+ SYMBOL_DESCRIPTION_IGNORED,
+ TAGGED_FIELDS_IGNORED,
+ TAGGED_TAG_IGNORED,
+} from '@systemfsoftware/stryker-ignorer-effect-schema-declarations'
+
+import {
+ annotationsCall,
+ bareFactoryCall,
+ brandCall,
+ callOf,
+ classCall,
+ identifier,
+ memberOf,
+ namedProperty,
+ objectExpression,
+ objectOf,
+ propertyOf,
+ stringLiteral,
+ symbolForCall,
+ taggedCall,
+} from './__fixtures__/EffectSchemaAst.fixtures.js'
+
+const brandDescription = stringLiteral('MyBrand')
+const brandDescriptionCall = symbolForCall(brandDescription)
+
+const keyForDescription = stringLiteral('desc')
+const keyForCall = callOf(memberOf('Symbol', 'keyFor'), [keyForDescription])
+
+const objectForDescription = stringLiteral('desc')
+const objectForCall = callOf(memberOf('Object', 'for'), [objectForDescription])
+
+const iteratorDescription = stringLiteral('desc')
+const iteratorCall = callOf(memberOf('Symbol', 'iterator'), [iteratorDescription])
+
+const unheldDescription = stringLiteral('MyBrand')
+const otherDescription = stringLiteral('Other')
+const unheldDescriptionCall = symbolForCall(otherDescription)
+
+const objectAtSymbolFor = objectExpression()
+const objectAtSymbolForCall = symbolForCall(objectAtSymbolFor)
+
+const taggedTag = stringLiteral('myTag')
+const taggedFields = objectExpression()
+const taggedClassCall = taggedCall('TaggedClass', taggedTag, taggedFields)
+
+const taggedErrorTag = stringLiteral('err')
+const taggedErrorFields = objectExpression()
+const taggedErrorCall = taggedCall('TaggedError', taggedErrorTag, taggedErrorFields)
+
+const structTag = stringLiteral('tag')
+const structFields = objectExpression()
+const structCall = taggedCall('Struct', structTag, structFields)
+
+const swappedFields = objectExpression()
+const swappedTag = stringLiteral('tag')
+const swappedCall = taggedCall('TaggedClass', swappedFields, swappedTag)
+
+const bareTag = stringLiteral('someTag')
+const bareFields = objectExpression()
+const bareFactoryCallNode = bareFactoryCall('TaggedClass', bareTag, bareFields)
+
+const classId = stringLiteral('ChildPolicyConfig')
+const classFields = objectExpression()
+const classOuterCall = classCall(classId, classFields)
+const classInnerCall = classOuterCall.callee
+
+const acceptedLiteralMember = stringLiteral('permanent')
+const acceptedLiteralCall = callOf(memberOf('Schema', 'Literal'), [
+ acceptedLiteralMember,
+ { type: 'Literal', value: 'transient' },
+])
+
+const brandName = stringLiteral('MaxChildren')
+const brandNameCall = brandCall(brandName)
+
+const optionalDefault = { type: 'ArrowFunctionExpression' }
+const optionalSchema = memberOf('S', 'String')
+const optionalWithCall = callOf(memberOf('S', 'optionalWith'), [optionalSchema, optionalDefault])
+const stringDefault = stringLiteral('x')
+const optionalWithStringCall = callOf(memberOf('S', 'optionalWith'), [optionalSchema, stringDefault])
+const optionalCall = callOf(memberOf('S', 'optional'), [optionalSchema, optionalDefault])
+
+const matchTag = stringLiteral('a')
+const matchTagCall = callOf(memberOf('Match', 'tag'), [matchTag])
+
+const orphanIdentifier = identifier('x')
+
+const identifierEntry = namedProperty('identifier', stringLiteral('HexBytes'))
+const descriptionEntry = namedProperty('description', {
+ type: 'Literal',
+ value: 'Uint8Array encoded as a lowercase hex string',
+})
+const titleEntry = namedProperty('title', stringLiteral('Hex Bytes'))
+const documentationOnly = objectOf([identifierEntry, descriptionEntry, titleEntry])
+const documentationOnlyCall = annotationsCall(documentationOnly)
+
+const singleIdentifierEntry = namedProperty('identifier', stringLiteral('HexBytes'))
+const identifierOnly = objectOf([singleIdentifierEntry])
+const identifierOnlyCall = annotationsCall(identifierOnly)
+
+const quotedKeyObject = objectOf([propertyOf(stringLiteral('identifier'), stringLiteral('HexBytes'))])
+const quotedKeyCall = annotationsCall(quotedKeyObject)
+
+const computedKeyEntry = propertyOf(identifier('identifier'), stringLiteral('x'), true)
+const computedKeyObject = objectOf([computedKeyEntry])
+const computedKeyCall = annotationsCall(computedKeyObject)
+
+const mixedBehaviourEntry = namedProperty('arbitrary', { type: 'ArrowFunctionExpression' })
+const mixedDocumentationEntry = namedProperty('identifier', stringLiteral('HexStringInput'))
+const mixedAnnotations = objectOf([mixedBehaviourEntry, mixedDocumentationEntry])
+const mixedAnnotationsCall = annotationsCall(mixedAnnotations)
+
+const behaviourOnlyEntry = namedProperty('arbitrary', { type: 'ArrowFunctionExpression' })
+const behaviourOnlyObject = objectOf([behaviourOnlyEntry])
+const behaviourOnlyCall = annotationsCall(behaviourOnlyObject)
+
+const documentationBesideBehaviourEntry = namedProperty('description', stringLiteral('x'))
+const behaviourBesideDocumentationEntry = namedProperty('arbitrary', { type: 'ArrowFunctionExpression' })
+const documentationAndBehaviour = objectOf([documentationBesideBehaviourEntry, behaviourBesideDocumentationEntry])
+const documentationAndBehaviourCall = annotationsCall(documentationAndBehaviour)
+
+const emptyAnnotations = objectOf([])
+const emptyAnnotationsCall = annotationsCall(emptyAnnotations)
+
+const displacedDocumentation = objectOf([namedProperty('title', stringLiteral('Hex Bytes'))])
+const displacedDocumentationCall = callOf(memberOf('S', 'annotations'), [
+ stringLiteral('other'),
+ displacedDocumentation,
+])
+
+const bareCalleeDocumentation = objectOf([namedProperty('title', stringLiteral('Hex Bytes'))])
+const bareCalleeCall = callOf(identifier('annotations'), [bareCalleeDocumentation])
+
+const filteredDocumentationEntry = namedProperty('identifier', stringLiteral('x'))
+const filteredDocumentation = objectOf([filteredDocumentationEntry])
+const filteredDocumentationCall = callOf(memberOf('S', 'filter'), [filteredDocumentation])
+
+const uncalledDocumentationEntry = namedProperty('identifier', stringLiteral('x'))
+const uncalledDocumentation = objectOf([uncalledDocumentationEntry])
+
+const secondArgumentDocumentationEntry = namedProperty('identifier', stringLiteral('x'))
+const secondArgumentDocumentation = objectOf([secondArgumentDocumentationEntry])
+const secondArgumentCall = callOf(memberOf('S', 'annotations'), [
+ stringLiteral('other'),
+ secondArgumentDocumentation,
+])
+
+const descriptor = strykerIgnorers[0]
+if (descriptor === undefined) throw new Error('the package publishes one ignorer descriptor')
+
+const CASES = {
+ ignored: [
+ {
+ name: 'a `Symbol.for` description, which names a brand and carries no behaviour',
+ path: { node: brandDescription, ancestors: [brandDescriptionCall] },
+ reason: SYMBOL_DESCRIPTION_IGNORED,
+ },
+ {
+ name: 'a `TaggedClass` tag, which only labels the declaration',
+ path: { node: taggedTag, ancestors: [taggedClassCall] },
+ reason: TAGGED_TAG_IGNORED,
+ },
+ {
+ name: 'a `TaggedError` tag',
+ path: { node: taggedErrorTag, ancestors: [taggedErrorCall] },
+ reason: TAGGED_TAG_IGNORED,
+ },
+ {
+ name: 'a `TaggedClass` fields object, whose schema declares rather than decides',
+ path: { node: taggedFields, ancestors: [taggedClassCall] },
+ reason: TAGGED_FIELDS_IGNORED,
+ },
+ {
+ name: 'a `TaggedError` fields object',
+ path: { node: taggedErrorFields, ancestors: [taggedErrorCall] },
+ reason: TAGGED_FIELDS_IGNORED,
+ },
+ {
+ name: 'the arrow function `S.optionalWith` holds as a default',
+ path: { node: optionalDefault, ancestors: [optionalWithCall] },
+ reason: OPTIONAL_DEFAULT_IGNORED,
+ },
+ {
+ name: 'the identifier of a `Schema.Class`, which rides the inner call',
+ path: { node: classId, ancestors: [classInnerCall] },
+ reason: CLASS_ID_IGNORED,
+ },
+ {
+ name: 'a brand name, which is identity data like a description',
+ path: { node: brandName, ancestors: [brandNameCall] },
+ reason: BRAND_NAME_IGNORED,
+ },
+ {
+ name: 'an `annotations` object whose every entry documents',
+ path: { node: documentationOnly, ancestors: [documentationOnlyCall] },
+ reason: ANNOTATION_OBJECT_IGNORED,
+ },
+ {
+ name: 'the identifier value inside a documentation-only object',
+ path: { node: identifierEntry.value, ancestors: [identifierEntry, documentationOnly, documentationOnlyCall] },
+ reason: ANNOTATION_TEXT_IGNORED,
+ },
+ {
+ name: 'the description value inside a documentation-only object',
+ path: { node: descriptionEntry.value, ancestors: [descriptionEntry, documentationOnly, documentationOnlyCall] },
+ reason: ANNOTATION_TEXT_IGNORED,
+ },
+ {
+ name: 'the title value inside a documentation-only object',
+ path: { node: titleEntry.value, ancestors: [titleEntry, documentationOnly, documentationOnlyCall] },
+ reason: ANNOTATION_TEXT_IGNORED,
+ },
+ {
+ name: 'a documentation entry that shares its object with a generator',
+ path: {
+ node: mixedDocumentationEntry.value,
+ ancestors: [mixedDocumentationEntry, mixedAnnotations, mixedAnnotationsCall],
+ },
+ reason: ANNOTATION_TEXT_IGNORED,
+ },
+ {
+ name: 'an `annotations` object holding one identifier entry',
+ path: { node: identifierOnly, ancestors: [identifierOnlyCall] },
+ reason: ANNOTATION_OBJECT_IGNORED,
+ },
+ {
+ name: 'the value of that single identifier entry',
+ path: {
+ node: singleIdentifierEntry.value,
+ ancestors: [singleIdentifierEntry, identifierOnly, identifierOnlyCall],
+ },
+ reason: ANNOTATION_TEXT_IGNORED,
+ },
+ {
+ name: 'an `annotations` object keyed by string literals',
+ path: { node: quotedKeyObject, ancestors: [quotedKeyCall] },
+ reason: ANNOTATION_OBJECT_IGNORED,
+ },
+ {
+ name: 'a description entry beside an `arbitrary` generator',
+ path: {
+ node: documentationBesideBehaviourEntry.value,
+ ancestors: [documentationBesideBehaviourEntry, documentationAndBehaviour, documentationAndBehaviourCall],
+ },
+ reason: ANNOTATION_TEXT_IGNORED,
+ },
+ ],
+ kept: [
+ {
+ name: 'a bare `TaggedClass` factory call keeps its tag',
+ path: { node: bareTag, ancestors: [bareFactoryCallNode] },
+ },
+ {
+ name: 'a bare `TaggedClass` factory call keeps its fields',
+ path: { node: bareFields, ancestors: [bareFactoryCallNode] },
+ },
+ {
+ name: 'a description in `Symbol.keyFor`, whose member is not `for`',
+ path: { node: keyForDescription, ancestors: [keyForCall] },
+ },
+ {
+ name: 'a description in `Object.for`, whose object is not `Symbol`',
+ path: { node: objectForDescription, ancestors: [objectForCall] },
+ },
+ {
+ name: 'a description in `Symbol.iterator`, whose member name differs',
+ path: { node: iteratorDescription, ancestors: [iteratorCall] },
+ },
+ {
+ name: 'a string literal default under `S.optionalWith`',
+ path: { node: stringDefault, ancestors: [optionalWithStringCall] },
+ },
+ {
+ name: 'an arrow function default under `S.optional`, whose callee differs',
+ path: { node: optionalDefault, ancestors: [optionalCall] },
+ },
+ {
+ name: 'an `annotations` object mixing a generator with documentation',
+ path: { node: mixedAnnotations, ancestors: [mixedAnnotationsCall] },
+ },
+ {
+ name: 'the generator value in a mixed `annotations` object',
+ path: {
+ node: mixedBehaviourEntry.value,
+ ancestors: [mixedBehaviourEntry, mixedAnnotations, mixedAnnotationsCall],
+ },
+ },
+ {
+ name: 'an `annotations` object holding only a generator',
+ path: { node: behaviourOnlyObject, ancestors: [behaviourOnlyCall] },
+ },
+ {
+ name: 'the generator value in a generator-only `annotations` object',
+ path: { node: behaviourOnlyEntry.value, ancestors: [behaviourOnlyEntry, behaviourOnlyObject, behaviourOnlyCall] },
+ },
+ {
+ name: 'an empty `annotations` object, which has no documentation entry',
+ path: { node: emptyAnnotations, ancestors: [emptyAnnotationsCall] },
+ },
+ {
+ name: 'an `annotations` object holding documentation beside a generator',
+ path: { node: documentationAndBehaviour, ancestors: [documentationAndBehaviourCall] },
+ },
+ {
+ name: 'a documentation object at the second argument of `S.annotations`',
+ path: { node: displacedDocumentation, ancestors: [displacedDocumentationCall] },
+ },
+ {
+ name: 'a documentation object under a bare `annotations` callee',
+ path: { node: bareCalleeDocumentation, ancestors: [bareCalleeCall] },
+ },
+ {
+ name: 'a `Schema.Class` fields object, which carries accepted value sets',
+ path: { node: classFields, ancestors: [classOuterCall] },
+ },
+ {
+ name: 'an accepted literal inside a `Schema.Literal` call',
+ path: { node: acceptedLiteralMember, ancestors: [acceptedLiteralCall] },
+ },
+ {
+ name: 'a `Schema.Struct` tag, which is not a tagged factory',
+ path: { node: structTag, ancestors: [structCall] },
+ },
+ {
+ name: 'a `Schema.Struct` fields object',
+ path: { node: structFields, ancestors: [structCall] },
+ },
+ {
+ name: 'an object expression at a `TaggedClass` tag slot',
+ path: { node: swappedFields, ancestors: [swappedCall] },
+ },
+ {
+ name: 'an object expression at a `Symbol.for` argument slot',
+ path: { node: objectAtSymbolFor, ancestors: [objectAtSymbolForCall] },
+ },
+ {
+ name: 'a string that is not the `Symbol.for` argument',
+ path: { node: unheldDescription, ancestors: [unheldDescriptionCall] },
+ },
+ {
+ name: 'a `Match.tag` argument, which is a runtime discriminator',
+ path: { node: matchTag, ancestors: [matchTagCall] },
+ },
+ {
+ name: 'an orphan identifier with no parent',
+ path: { node: orphanIdentifier },
+ },
+ {
+ name: 'a documentation object under `S.filter`',
+ path: { node: filteredDocumentation, ancestors: [filteredDocumentationCall] },
+ },
+ {
+ name: 'a documentation value under `S.filter`',
+ path: {
+ node: filteredDocumentationEntry.value,
+ ancestors: [filteredDocumentationEntry, filteredDocumentation, filteredDocumentationCall],
+ },
+ },
+ {
+ name: 'a documentation value whose enclosing call is absent',
+ path: { node: uncalledDocumentationEntry.value, ancestors: [uncalledDocumentationEntry, uncalledDocumentation] },
+ },
+ {
+ name: 'a documentation value at the second argument of `S.annotations`',
+ path: {
+ node: secondArgumentDocumentationEntry.value,
+ ancestors: [secondArgumentDocumentationEntry, secondArgumentDocumentation, secondArgumentCall],
+ },
+ },
+ {
+ name: 'an `annotations` object whose documentation key is computed',
+ path: { node: computedKeyObject, ancestors: [computedKeyCall] },
+ },
+ {
+ name: 'the value under a computed documentation key',
+ path: { node: computedKeyEntry.value, ancestors: [computedKeyEntry, computedKeyObject, computedKeyCall] },
+ },
+ ],
+}
+
+interface CasePath {
+ readonly node: unknown
+ readonly ancestors?: readonly unknown[] | undefined
+}
+
+const pathOf = (spec: CasePath): NodePath => ({ node: spec.node, ancestors: spec.ancestors ?? [] })
+
+describe('effect-schema-declarations', () => {
+ it('Should_Register_The_Descriptor', () => {
+ expect(descriptor.name).toBe('effect-schema-declarations')
+ })
+ it.each(CASES.ignored)('ignores: $name', (testCase) => {
+ expect(descriptor.shouldIgnore(pathOf(testCase.path))).toBe(testCase.reason)
+ })
+ it.each(CASES.kept)('keeps: $name', (testCase) => {
+ expect(descriptor.shouldIgnore(pathOf(testCase.path))).toBeUndefined()
+ })
+})
diff --git a/packages/stryker-plugins/tsconfig.build.json b/packages/ignorers/effect-schema-declarations/tsconfig.build.json
similarity index 100%
rename from packages/stryker-plugins/tsconfig.build.json
rename to packages/ignorers/effect-schema-declarations/tsconfig.build.json
diff --git a/packages/stryker-plugins/tsconfig.json b/packages/ignorers/effect-schema-declarations/tsconfig.json
similarity index 88%
rename from packages/stryker-plugins/tsconfig.json
rename to packages/ignorers/effect-schema-declarations/tsconfig.json
index e3e0889bc..04c392e87 100644
--- a/packages/stryker-plugins/tsconfig.json
+++ b/packages/ignorers/effect-schema-declarations/tsconfig.json
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
- "extends": ["@systemfsoftware/tsconfig/tsc/dom/library-monorepo", "@systemfsoftware/tsconfig/effect"],
+ "extends": ["@systemfsoftware/tsconfig/tsc/dom/library-monorepo"],
"compilerOptions": {
"types": []
},
diff --git a/packages/stryker-plugins/tsconfig.node.json b/packages/ignorers/effect-schema-declarations/tsconfig.node.json
similarity index 79%
rename from packages/stryker-plugins/tsconfig.node.json
rename to packages/ignorers/effect-schema-declarations/tsconfig.node.json
index 6895d764b..4d32e1ec2 100644
--- a/packages/stryker-plugins/tsconfig.node.json
+++ b/packages/ignorers/effect-schema-declarations/tsconfig.node.json
@@ -1,4 +1,4 @@
{
"extends": "@systemfsoftware/tsconfig/node",
- "include": ["oxlint.config.ts", "tsdown.config.ts", "vitest.config.ts", "vitest-setup.ts"]
+ "include": ["oxlint.config.ts", "tsdown.config.ts", "vitest.config.ts"]
}
diff --git a/packages/stryker-plugins/tsdown.config.ts b/packages/ignorers/effect-schema-declarations/tsdown.config.ts
similarity index 50%
rename from packages/stryker-plugins/tsdown.config.ts
rename to packages/ignorers/effect-schema-declarations/tsdown.config.ts
index 988226568..6a4bc1e03 100644
--- a/packages/stryker-plugins/tsdown.config.ts
+++ b/packages/ignorers/effect-schema-declarations/tsdown.config.ts
@@ -3,16 +3,10 @@ import { defineConfig } from 'tsdown'
export default defineConfig({
entry: {
index: './src/mod.ts',
- 'effect-schema-ignorer': './src/effect-schema-ignorer/index.ts',
- 'in-source-test-ignorer': './src/in-source-test-ignorer/index.ts',
- 'workflow-make-ignorer': './src/workflow-make-ignorer/index.ts',
},
format: 'esm',
dts: true,
tsconfig: './tsconfig.build.json',
clean: true,
outExtensions: () => ({ js: '.mjs', dts: '.d.ts' }),
- deps: {
- onlyBundle: false,
- },
})
diff --git a/packages/ignorers/effect-schema-declarations/vitest.config.ts b/packages/ignorers/effect-schema-declarations/vitest.config.ts
new file mode 100644
index 000000000..6536c4494
--- /dev/null
+++ b/packages/ignorers/effect-schema-declarations/vitest.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig, sharedConfig } from '@systemfsoftware/vitest-config'
+
+export default defineConfig({
+ ...sharedConfig,
+ test: {
+ ...sharedConfig.test,
+ include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
+ },
+})
diff --git a/packages/ignorers/in-source-vitest-block/.attw.json b/packages/ignorers/in-source-vitest-block/.attw.json
new file mode 100644
index 000000000..76f3418da
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/.attw.json
@@ -0,0 +1 @@
+{ "ignoreRules": ["no-resolution", "cjs-resolves-to-esm"] }
diff --git a/packages/ignorers/in-source-vitest-block/.gitignore b/packages/ignorers/in-source-vitest-block/.gitignore
new file mode 100644
index 000000000..09c81c214
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/.gitignore
@@ -0,0 +1,4 @@
+dist
+temp
+coverage
+reports
diff --git a/packages/ignorers/in-source-vitest-block/AGENTS.md b/packages/ignorers/in-source-vitest-block/AGENTS.md
new file mode 100644
index 000000000..6e3b15576
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/AGENTS.md
@@ -0,0 +1,27 @@
+# AGENTS.md — `@systemfsoftware/stryker-ignorer-in-source-vitest-block`
+
+Stryker ignorer for in-source Vitest blocks: removes mutants inside an `if (import.meta.vitest)` guard. The guards are hand-written predicates declared beside the decisions, and the package's only dependency is `@systemfsoftware/stryker-ignorer-interface`, which ships the node types and nothing that runs — zero Effect is part of its identity. Root `AGENTS.md` governs.
+
+## Rules
+
+| ID | Rule | Gate |
+| ------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
+| **SP1** | An ignored mutant is unreachable in a mutation run: it lives inside an `if (import.meta.vitest)` block — test code, not production behaviour. | `pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block test` |
+| **SP2** | Every new ignore pattern arrives with a test demonstrating the unreachable mutant. | `pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block test` |
+| **SP3** | Register through the plain ignorer protocol only; never bypass other mutation stages. | `review` |
+| **SP4** | No `effect` or `@effect/*` import in any source or built file — the preset's `no-restricted-imports` ban is the gate. | `pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block lint` |
+| **SP5** | No `effect` or `@effect/*` key in any dependency block; the interface package is the only runtime dependency. | `review` |
+| **SP6** | Every behavior is pinned by a case table — no FastCheck generator and no snapshot call in a committed test. | `pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block lint` |
+
+The reviewer's decision on each `review`-gated row, shown as `wrong:`/`right:`:
+
+- **SP3** — `wrong:` registering a checker or runner contribution beside the ignorer; `right:` `src/mod.ts` exports `strykerIgnorers` and nothing that answers another mutation stage.
+- **SP5** — `wrong:` `"effect": "catalog:"` in `dependencies` or anywhere under `devDependencies`; `right:` `"@systemfsoftware/stryker-ignorer-interface": "workspace:^"` as the only `dependencies` entry.
+
+## Verification
+
+```bash
+pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block typecheck
+pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block test
+pnpm --filter @systemfsoftware/stryker-ignorer-in-source-vitest-block lint
+```
diff --git a/packages/ignorers/in-source-vitest-block/LICENSE b/packages/ignorers/in-source-vitest-block/LICENSE
new file mode 100644
index 000000000..896c3747c
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/LICENSE
@@ -0,0 +1,203 @@
+Copyright (c) 2026 Ryan Lee (systemfsoftware)
+
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2026 Ryan Lee (systemfsoftware)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/packages/ignorers/in-source-vitest-block/README.md b/packages/ignorers/in-source-vitest-block/README.md
new file mode 100644
index 000000000..c3de8ffb2
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/README.md
@@ -0,0 +1,71 @@
+# @systemfsoftware/stryker-ignorer-in-source-vitest-block
+
+
+
+
+> In-source tests are not production behaviour. Stop their mutants from dragging your Stryker score below 100%.
+
+Your `if (import.meta.vitest)` blocks are stripped before production, so nothing that runs in a mutation run can ever reach their mutants. They sit in the report forever, indistinguishable from real coverage gaps. This [Stryker](https://stryker-mutator.io) ignorer removes them, so the score that remains is production behaviour.
+
+## Install
+
+```bash
+pnpm add -D @systemfsoftware/stryker-ignorer-in-source-vitest-block
+```
+
+In `stryker.config.json`, the plugin module and the ignorer name travel as a pair:
+
+| Config key | Value |
+| ---------- | --------------------------------------------------------- |
+| `plugins` | `@systemfsoftware/stryker-ignorer-in-source-vitest-block` |
+| `ignorers` | `in-source-vitest-block` |
+
+```jsonc
+{
+ "plugins": [
+ "@systemfsoftware/stryker-js-vitest-runner",
+ "@systemfsoftware/stryker-js-typescript-checker",
+ "@systemfsoftware/stryker-ignorer-in-source-vitest-block"
+ ],
+ "ignorers": ["in-source-vitest-block"]
+}
+```
+
+Mutants it recognizes are reported as `Ignored`, each carrying the reason it was safe to skip.
+
+> [!WARNING]
+> A name in `ignorers` that no loaded plugin answers is silently skipped — the run proceeds and the mutants stay in your score. Copy the name from the table, not from memory.
+
+> [!NOTE]
+> Requires `@systemfsoftware/stryker-js-engine` 4.1.0 or later (the first version whose plugin loader speaks the plain ignorer protocol). The package has no peer dependencies, no Effect dependency at all, and exactly one dependency — `@systemfsoftware/stryker-ignorer-interface`, whose node types declare the AST shapes the guards read.
+
+## Migrating from `@systemfsoftware/stryker-plugins`
+
+| Before | After |
+| ------------------------------------------------- | ------------------------------------------------------------------------ |
+| `"plugins": ["@systemfsoftware/stryker-plugins"]` | `"plugins": ["@systemfsoftware/stryker-ignorer-in-source-vitest-block"]` |
+| `"ignorers": ["in-source-vitest-block"]` | unchanged |
+
+## What it ignores
+
+| Guard shape | Example |
+| ---------------------------------------------------------- | ----------------------------------------------- |
+| The bare Vitest flag | `if (import.meta.vitest) { … }` |
+| The flag on either side of a comparison | `if (import.meta.vitest === undefined) { … }` |
+| Any mutant whose ancestors include one of the guards above | the whole guarded block, test fixtures included |
+
+## Where the line is
+
+The ignored mutants are _unreachable_, not merely believed equivalent: the guard is false under the mutation run, so no test can exercise what is inside it. That is a different claim from a proven-equivalent mutant, and it is why the reason constant says so.
+
+- A flag on a different meta property (`import.meta.env`) or on a non-import meta (`require.meta.vitest`) is **not** a guard — its mutants stay live.
+- A bare `import.meta.vitest` expression without an enclosing `if` is **not** a guard.
+- Only the guard shape is recognized; ordinary `if` statements keep every mutant, because their branches are reached in production.
+
+## Contributing
+
+Development setup and workflow: [AGENTS.md](AGENTS.md).
+
+## License
+
+[Apache 2.0](LICENSE)
diff --git a/packages/ignorers/in-source-vitest-block/api-extractor.json b/packages/ignorers/in-source-vitest-block/api-extractor.json
new file mode 100644
index 000000000..f2ca4f1f0
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/api-extractor.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/index.d.ts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/main/",
+ "reportFileName": "stryker-ignorer-in-source-vitest-block.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/index.d.ts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/ignorers/in-source-vitest-block/etc/stryker-ignorer-in-source-vitest-block.api.md b/packages/ignorers/in-source-vitest-block/etc/stryker-ignorer-in-source-vitest-block.api.md
new file mode 100644
index 000000000..1b1ccc70a
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/etc/stryker-ignorer-in-source-vitest-block.api.md
@@ -0,0 +1,29 @@
+## API Report File for "@systemfsoftware/stryker-ignorer-in-source-vitest-block"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import { NodePath } from '@systemfsoftware/stryker-ignorer-interface';
+
+// @public (undocumented)
+export const decideInSourceTestIgnore: (ancestors: Iterable) => string | undefined;
+
+// @public (undocumented)
+export const firstIgnoreReason: (path: NodePath) => string | undefined;
+
+// @public (undocumented)
+export const IN_SOURCE_TEST_IGNORED: 'inside an `if (import.meta.vitest)` block — test code, not production behaviour';
+
+// @public (undocumented)
+export const isInSourceTestGuard: (node: unknown) => boolean;
+
+// @public (undocumented)
+export const strykerIgnorers: {
+ name: string;
+ shouldIgnore: typeof firstIgnoreReason;
+}[];
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/ignorers/in-source-vitest-block/oxlint.config.ts b/packages/ignorers/in-source-vitest-block/oxlint.config.ts
new file mode 100644
index 000000000..879f8a966
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/oxlint.config.ts
@@ -0,0 +1,6 @@
+import preset from '@systemfsoftware/oxlint-ignorer-config'
+import { defineConfig } from 'oxlint'
+
+export default defineConfig({
+ extends: [preset],
+})
diff --git a/packages/ignorers/in-source-vitest-block/package.json b/packages/ignorers/in-source-vitest-block/package.json
new file mode 100644
index 000000000..4223ac6f9
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "@systemfsoftware/stryker-ignorer-in-source-vitest-block",
+ "license": "Apache-2.0",
+ "version": "0.0.0",
+ "author": "Ryan Lee ",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
+ "directory": "packages/ignorers/in-source-vitest-block"
+ },
+ "homepage": "https://github.com/systemfsoftware/stryker-js-effect/tree/main/packages/ignorers/in-source-vitest-block#readme",
+ "bugs": {
+ "url": "https://github.com/systemfsoftware/stryker-js-effect/issues"
+ },
+ "description": "Stryker ignorer that removes mutants inside `if (import.meta.vitest)` guards — test code unreachable in a mutation run.",
+ "keywords": [
+ "stryker",
+ "stryker-mutator",
+ "mutation-testing",
+ "ignorer"
+ ],
+ "type": "module",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "build": "tsdown && pnpm api:check",
+ "clean": "rimraf dist",
+ "typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
+ "test": "vitest run",
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
+ "attw": "attw --pack .",
+ "api:check": "tsdown && api-extractor run",
+ "api:update": "api-extractor run --local"
+ },
+ "dependencies": {
+ "@systemfsoftware/stryker-ignorer-interface": "workspace:^"
+ },
+ "devDependencies": {
+ "@microsoft/api-extractor": "catalog:",
+ "@systemfsoftware/arethetypeswrong-cli": "catalog:",
+ "@systemfsoftware/oxlint-ignorer-config": "workspace:^",
+ "@systemfsoftware/tsconfig": "catalog:",
+ "@systemfsoftware/vitest-config": "workspace:^",
+ "@types/node": "catalog:",
+ "oxlint": "catalog:",
+ "rimraf": "catalog:",
+ "tsdown": "catalog:",
+ "typescript": "catalog:",
+ "vitest": "catalog:"
+ },
+ "publishConfig": {
+ "access": "public",
+ "provenance": true,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ }
+ }
+}
diff --git a/packages/ignorers/in-source-vitest-block/src/InSourceTestIgnore.ts b/packages/ignorers/in-source-vitest-block/src/InSourceTestIgnore.ts
new file mode 100644
index 000000000..9f3400554
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/src/InSourceTestIgnore.ts
@@ -0,0 +1,88 @@
+import type {
+ BinaryExpression,
+ IdentifierName,
+ IfStatement,
+ MemberExpression,
+ MetaProperty,
+} from '@systemfsoftware/stryker-ignorer-interface'
+
+export const IN_SOURCE_TEST_IGNORED =
+ 'inside an `if (import.meta.vitest)` block — test code, not production behaviour' as const
+
+export const VITEST_META_PROPERTY = 'vitest' as const
+
+interface TypedNode {
+ readonly type: string
+}
+
+const isObject = (value: unknown): value is object => typeof value === 'object' && value !== null
+
+const hasStringType = (value: object): value is TypedNode => 'type' in value && typeof value.type === 'string'
+
+const isAstLike = (value: unknown): value is TypedNode => isObject(value) && hasStringType(value)
+
+const isNodeOfType = (value: unknown, type: string): value is TypedNode => isAstLike(value) && value.type === type
+
+const hasStringName = (value: TypedNode): boolean => 'name' in value && typeof value.name === 'string'
+
+export const isIdentifierName = (value: unknown): value is IdentifierName =>
+ isNodeOfType(value, 'Identifier') && hasStringName(value)
+
+const isNamed = (value: unknown, name: string): value is IdentifierName =>
+ isIdentifierName(value) && value.name === name
+
+const hasMetaIdentifier = (value: TypedNode): boolean => 'meta' in value && isIdentifierName(value.meta)
+
+const hasPropertyIdentifier = (value: TypedNode): boolean => 'property' in value && isIdentifierName(value.property)
+
+const hasIdentifierPair = (value: TypedNode): boolean => hasMetaIdentifier(value) && hasPropertyIdentifier(value)
+
+const isMetaProperty = (value: unknown): value is MetaProperty =>
+ isNodeOfType(value, 'MetaProperty') && hasIdentifierPair(value)
+
+const hasObjectMetaProperty = (value: TypedNode): boolean => 'object' in value && isMetaProperty(value.object)
+
+const hasImportMetaPair = (value: TypedNode): boolean => hasObjectMetaProperty(value) && hasPropertyIdentifier(value)
+
+export const isImportMetaMember = (value: unknown): value is MemberExpression =>
+ isNodeOfType(value, 'MemberExpression') && hasImportMetaPair(value)
+
+const hasLeftAst = (value: TypedNode): boolean => 'left' in value && isAstLike(value.left)
+
+const hasRightAst = (value: TypedNode): boolean => 'right' in value && isAstLike(value.right)
+
+const hasBinaryOperands = (value: TypedNode): boolean => hasLeftAst(value) && hasRightAst(value)
+
+export const isBinaryExpression = (value: unknown): value is BinaryExpression =>
+ isNodeOfType(value, 'BinaryExpression') && hasBinaryOperands(value)
+
+const hasTestAst = (value: TypedNode): boolean => 'test' in value && isAstLike(value.test)
+
+export const isIfStatement = (value: unknown): value is IfStatement =>
+ isNodeOfType(value, 'IfStatement') && hasTestAst(value)
+
+const isImportMetaObject = (node: MetaProperty): boolean =>
+ isNamed(node.meta, 'import') && isNamed(node.property, 'meta')
+
+const hasImportMetaObject = (node: MemberExpression): boolean =>
+ isMetaProperty(node.object) && isImportMetaObject(node.object)
+
+const hasVitestProperty = (node: MemberExpression): boolean => isNamed(node.property, VITEST_META_PROPERTY)
+
+const hasImportMetaVitest = (node: MemberExpression): boolean => hasImportMetaObject(node) && hasVitestProperty(node)
+
+const isImportMetaVitest = (node: unknown): boolean => isImportMetaMember(node) && hasImportMetaVitest(node)
+
+const hasImportMetaVitestOperand = (test: BinaryExpression): boolean =>
+ isImportMetaVitest(test.left) || isImportMetaVitest(test.right)
+
+const isBinaryImportMetaVitest = (test: unknown): boolean =>
+ isBinaryExpression(test) && hasImportMetaVitestOperand(test)
+
+const guardsOnImportMetaVitest = (test: unknown): boolean => isImportMetaVitest(test) || isBinaryImportMetaVitest(test)
+
+export const isInSourceTestGuard = (node: unknown): boolean =>
+ isIfStatement(node) && guardsOnImportMetaVitest(node.test)
+
+export const decideInSourceTestIgnore = (ancestors: Iterable): string | undefined =>
+ [...ancestors].some(isInSourceTestGuard) ? IN_SOURCE_TEST_IGNORED : undefined
diff --git a/packages/ignorers/in-source-vitest-block/src/mod.ts b/packages/ignorers/in-source-vitest-block/src/mod.ts
new file mode 100644
index 000000000..37a3a7657
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/src/mod.ts
@@ -0,0 +1,9 @@
+import type { NodePath } from '@systemfsoftware/stryker-ignorer-interface'
+
+import { decideInSourceTestIgnore, IN_SOURCE_TEST_IGNORED, isInSourceTestGuard } from './InSourceTestIgnore.js'
+
+const firstIgnoreReason = (path: NodePath): string | undefined => decideInSourceTestIgnore(path.ancestors)
+
+export const strykerIgnorers = [{ name: 'in-source-vitest-block', shouldIgnore: firstIgnoreReason }]
+
+export { decideInSourceTestIgnore, IN_SOURCE_TEST_IGNORED, isInSourceTestGuard }
diff --git a/packages/stryker-plugins/tests/__fixtures__/InSourceTestAst.fixtures.ts b/packages/ignorers/in-source-vitest-block/tests/__fixtures__/InSourceTestAst.fixtures.ts
similarity index 100%
rename from packages/stryker-plugins/tests/__fixtures__/InSourceTestAst.fixtures.ts
rename to packages/ignorers/in-source-vitest-block/tests/__fixtures__/InSourceTestAst.fixtures.ts
diff --git a/packages/ignorers/in-source-vitest-block/tests/in-source-vitest-block.test.ts b/packages/ignorers/in-source-vitest-block/tests/in-source-vitest-block.test.ts
new file mode 100644
index 000000000..6c88e555f
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/tests/in-source-vitest-block.test.ts
@@ -0,0 +1,123 @@
+import { describe, expect, it } from 'vitest'
+
+import { IN_SOURCE_TEST_IGNORED, strykerIgnorers } from '@systemfsoftware/stryker-ignorer-in-source-vitest-block'
+import { type NodePath, type PlainIgnorer } from '@systemfsoftware/stryker-ignorer-interface'
+
+import { binaryOf, guardOf, identifier, importMetaMember, metaOf } from './__fixtures__/InSourceTestAst.fixtures.js'
+
+const CASES = {
+ ignored: [
+ {
+ name: 'A bare vitest flag as the guard condition matches',
+ path: { node: identifier('x'), ancestors: [guardOf(importMetaMember('vitest'))] },
+ reason: IN_SOURCE_TEST_IGNORED,
+ },
+ {
+ name: 'A vitest flag on the left side of a comparison matches',
+ path: {
+ node: identifier('x'),
+ ancestors: [guardOf(binaryOf(importMetaMember('vitest'), identifier('undefined')))],
+ },
+ reason: IN_SOURCE_TEST_IGNORED,
+ },
+ {
+ name: 'A vitest flag on the right side of a comparison matches',
+ path: {
+ node: identifier('x'),
+ ancestors: [guardOf(binaryOf(identifier('undefined'), importMetaMember('vitest')))],
+ },
+ reason: IN_SOURCE_TEST_IGNORED,
+ },
+ {
+ name: 'A mutant guarded by an ancestor vitest check is ignored',
+ path: {
+ node: identifier('mutant'),
+ ancestors: [identifier('x'), binaryOf(identifier('a'), identifier('b')), guardOf(importMetaMember('vitest'))],
+ },
+ reason: IN_SOURCE_TEST_IGNORED,
+ },
+ {
+ name:
+ 'The exported descriptor registers under the in-source-vitest-block name and answers like the decision function',
+ path: { node: identifier('x'), ancestors: [guardOf(importMetaMember('vitest'))] },
+ reason: IN_SOURCE_TEST_IGNORED,
+ },
+ ],
+ kept: [
+ {
+ name: 'A flag for a different meta property does not match',
+ path: { node: identifier('x'), ancestors: [guardOf(importMetaMember('env'))] },
+ },
+ {
+ name: 'A vitest flag on a non-import meta does not match',
+ path: {
+ node: identifier('x'),
+ ancestors: [
+ guardOf({ type: 'MemberExpression', object: metaOf('require', 'meta'), property: identifier('vitest') }),
+ ],
+ },
+ },
+ {
+ name: 'A vitest property on a non-meta object does not match',
+ path: {
+ node: identifier('x'),
+ ancestors: [
+ guardOf({ type: 'MemberExpression', object: metaOf('import', 'cache'), property: identifier('vitest') }),
+ ],
+ },
+ },
+ {
+ name: 'A bare vitest expression without an if statement does not match',
+ path: { node: importMetaMember('vitest') },
+ },
+ {
+ name: 'A plain comparison without a vitest flag does not match',
+ path: { node: identifier('x'), ancestors: [guardOf(binaryOf(identifier('a'), identifier('b')))] },
+ },
+ {
+ name: 'A mutant with no guard in its ancestors stays live',
+ path: { node: identifier('mutant'), ancestors: [identifier('x'), guardOf(importMetaMember('env'))] },
+ },
+ {
+ name: 'A mutant with no ancestors at all stays live',
+ path: { node: identifier('mutant') },
+ },
+ {
+ name: 'A non-IfStatement parent is not a guard',
+ path: { node: identifier('x'), ancestors: [importMetaMember('vitest')] },
+ },
+ ],
+}
+
+const ignorer: PlainIgnorer | undefined = strykerIgnorers[0]
+if (ignorer === undefined) throw new Error('@systemfsoftware/stryker-ignorer-in-source-vitest-block exports no ignorer')
+
+interface CasePath {
+ readonly node: unknown
+ readonly ancestors?: readonly unknown[] | undefined
+}
+
+const pathOf = (spec: CasePath): NodePath => ({ node: spec.node, ancestors: spec.ancestors ?? [] })
+
+describe('in-source-vitest-block', () => {
+ it('Should_Register_The_Descriptor', () => {
+ expect(ignorer.name).toBe('in-source-vitest-block')
+ })
+ it.each(CASES.ignored)('ignores: $name', (testCase) => {
+ expect(ignorer.shouldIgnore(pathOf(testCase.path))).toBe(testCase.reason)
+ })
+ it.each(CASES.kept)('keeps: $name', (testCase) => {
+ expect(ignorer.shouldIgnore(pathOf(testCase.path))).toBeUndefined()
+ })
+ it('ignores a literal whose ancestors carry the guard', () => {
+ expect(
+ ignorer.shouldIgnore({
+ node: { type: 'Literal', value: 'production' },
+ ancestors: [
+ binaryOf(importMetaMember('vitest'), identifier('undefined')),
+ guardOf(importMetaMember('vitest')),
+ ],
+ }),
+ ).toBe(IN_SOURCE_TEST_IGNORED)
+ })
+})
diff --git a/packages/ignorers/in-source-vitest-block/tsconfig.build.json b/packages/ignorers/in-source-vitest-block/tsconfig.build.json
new file mode 100644
index 000000000..869b9aaa9
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/tsconfig.build.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "declarationMap": false,
+ "sourceMap": false,
+ "stripInternal": true
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.tst.ts"]
+}
diff --git a/packages/ignorers/in-source-vitest-block/tsconfig.json b/packages/ignorers/in-source-vitest-block/tsconfig.json
new file mode 100644
index 000000000..04c392e87
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": ["@systemfsoftware/tsconfig/tsc/dom/library-monorepo"],
+ "compilerOptions": {
+ "types": []
+ },
+ "include": [
+ "src",
+ "tests"
+ ],
+ "exclude": [
+ "node_modules",
+ "dist"
+ ],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
+}
diff --git a/packages/ignorers/in-source-vitest-block/tsconfig.node.json b/packages/ignorers/in-source-vitest-block/tsconfig.node.json
new file mode 100644
index 000000000..4d32e1ec2
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/tsconfig.node.json
@@ -0,0 +1,4 @@
+{
+ "extends": "@systemfsoftware/tsconfig/node",
+ "include": ["oxlint.config.ts", "tsdown.config.ts", "vitest.config.ts"]
+}
diff --git a/packages/ignorers/in-source-vitest-block/tsdown.config.ts b/packages/ignorers/in-source-vitest-block/tsdown.config.ts
new file mode 100644
index 000000000..6a4bc1e03
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/tsdown.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'tsdown'
+
+export default defineConfig({
+ entry: {
+ index: './src/mod.ts',
+ },
+ format: 'esm',
+ dts: true,
+ tsconfig: './tsconfig.build.json',
+ clean: true,
+ outExtensions: () => ({ js: '.mjs', dts: '.d.ts' }),
+})
diff --git a/packages/ignorers/in-source-vitest-block/vitest.config.ts b/packages/ignorers/in-source-vitest-block/vitest.config.ts
new file mode 100644
index 000000000..6536c4494
--- /dev/null
+++ b/packages/ignorers/in-source-vitest-block/vitest.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig, sharedConfig } from '@systemfsoftware/vitest-config'
+
+export default defineConfig({
+ ...sharedConfig,
+ test: {
+ ...sharedConfig.test,
+ include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
+ },
+})
diff --git a/packages/ignorers/interface/.attw.json b/packages/ignorers/interface/.attw.json
new file mode 100644
index 000000000..76f3418da
--- /dev/null
+++ b/packages/ignorers/interface/.attw.json
@@ -0,0 +1 @@
+{ "ignoreRules": ["no-resolution", "cjs-resolves-to-esm"] }
diff --git a/packages/ignorers/interface/AGENTS.md b/packages/ignorers/interface/AGENTS.md
new file mode 100644
index 000000000..816ade357
--- /dev/null
+++ b/packages/ignorers/interface/AGENTS.md
@@ -0,0 +1,35 @@
+# AGENTS.md — `@systemfsoftware/stryker-ignorer-interface`
+
+The ignorer interface as types only: `PlainIgnorer`, the AST vocabulary
+re-exported from `@oxc-project/types` and bundled into this package's own
+declarations, and `NodePath`. The package publishes no value, no function, and
+no schema — a guard and a reason string belong to the ignorer that needs them.
+Zero runtime dependencies and zero Effect is the package's identity — nothing
+that imports effect or a host package, at run time or in `devDependencies`, may
+land here. Root `AGENTS.md` governs.
+
+## Rules
+
+| ID | Rule | Gate |
+| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
+| **SI1** | No `effect` or `@effect/*` import in any source or built file — the preset's `no-restricted-imports` ban is the gate. | `pnpm --filter @systemfsoftware/stryker-ignorer-interface lint` |
+| **SI2** | No `effect` or `@effect/*` key in any dependency block of `package.json`. | `review` — the reviewer reads the dependency blocks |
+| **SI3** | Types only: no source or built file exports a value, a function, or a class — `dist/index.mjs` stays empty and every export is a type. | `pnpm --filter @systemfsoftware/stryker-ignorer-interface build` |
+| **SI4** | An ignorer's absence is never a reason: `shouldIgnore` returns a reason string or `undefined`, nothing else. | `review` — the reviewer reads the declared signature |
+| **SI5** | The published surface is exactly what `etc/stryker-ignorer-interface.api.md` lists; the report is regenerated with `pnpm api:update`, never hand-edited. | `pnpm --filter @systemfsoftware/stryker-ignorer-interface build` |
+| **SI6** | No test suite ships here: the package has no behaviour to pin, so the API report and the type-checker are its verification. | `pnpm --filter @systemfsoftware/stryker-ignorer-interface test` |
+
+The reviewer's decision on each `review`-gated row, shown as `wrong:`/`right:`:
+
+- **SI2** — `wrong:` `"effect": "catalog:"` in `dependencies` or anywhere under `devDependencies`; `right:` an empty `dependencies` and a `devDependencies` block holding only build tooling.
+- **SI4** — `wrong:` `shouldIgnore(path: NodePath): boolean | string`; `right:` `shouldIgnore(path: NodePath): string | undefined`.
+
+## Verification
+
+```bash
+pnpm --filter @systemfsoftware/stryker-ignorer-interface build
+pnpm --filter @systemfsoftware/stryker-ignorer-interface typecheck
+pnpm --filter @systemfsoftware/stryker-ignorer-interface test
+pnpm --filter @systemfsoftware/stryker-ignorer-interface lint
+pnpm --filter @systemfsoftware/stryker-ignorer-interface attw
+```
diff --git a/packages/ignorers/interface/LICENSE b/packages/ignorers/interface/LICENSE
new file mode 100644
index 000000000..896c3747c
--- /dev/null
+++ b/packages/ignorers/interface/LICENSE
@@ -0,0 +1,203 @@
+Copyright (c) 2026 Ryan Lee (systemfsoftware)
+
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2026 Ryan Lee (systemfsoftware)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/packages/ignorers/interface/README.md b/packages/ignorers/interface/README.md
new file mode 100644
index 000000000..af62c63ad
--- /dev/null
+++ b/packages/ignorers/interface/README.md
@@ -0,0 +1,72 @@
+# @systemfsoftware/stryker-ignorer-interface
+
+
+
+
+> The ignorer interface: the descriptor and the AST vocabulary an ignorer reasons
+> about, as types only — the package ships no runtime code.
+
+An ignorer is the smallest thing a StrykerJS-style mutation tool can load: a
+name and a synchronous decision over a path the host hands it. This package
+carries the shapes of that contract and nothing that runs. It has **no** Effect
+dependency, runtime or development: `dependencies` is empty, no value is
+exported, and the kitchen-sink family aggregate preset is replaced by
+`@systemfsoftware/oxlint-ignorer-config`, which bans Effect imports outright.
+
+| Export | What it is |
+| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `PlainIgnorer` | The descriptor: `{ name, shouldIgnore(path): string \| undefined }` |
+| vocabulary | the AST vocabulary, re-exported from [`@oxc-project/types`](https://www.npmjs.com/package/@oxc-project/types) and bundled into this package's own declarations, so a consumer installs nothing else — `Node`, `Expression`, `Statement`, `Program`, `Span`, and every concrete node interface |
+| `NodePath` | the path shape the host hands `shouldIgnore`: the node and its ancestors, nearest first |
+
+Everything the package publishes is a type. A guard and a reason string are the
+ignorer's own code: the host never interprets a schema, it calls
+`shouldIgnore(path)` and uses what comes back.
+
+## Install
+
+```bash
+pnpm add -D @systemfsoftware/stryker-ignorer-interface
+```
+
+## Write an ignorer
+
+```ts
+import type { NodePath, PlainIgnorer } from '@systemfsoftware/stryker-ignorer-interface'
+
+const isGenerated = (node: unknown): boolean =>
+ typeof node === 'object' && node !== null && 'name' in node && node.name === '__generated'
+
+const inGeneratedCode = (path: NodePath): boolean => path.ancestors.some(isGenerated)
+
+const myIgnorer: PlainIgnorer = {
+ name: 'generated-code',
+ shouldIgnore: (path) => (inGeneratedCode(path) ? 'the enclosing member is generated' : undefined),
+}
+
+export const strykerIgnorers = [myIgnorer]
+```
+
+An entry is just `{ name, shouldIgnore }`: the host registers each one as an
+`Ignore` contribution and validates at load that `name` is a string and
+`shouldIgnore` is callable — an entry that fails fails the load by name.
+
+A module exporting `strykerIgnorers` loads as `Ignore` plugin contributions in
+any engine carrying the plain-ignorer loader. Configure the pair — they take
+different strings:
+
+| Config key | Value |
+| ---------- | ----------------------------------------------- |
+| `plugins` | the module specifier (this package or your own) |
+| `ignorers` | the contribution name (`generated-code` above) |
+
+A mismatched pair fails silent-green: the run completes with nothing ignored
+and only a `Cannot find plugin` warning.
+
+## Contributing
+
+Development setup and workflow: [AGENTS.md](AGENTS.md).
+
+## License
+
+[Apache 2.0](LICENSE)
diff --git a/packages/ignorers/interface/api-extractor.json b/packages/ignorers/interface/api-extractor.json
new file mode 100644
index 000000000..3c8aaa7bf
--- /dev/null
+++ b/packages/ignorers/interface/api-extractor.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/index.d.ts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/main/",
+ "reportFileName": "stryker-ignorer-interface.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/index.d.ts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/ignorers/interface/etc/stryker-ignorer-interface.api.md b/packages/ignorers/interface/etc/stryker-ignorer-interface.api.md
new file mode 100644
index 000000000..6c165e11d
--- /dev/null
+++ b/packages/ignorers/interface/etc/stryker-ignorer-interface.api.md
@@ -0,0 +1,2678 @@
+## API Report File for "@systemfsoftware/stryker-ignorer-interface"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+// @public (undocumented)
+export interface AccessorProperty extends Span {
+ // (undocumented)
+ accessibility?: TSAccessibility | null;
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ declare?: false;
+ // (undocumented)
+ decorators: Array;
+ // (undocumented)
+ definite?: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ override?: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly?: false;
+ // (undocumented)
+ static: boolean;
+ // (undocumented)
+ type: AccessorPropertyType;
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+ // (undocumented)
+ value: Expression | null;
+}
+
+// @public (undocumented)
+export type AccessorPropertyType = "AccessorProperty" | "TSAbstractAccessorProperty";
+
+// @public (undocumented)
+export type Argument = SpreadElement | Expression;
+
+// @public (undocumented)
+export interface ArrayAssignmentTarget extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ elements: Array;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ArrayPattern";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export interface ArrayExpression extends Span {
+ // (undocumented)
+ elements: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ArrayExpression";
+}
+
+// @public (undocumented)
+export type ArrayExpressionElement = SpreadElement | null | Expression;
+
+// @public (undocumented)
+export interface ArrayPattern extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ elements: Array;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ArrayPattern";
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+}
+
+// @public (undocumented)
+export interface ArrowFunctionExpression extends Span {
+ // (undocumented)
+ async: boolean;
+ // (undocumented)
+ body: FunctionBody | Expression;
+ // (undocumented)
+ expression: boolean;
+ // (undocumented)
+ generator: false;
+ // (undocumented)
+ id: null;
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ returnType?: TSTypeAnnotation | null;
+ // (undocumented)
+ type: "ArrowFunctionExpression";
+ // (undocumented)
+ typeParameters?: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface AssignmentExpression extends Span {
+ // (undocumented)
+ left: AssignmentTarget;
+ // (undocumented)
+ operator: AssignmentOperator;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "AssignmentExpression";
+}
+
+// @public (undocumented)
+export type AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "**=" | "<<=" | ">>=" | ">>>=" | "|=" | "^=" | "&=" | "||=" | "&&=" | "??=";
+
+// @public (undocumented)
+export interface AssignmentPattern extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ left: BindingPattern;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "AssignmentPattern";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export type AssignmentTarget = SimpleAssignmentTarget | AssignmentTargetPattern;
+
+// @public (undocumented)
+export type AssignmentTargetMaybeDefault = AssignmentTargetWithDefault | AssignmentTarget;
+
+// @public (undocumented)
+export type AssignmentTargetPattern = ArrayAssignmentTarget | ObjectAssignmentTarget;
+
+// @public (undocumented)
+export type AssignmentTargetProperty = AssignmentTargetPropertyIdentifier | AssignmentTargetPropertyProperty;
+
+// @public (undocumented)
+export interface AssignmentTargetPropertyIdentifier extends Span {
+ // (undocumented)
+ computed: false;
+ // (undocumented)
+ key: IdentifierReference;
+ // (undocumented)
+ kind: "init";
+ // (undocumented)
+ method: false;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ shorthand: true;
+ // (undocumented)
+ type: "Property";
+ // (undocumented)
+ value: IdentifierReference | AssignmentTargetWithDefault;
+}
+
+// @public (undocumented)
+export interface AssignmentTargetPropertyProperty extends Span {
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ kind: "init";
+ // (undocumented)
+ method: false;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ shorthand: false;
+ // (undocumented)
+ type: "Property";
+ // (undocumented)
+ value: AssignmentTargetMaybeDefault;
+}
+
+// @public (undocumented)
+export interface AssignmentTargetRest extends Span {
+ // (undocumented)
+ argument: AssignmentTarget;
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "RestElement";
+ // (undocumented)
+ typeAnnotation?: null;
+ // (undocumented)
+ value?: null;
+}
+
+// @public (undocumented)
+export interface AssignmentTargetWithDefault extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ left: AssignmentTarget;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "AssignmentPattern";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export interface AwaitExpression extends Span {
+ // (undocumented)
+ argument: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "AwaitExpression";
+}
+
+// @public (undocumented)
+export interface BigIntLiteral extends Span {
+ // (undocumented)
+ bigint: string;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: string | null;
+ // (undocumented)
+ type: "Literal";
+ // (undocumented)
+ value: bigint;
+}
+
+// @public (undocumented)
+export interface BinaryExpression extends Span {
+ // (undocumented)
+ left: Expression;
+ // (undocumented)
+ operator: BinaryOperator;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "BinaryExpression";
+}
+
+// @public (undocumented)
+export type BinaryOperator = "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" | "%" | "**" | "<<" | ">>" | ">>>" | "|" | "^" | "&" | "in" | "instanceof";
+
+// @public (undocumented)
+export interface BindingIdentifier extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Identifier";
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+}
+
+// @public (undocumented)
+export type BindingPattern = BindingIdentifier | ObjectPattern | ArrayPattern | AssignmentPattern;
+
+// @public (undocumented)
+export interface BindingProperty extends Span {
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ kind: "init";
+ // (undocumented)
+ method: false;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ shorthand: boolean;
+ // (undocumented)
+ type: "Property";
+ // (undocumented)
+ value: BindingPattern;
+}
+
+// @public (undocumented)
+export interface BindingRestElement extends Span {
+ // (undocumented)
+ argument: BindingPattern;
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "RestElement";
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+ // (undocumented)
+ value?: null;
+}
+
+// @public (undocumented)
+export interface BlockStatement extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "BlockStatement";
+}
+
+// @public (undocumented)
+export interface BooleanLiteral extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: string | null;
+ // (undocumented)
+ type: "Literal";
+ // (undocumented)
+ value: boolean;
+}
+
+// @public (undocumented)
+export interface BreakStatement extends Span {
+ // (undocumented)
+ label: LabelIdentifier | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "BreakStatement";
+}
+
+// @public (undocumented)
+export interface CallExpression extends Span {
+ // (undocumented)
+ arguments: Array;
+ // (undocumented)
+ callee: Expression;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "CallExpression";
+ // (undocumented)
+ typeArguments?: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export interface CatchClause extends Span {
+ // (undocumented)
+ body: BlockStatement;
+ // (undocumented)
+ param: BindingPattern | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "CatchClause";
+}
+
+// @public (undocumented)
+export type ChainElement = CallExpression | TSNonNullExpression | MemberExpression;
+
+// @public (undocumented)
+export interface ChainExpression extends Span {
+ // (undocumented)
+ expression: ChainElement;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ChainExpression";
+}
+
+// @public (undocumented)
+export interface Class extends Span {
+ // (undocumented)
+ abstract?: boolean;
+ // (undocumented)
+ body: ClassBody;
+ // (undocumented)
+ declare?: boolean;
+ // (undocumented)
+ decorators: Array;
+ // (undocumented)
+ id: BindingIdentifier | null;
+ // (undocumented)
+ implements?: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ superClass: Expression | null;
+ // (undocumented)
+ superTypeArguments?: TSTypeParameterInstantiation | null;
+ // (undocumented)
+ type: ClassType;
+ // (undocumented)
+ typeParameters?: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface ClassBody extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ClassBody";
+}
+
+// @public (undocumented)
+export type ClassElement = StaticBlock | MethodDefinition | PropertyDefinition_2 | AccessorProperty | TSIndexSignature;
+
+// @public (undocumented)
+export type ClassType = "ClassDeclaration" | "ClassExpression";
+
+// @public (undocumented)
+export interface ComputedMemberExpression extends Span {
+ // (undocumented)
+ computed: true;
+ // (undocumented)
+ object: Expression;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ property: Expression;
+ // (undocumented)
+ type: "MemberExpression";
+}
+
+// @public (undocumented)
+export interface ConditionalExpression extends Span {
+ // (undocumented)
+ alternate: Expression;
+ // (undocumented)
+ consequent: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ test: Expression;
+ // (undocumented)
+ type: "ConditionalExpression";
+}
+
+// @public (undocumented)
+export interface ContinueStatement extends Span {
+ // (undocumented)
+ label: LabelIdentifier | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ContinueStatement";
+}
+
+// @public (undocumented)
+export interface DebuggerStatement extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "DebuggerStatement";
+}
+
+// @public (undocumented)
+export type Declaration = VariableDeclaration | Function_2 | Class | TSTypeAliasDeclaration | TSInterfaceDeclaration | TSEnumDeclaration | TSModuleDeclaration | TSGlobalDeclaration | TSImportEqualsDeclaration;
+
+// @public (undocumented)
+export interface Decorator extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Decorator";
+}
+
+// @public (undocumented)
+export interface Directive extends Span {
+ // (undocumented)
+ directive: string;
+ // (undocumented)
+ expression: StringLiteral;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ExpressionStatement";
+}
+
+// @public (undocumented)
+export interface DoWhileStatement extends Span {
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ test: Expression;
+ // (undocumented)
+ type: "DoWhileStatement";
+}
+
+// @public (undocumented)
+export interface EmptyStatement extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "EmptyStatement";
+}
+
+// @public (undocumented)
+export interface ExportAllDeclaration extends Span {
+ // (undocumented)
+ attributes: Array;
+ // (undocumented)
+ exported: ModuleExportName | null;
+ // (undocumented)
+ exportKind?: ImportOrExportKind;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ source: StringLiteral;
+ // (undocumented)
+ type: "ExportAllDeclaration";
+}
+
+// @public (undocumented)
+export interface ExportDefaultDeclaration extends Span {
+ // (undocumented)
+ declaration: ExportDefaultDeclarationKind;
+ // (undocumented)
+ exportKind?: "value";
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ExportDefaultDeclaration";
+}
+
+// @public (undocumented)
+export type ExportDefaultDeclarationKind = Function_2 | Class | TSInterfaceDeclaration | Expression;
+
+// @public (undocumented)
+export interface ExportNamedDeclaration extends Span {
+ // (undocumented)
+ attributes: Array;
+ // (undocumented)
+ declaration: Declaration | null;
+ // (undocumented)
+ exportKind?: ImportOrExportKind;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ source: StringLiteral | null;
+ // (undocumented)
+ specifiers: Array;
+ // (undocumented)
+ type: "ExportNamedDeclaration";
+}
+
+// @public (undocumented)
+export interface ExportSpecifier extends Span {
+ // (undocumented)
+ exported: ModuleExportName;
+ // (undocumented)
+ exportKind?: ImportOrExportKind;
+ // (undocumented)
+ local: ModuleExportName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ExportSpecifier";
+}
+
+// @public (undocumented)
+export type Expression = BooleanLiteral | NullLiteral | NumericLiteral | BigIntLiteral | RegExpLiteral | StringLiteral | TemplateLiteral | IdentifierReference | MetaProperty | Super | ArrayExpression | ArrowFunctionExpression | AssignmentExpression | AwaitExpression | BinaryExpression | CallExpression | ChainExpression | Class | ConditionalExpression | Function_2 | ImportExpression | LogicalExpression | NewExpression | ObjectExpression | ParenthesizedExpression | SequenceExpression | TaggedTemplateExpression | ThisExpression | UnaryExpression | UpdateExpression | YieldExpression | PrivateInExpression | JSXElement | JSXFragment | TSAsExpression | TSSatisfiesExpression | TSTypeAssertion | TSNonNullExpression | TSInstantiationExpression | V8IntrinsicExpression | MemberExpression;
+
+// @public (undocumented)
+export interface ExpressionStatement extends Span {
+ // (undocumented)
+ directive?: string | null;
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ExpressionStatement";
+}
+
+// @public (undocumented)
+export interface ForInStatement extends Span {
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ left: ForStatementLeft;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "ForInStatement";
+}
+
+// @public (undocumented)
+export type FormalParameter = {
+ decorators?: Array;
+} & BindingPattern;
+
+// @public (undocumented)
+export interface FormalParameterRest extends Span {
+ // (undocumented)
+ argument: BindingPattern;
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ optional?: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "RestElement";
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+ // (undocumented)
+ value?: null;
+}
+
+// @public (undocumented)
+export interface ForOfStatement extends Span {
+ // (undocumented)
+ await: boolean;
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ left: ForStatementLeft;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "ForOfStatement";
+}
+
+// @public (undocumented)
+export interface ForStatement extends Span {
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ init: ForStatementInit | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ test: Expression | null;
+ // (undocumented)
+ type: "ForStatement";
+ // (undocumented)
+ update: Expression | null;
+}
+
+// @public (undocumented)
+export type ForStatementInit = VariableDeclaration | Expression;
+
+// @public (undocumented)
+export type ForStatementLeft = VariableDeclaration | AssignmentTarget;
+
+// @public (undocumented)
+interface Function_2 extends Span {
+ // (undocumented)
+ async: boolean;
+ // (undocumented)
+ body: FunctionBody | null;
+ // (undocumented)
+ declare?: boolean;
+ // (undocumented)
+ expression: false;
+ // (undocumented)
+ generator: boolean;
+ // (undocumented)
+ id: BindingIdentifier | null;
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ returnType?: TSTypeAnnotation | null;
+ // (undocumented)
+ type: FunctionType;
+ // (undocumented)
+ typeParameters?: TSTypeParameterDeclaration | null;
+}
+export { Function_2 as Function }
+
+// @public (undocumented)
+export interface FunctionBody extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "BlockStatement";
+}
+
+// @public (undocumented)
+export type FunctionType = "FunctionDeclaration" | "FunctionExpression" | "TSDeclareFunction" | "TSEmptyBodyFunctionExpression";
+
+// @public (undocumented)
+export interface Hashbang extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Hashbang";
+ // (undocumented)
+ value: string;
+}
+
+// @public (undocumented)
+export interface IdentifierName extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Identifier";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export interface IdentifierReference extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Identifier";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export interface IfStatement extends Span {
+ // (undocumented)
+ alternate: Statement | null;
+ // (undocumented)
+ consequent: Statement;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ test: Expression;
+ // (undocumented)
+ type: "IfStatement";
+}
+
+// @public (undocumented)
+export interface ImportAttribute extends Span {
+ // (undocumented)
+ key: ImportAttributeKey;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ImportAttribute";
+ // (undocumented)
+ value: StringLiteral;
+}
+
+// @public (undocumented)
+export type ImportAttributeKey = IdentifierName | StringLiteral;
+
+// @public (undocumented)
+export interface ImportDeclaration extends Span {
+ // (undocumented)
+ attributes: Array;
+ // (undocumented)
+ importKind?: ImportOrExportKind;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ phase: ImportPhase | null;
+ // (undocumented)
+ source: StringLiteral;
+ // (undocumented)
+ specifiers: Array;
+ // (undocumented)
+ type: "ImportDeclaration";
+}
+
+// @public (undocumented)
+export type ImportDeclarationSpecifier = ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier;
+
+// @public (undocumented)
+export interface ImportDefaultSpecifier extends Span {
+ // (undocumented)
+ local: BindingIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ImportDefaultSpecifier";
+}
+
+// @public (undocumented)
+export interface ImportExpression extends Span {
+ // (undocumented)
+ options: Expression | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ phase: ImportPhase | null;
+ // (undocumented)
+ source: Expression;
+ // (undocumented)
+ type: "ImportExpression";
+}
+
+// @public (undocumented)
+export interface ImportNamespaceSpecifier extends Span {
+ // (undocumented)
+ local: BindingIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ImportNamespaceSpecifier";
+}
+
+// @public (undocumented)
+export type ImportOrExportKind = "value" | "type";
+
+// @public (undocumented)
+export type ImportPhase = "source" | "defer";
+
+// @public (undocumented)
+export interface ImportSpecifier extends Span {
+ // (undocumented)
+ imported: ModuleExportName;
+ // (undocumented)
+ importKind?: ImportOrExportKind;
+ // (undocumented)
+ local: BindingIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ImportSpecifier";
+}
+
+// @public (undocumented)
+export interface JSDocNonNullableType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ postfix: boolean;
+ // (undocumented)
+ type: "TSJSDocNonNullableType";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface JSDocNullableType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ postfix: boolean;
+ // (undocumented)
+ type: "TSJSDocNullableType";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface JSDocUnknownType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSJSDocUnknownType";
+}
+
+// @public (undocumented)
+export interface JSXAttribute extends Span {
+ // (undocumented)
+ name: JSXAttributeName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXAttribute";
+ // (undocumented)
+ value: JSXAttributeValue | null;
+}
+
+// @public (undocumented)
+export type JSXAttributeItem = JSXAttribute | JSXSpreadAttribute;
+
+// @public (undocumented)
+export type JSXAttributeName = JSXIdentifier | JSXNamespacedName;
+
+// @public (undocumented)
+export type JSXAttributeValue = StringLiteral | JSXExpressionContainer | JSXElement | JSXFragment;
+
+// @public (undocumented)
+export type JSXChild = JSXText | JSXElement | JSXFragment | JSXExpressionContainer | JSXSpreadChild;
+
+// @public (undocumented)
+export interface JSXClosingElement extends Span {
+ // (undocumented)
+ name: JSXElementName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXClosingElement";
+}
+
+// @public (undocumented)
+export interface JSXClosingFragment extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXClosingFragment";
+}
+
+// @public (undocumented)
+export interface JSXElement extends Span {
+ // (undocumented)
+ children: Array;
+ // (undocumented)
+ closingElement: JSXClosingElement | null;
+ // (undocumented)
+ openingElement: JSXOpeningElement;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXElement";
+}
+
+// @public (undocumented)
+export type JSXElementName = JSXIdentifier | JSXNamespacedName | JSXMemberExpression;
+
+// @public (undocumented)
+export interface JSXEmptyExpression extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXEmptyExpression";
+}
+
+// @public (undocumented)
+export type JSXExpression = JSXEmptyExpression | Expression;
+
+// @public (undocumented)
+export interface JSXExpressionContainer extends Span {
+ // (undocumented)
+ expression: JSXExpression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXExpressionContainer";
+}
+
+// @public (undocumented)
+export interface JSXFragment extends Span {
+ // (undocumented)
+ children: Array;
+ // (undocumented)
+ closingFragment: JSXClosingFragment;
+ // (undocumented)
+ openingFragment: JSXOpeningFragment;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXFragment";
+}
+
+// @public (undocumented)
+export interface JSXIdentifier extends Span {
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXIdentifier";
+}
+
+// @public (undocumented)
+export interface JSXMemberExpression extends Span {
+ // (undocumented)
+ object: JSXMemberExpressionObject;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ property: JSXIdentifier;
+ // (undocumented)
+ type: "JSXMemberExpression";
+}
+
+// @public (undocumented)
+export type JSXMemberExpressionObject = JSXIdentifier | JSXMemberExpression;
+
+// @public (undocumented)
+export interface JSXNamespacedName extends Span {
+ // (undocumented)
+ name: JSXIdentifier;
+ // (undocumented)
+ namespace: JSXIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXNamespacedName";
+}
+
+// @public (undocumented)
+export interface JSXOpeningElement extends Span {
+ // (undocumented)
+ attributes: Array;
+ // (undocumented)
+ name: JSXElementName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ selfClosing: boolean;
+ // (undocumented)
+ type: "JSXOpeningElement";
+ // (undocumented)
+ typeArguments?: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export interface JSXOpeningFragment extends Span {
+ // (undocumented)
+ attributes?: [];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ selfClosing?: false;
+ // (undocumented)
+ type: "JSXOpeningFragment";
+}
+
+// @public (undocumented)
+export interface JSXSpreadAttribute extends Span {
+ // (undocumented)
+ argument: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXSpreadAttribute";
+}
+
+// @public (undocumented)
+export interface JSXSpreadChild extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "JSXSpreadChild";
+}
+
+// @public (undocumented)
+export interface JSXText extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: string | null;
+ // (undocumented)
+ type: "JSXText";
+ // (undocumented)
+ value: string;
+}
+
+// @public (undocumented)
+export interface LabeledStatement extends Span {
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ label: LabelIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "LabeledStatement";
+}
+
+// @public (undocumented)
+export interface LabelIdentifier extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Identifier";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export interface LogicalExpression extends Span {
+ // (undocumented)
+ left: Expression;
+ // (undocumented)
+ operator: LogicalOperator;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "LogicalExpression";
+}
+
+// @public (undocumented)
+export type LogicalOperator = "||" | "&&" | "??";
+
+// @public (undocumented)
+export type MemberExpression = ComputedMemberExpression | StaticMemberExpression | PrivateFieldExpression;
+
+// @public (undocumented)
+export interface MetaProperty extends Span {
+ // (undocumented)
+ meta: IdentifierName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ property: IdentifierName;
+ // (undocumented)
+ type: "MetaProperty";
+}
+
+// @public (undocumented)
+export interface MethodDefinition extends Span {
+ // (undocumented)
+ accessibility?: TSAccessibility | null;
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ decorators: Array;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ kind: MethodDefinitionKind;
+ // (undocumented)
+ optional?: boolean;
+ // (undocumented)
+ override?: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ static: boolean;
+ // (undocumented)
+ type: MethodDefinitionType;
+ // (undocumented)
+ value: Function_2;
+}
+
+// @public (undocumented)
+export type MethodDefinitionKind = "constructor" | "method" | "get" | "set";
+
+// @public (undocumented)
+export type MethodDefinitionType = "MethodDefinition" | "TSAbstractMethodDefinition";
+
+// @public (undocumented)
+export type ModuleDeclaration = ImportDeclaration | ExportAllDeclaration | ExportDefaultDeclaration | ExportNamedDeclaration | TSExportAssignment | TSNamespaceExportDeclaration;
+
+// @public (undocumented)
+export type ModuleExportName = IdentifierName | IdentifierReference | StringLiteral;
+
+// @public (undocumented)
+export type ModuleKind = "script" | "module" | "commonjs";
+
+// @public (undocumented)
+export interface NewExpression extends Span {
+ // (undocumented)
+ arguments: Array;
+ // (undocumented)
+ callee: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "NewExpression";
+ // (undocumented)
+ typeArguments?: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+type Node_2 = Program | IdentifierName | IdentifierReference | BindingIdentifier | LabelIdentifier | ThisExpression | ArrayExpression | ObjectExpression | ObjectProperty | TemplateLiteral | TaggedTemplateExpression | TemplateElement | ComputedMemberExpression | StaticMemberExpression | PrivateFieldExpression | CallExpression | NewExpression | MetaProperty | SpreadElement | UpdateExpression | UnaryExpression | BinaryExpression | PrivateInExpression | LogicalExpression | ConditionalExpression | AssignmentExpression | ArrayAssignmentTarget | ObjectAssignmentTarget | AssignmentTargetRest | AssignmentTargetWithDefault | AssignmentTargetPropertyIdentifier | AssignmentTargetPropertyProperty | SequenceExpression | Super | AwaitExpression | ChainExpression | ParenthesizedExpression | Directive | Hashbang | BlockStatement | VariableDeclaration | VariableDeclarator | EmptyStatement | ExpressionStatement | IfStatement | DoWhileStatement | WhileStatement | ForStatement | ForInStatement | ForOfStatement | ContinueStatement | BreakStatement | ReturnStatement | WithStatement | SwitchStatement | SwitchCase | LabeledStatement | ThrowStatement | TryStatement | CatchClause | DebuggerStatement | AssignmentPattern | ObjectPattern | BindingProperty | ArrayPattern | BindingRestElement | Function_2 | FunctionBody | ArrowFunctionExpression | YieldExpression | Class | ClassBody | MethodDefinition | PropertyDefinition_2 | PrivateIdentifier | StaticBlock | AccessorProperty | ImportExpression | ImportDeclaration | ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ImportAttribute | ExportNamedDeclaration | ExportDefaultDeclaration | ExportAllDeclaration | ExportSpecifier | V8IntrinsicExpression | BooleanLiteral | NullLiteral | NumericLiteral | StringLiteral | BigIntLiteral | RegExpLiteral | JSXElement | JSXOpeningElement | JSXClosingElement | JSXFragment | JSXOpeningFragment | JSXClosingFragment | JSXNamespacedName | JSXMemberExpression | JSXExpressionContainer | JSXEmptyExpression | JSXAttribute | JSXSpreadAttribute | JSXIdentifier | JSXSpreadChild | JSXText | TSThisParameter | TSEnumDeclaration | TSEnumBody | TSEnumMember | TSTypeAnnotation | TSLiteralType | TSConditionalType | TSUnionType | TSIntersectionType | TSParenthesizedType | TSTypeOperator | TSArrayType | TSIndexedAccessType | TSTupleType | TSNamedTupleMember | TSOptionalType | TSRestType | TSAnyKeyword | TSStringKeyword | TSBooleanKeyword | TSNumberKeyword | TSNeverKeyword | TSIntrinsicKeyword | TSUnknownKeyword | TSNullKeyword | TSUndefinedKeyword | TSVoidKeyword | TSSymbolKeyword | TSThisType | TSObjectKeyword | TSBigIntKeyword | TSTypeReference | TSQualifiedName | TSTypeParameterInstantiation | TSTypeParameter | TSTypeParameterDeclaration | TSTypeAliasDeclaration | TSClassImplements | TSInterfaceDeclaration | TSInterfaceBody | TSPropertySignature | TSIndexSignature | TSCallSignatureDeclaration | TSMethodSignature | TSConstructSignatureDeclaration | TSIndexSignatureName | TSInterfaceHeritage | TSTypePredicate | TSModuleDeclaration | TSGlobalDeclaration | TSModuleBlock | TSTypeLiteral | TSInferType | TSTypeQuery | TSImportType | TSImportTypeQualifiedName | TSFunctionType | TSConstructorType | TSMappedType | TSTemplateLiteralType | TSAsExpression | TSSatisfiesExpression | TSTypeAssertion | TSImportEqualsDeclaration | TSExternalModuleReference | TSNonNullExpression | Decorator | TSExportAssignment | TSNamespaceExportDeclaration | TSInstantiationExpression | JSDocNullableType | JSDocNonNullableType | JSDocUnknownType | ParamPattern;
+export { Node_2 as Node }
+
+// @public (undocumented)
+export interface NodePath {
+ // (undocumented)
+ readonly ancestors: readonly unknown[];
+ // (undocumented)
+ readonly node: unknown;
+}
+
+// @public (undocumented)
+export interface NullLiteral extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: "null" | null;
+ // (undocumented)
+ type: "Literal";
+ // (undocumented)
+ value: null;
+}
+
+// @public (undocumented)
+export interface NumericLiteral extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: string | null;
+ // (undocumented)
+ type: "Literal";
+ // (undocumented)
+ value: number;
+}
+
+// @public (undocumented)
+export interface ObjectAssignmentTarget extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ properties: Array;
+ // (undocumented)
+ type: "ObjectPattern";
+ // (undocumented)
+ typeAnnotation?: null;
+}
+
+// @public (undocumented)
+export interface ObjectExpression extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ properties: Array;
+ // (undocumented)
+ type: "ObjectExpression";
+}
+
+// @public (undocumented)
+export interface ObjectPattern extends Span {
+ // (undocumented)
+ decorators?: [];
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ properties: Array;
+ // (undocumented)
+ type: "ObjectPattern";
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+}
+
+// @public (undocumented)
+export interface ObjectProperty extends Span {
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ kind: PropertyKind;
+ // (undocumented)
+ method: boolean;
+ // (undocumented)
+ optional?: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ shorthand: boolean;
+ // (undocumented)
+ type: "Property";
+ // (undocumented)
+ value: Expression;
+}
+
+// @public (undocumented)
+export type ObjectPropertyKind = ObjectProperty | SpreadElement;
+
+// @public (undocumented)
+export type ParamPattern = FormalParameter | TSParameterProperty | FormalParameterRest;
+
+// @public (undocumented)
+export interface ParenthesizedExpression extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ParenthesizedExpression";
+}
+
+// @public (undocumented)
+export interface PlainIgnorer {
+ // (undocumented)
+ readonly name: string;
+ // (undocumented)
+ shouldIgnore(path: NodePath): string | undefined;
+}
+
+// @public (undocumented)
+export interface PrivateFieldExpression extends Span {
+ // (undocumented)
+ computed: false;
+ // (undocumented)
+ object: Expression;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ property: PrivateIdentifier;
+ // (undocumented)
+ type: "MemberExpression";
+}
+
+// @public (undocumented)
+export interface PrivateIdentifier extends Span {
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "PrivateIdentifier";
+}
+
+// @public (undocumented)
+export interface PrivateInExpression extends Span {
+ // (undocumented)
+ left: PrivateIdentifier;
+ // (undocumented)
+ operator: "in";
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: Expression;
+ // (undocumented)
+ type: "BinaryExpression";
+}
+
+// @public (undocumented)
+export interface Program extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ hashbang: Hashbang | null;
+ // (undocumented)
+ parent?: null;
+ // (undocumented)
+ sourceType: ModuleKind;
+ // (undocumented)
+ type: "Program";
+}
+
+// @public (undocumented)
+interface PropertyDefinition_2 extends Span {
+ // (undocumented)
+ accessibility?: TSAccessibility | null;
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ declare?: boolean;
+ // (undocumented)
+ decorators: Array;
+ // (undocumented)
+ definite?: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ optional?: boolean;
+ // (undocumented)
+ override?: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly?: boolean;
+ // (undocumented)
+ static: boolean;
+ // (undocumented)
+ type: PropertyDefinitionType;
+ // (undocumented)
+ typeAnnotation?: TSTypeAnnotation | null;
+ // (undocumented)
+ value: Expression | null;
+}
+export { PropertyDefinition_2 as PropertyDefinition }
+
+// @public (undocumented)
+export type PropertyDefinitionType = "PropertyDefinition" | "TSAbstractPropertyDefinition";
+
+// @public (undocumented)
+type PropertyKey_2 = IdentifierName | PrivateIdentifier | Expression;
+export { PropertyKey_2 as PropertyKey }
+
+// @public (undocumented)
+export type PropertyKind = "init" | "get" | "set";
+
+// @public (undocumented)
+export interface RegExpLiteral extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: string | null;
+ // (undocumented)
+ regex: {
+ pattern: string;
+ flags: string;
+ };
+ // (undocumented)
+ type: "Literal";
+ // (undocumented)
+ value: RegExp | null;
+}
+
+// @public (undocumented)
+export interface ReturnStatement extends Span {
+ // (undocumented)
+ argument: Expression | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ReturnStatement";
+}
+
+// @public (undocumented)
+export interface SequenceExpression extends Span {
+ // (undocumented)
+ expressions: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "SequenceExpression";
+}
+
+// @public (undocumented)
+export type SimpleAssignmentTarget = IdentifierReference | TSAsExpression | TSSatisfiesExpression | TSNonNullExpression | TSTypeAssertion | MemberExpression;
+
+// @public (undocumented)
+export interface Span {
+ // (undocumented)
+ end: number;
+ // (undocumented)
+ range?: [number, number];
+ // (undocumented)
+ start: number;
+}
+
+// @public (undocumented)
+export interface SpreadElement extends Span {
+ // (undocumented)
+ argument: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "SpreadElement";
+}
+
+// @public (undocumented)
+export type Statement = BlockStatement | BreakStatement | ContinueStatement | DebuggerStatement | DoWhileStatement | EmptyStatement | ExpressionStatement | ForInStatement | ForOfStatement | ForStatement | IfStatement | LabeledStatement | ReturnStatement | SwitchStatement | ThrowStatement | TryStatement | WhileStatement | WithStatement | Declaration | ModuleDeclaration;
+
+// @public (undocumented)
+export interface StaticBlock extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "StaticBlock";
+}
+
+// @public (undocumented)
+export interface StaticMemberExpression extends Span {
+ // (undocumented)
+ computed: false;
+ // (undocumented)
+ object: Expression;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ property: IdentifierName;
+ // (undocumented)
+ type: "MemberExpression";
+}
+
+// @public (undocumented)
+export interface StringLiteral extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ raw: string | null;
+ // (undocumented)
+ type: "Literal";
+ // (undocumented)
+ value: string;
+}
+
+// @public (undocumented)
+export interface Super extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Super";
+}
+
+// @public (undocumented)
+export interface SwitchCase extends Span {
+ // (undocumented)
+ consequent: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ test: Expression | null;
+ // (undocumented)
+ type: "SwitchCase";
+}
+
+// @public (undocumented)
+export interface SwitchStatement extends Span {
+ // (undocumented)
+ cases: Array;
+ // (undocumented)
+ discriminant: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "SwitchStatement";
+}
+
+// @public (undocumented)
+export interface TaggedTemplateExpression extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ quasi: TemplateLiteral;
+ // (undocumented)
+ tag: Expression;
+ // (undocumented)
+ type: "TaggedTemplateExpression";
+ // (undocumented)
+ typeArguments?: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export interface TemplateElement extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ tail: boolean;
+ // (undocumented)
+ type: "TemplateElement";
+ // (undocumented)
+ value: TemplateElementValue;
+}
+
+// @public (undocumented)
+export interface TemplateElementValue {
+ // (undocumented)
+ cooked: string | null;
+ // (undocumented)
+ raw: string;
+}
+
+// @public (undocumented)
+export interface TemplateLiteral extends Span {
+ // (undocumented)
+ expressions: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ quasis: Array;
+ // (undocumented)
+ type: "TemplateLiteral";
+}
+
+// @public (undocumented)
+export interface ThisExpression extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ThisExpression";
+}
+
+// @public (undocumented)
+export interface ThrowStatement extends Span {
+ // (undocumented)
+ argument: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "ThrowStatement";
+}
+
+// @public (undocumented)
+export interface TryStatement extends Span {
+ // (undocumented)
+ block: BlockStatement;
+ // (undocumented)
+ finalizer: BlockStatement | null;
+ // (undocumented)
+ handler: CatchClause | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TryStatement";
+}
+
+// @public (undocumented)
+export type TSAccessibility = "private" | "protected" | "public";
+
+// @public (undocumented)
+export interface TSAnyKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSAnyKeyword";
+}
+
+// @public (undocumented)
+export interface TSArrayType extends Span {
+ // (undocumented)
+ elementType: TSType;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSArrayType";
+}
+
+// @public (undocumented)
+export interface TSAsExpression extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSAsExpression";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface TSBigIntKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSBigIntKeyword";
+}
+
+// @public (undocumented)
+export interface TSBooleanKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSBooleanKeyword";
+}
+
+// @public (undocumented)
+export interface TSCallSignatureDeclaration extends Span {
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ returnType: TSTypeAnnotation | null;
+ // (undocumented)
+ type: "TSCallSignatureDeclaration";
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface TSClassImplements extends Span {
+ // (undocumented)
+ expression: IdentifierReference | ThisExpression | MemberExpression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSClassImplements";
+ // (undocumented)
+ typeArguments: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export interface TSConditionalType extends Span {
+ // (undocumented)
+ checkType: TSType;
+ // (undocumented)
+ extendsType: TSType;
+ // (undocumented)
+ falseType: TSType;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ trueType: TSType;
+ // (undocumented)
+ type: "TSConditionalType";
+}
+
+// @public (undocumented)
+export interface TSConstructorType extends Span {
+ // (undocumented)
+ abstract: boolean;
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ returnType: TSTypeAnnotation;
+ // (undocumented)
+ type: "TSConstructorType";
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface TSConstructSignatureDeclaration extends Span {
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ returnType: TSTypeAnnotation | null;
+ // (undocumented)
+ type: "TSConstructSignatureDeclaration";
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface TSEnumBody extends Span {
+ // (undocumented)
+ members: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSEnumBody";
+}
+
+// @public (undocumented)
+export interface TSEnumDeclaration extends Span {
+ // (undocumented)
+ body: TSEnumBody;
+ // (undocumented)
+ const: boolean;
+ // (undocumented)
+ declare: boolean;
+ // (undocumented)
+ id: BindingIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSEnumDeclaration";
+}
+
+// @public (undocumented)
+export interface TSEnumMember extends Span {
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ id: TSEnumMemberName;
+ // (undocumented)
+ initializer: Expression | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSEnumMember";
+}
+
+// @public (undocumented)
+export type TSEnumMemberName = IdentifierName | StringLiteral | TemplateLiteral;
+
+// @public (undocumented)
+export interface TSExportAssignment extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSExportAssignment";
+}
+
+// @public (undocumented)
+export interface TSExternalModuleReference extends Span {
+ // (undocumented)
+ expression: StringLiteral;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSExternalModuleReference";
+}
+
+// @public (undocumented)
+export interface TSFunctionType extends Span {
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ returnType: TSTypeAnnotation;
+ // (undocumented)
+ type: "TSFunctionType";
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface TSGlobalDeclaration extends Span {
+ // (undocumented)
+ body: TSModuleBlock;
+ // (undocumented)
+ declare: boolean;
+ // (undocumented)
+ global: true;
+ // (undocumented)
+ id: IdentifierName;
+ // (undocumented)
+ kind: "global";
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSModuleDeclaration";
+}
+
+// @public (undocumented)
+export interface TSImportEqualsDeclaration extends Span {
+ // (undocumented)
+ id: BindingIdentifier;
+ // (undocumented)
+ importKind: ImportOrExportKind;
+ // (undocumented)
+ moduleReference: TSModuleReference;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSImportEqualsDeclaration";
+}
+
+// @public (undocumented)
+export interface TSImportType extends Span {
+ // (undocumented)
+ options: ObjectExpression | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ qualifier: TSImportTypeQualifier | null;
+ // (undocumented)
+ source: StringLiteral;
+ // (undocumented)
+ type: "TSImportType";
+ // (undocumented)
+ typeArguments: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export interface TSImportTypeQualifiedName extends Span {
+ // (undocumented)
+ left: TSImportTypeQualifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: IdentifierName;
+ // (undocumented)
+ type: "TSQualifiedName";
+}
+
+// @public (undocumented)
+export type TSImportTypeQualifier = IdentifierName | TSImportTypeQualifiedName;
+
+// @public (undocumented)
+export interface TSIndexedAccessType extends Span {
+ // (undocumented)
+ indexType: TSType;
+ // (undocumented)
+ objectType: TSType;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSIndexedAccessType";
+}
+
+// @public (undocumented)
+export interface TSIndexSignature extends Span {
+ // (undocumented)
+ accessibility: null;
+ // (undocumented)
+ parameters: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly: boolean;
+ // (undocumented)
+ static: boolean;
+ // (undocumented)
+ type: "TSIndexSignature";
+ // (undocumented)
+ typeAnnotation: TSTypeAnnotation;
+}
+
+// @public (undocumented)
+export interface TSIndexSignatureName extends Span {
+ // (undocumented)
+ decorators: [];
+ // (undocumented)
+ name: string;
+ // (undocumented)
+ optional: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Identifier";
+ // (undocumented)
+ typeAnnotation: TSTypeAnnotation;
+}
+
+// @public (undocumented)
+export interface TSInferType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSInferType";
+ // (undocumented)
+ typeParameter: TSTypeParameter;
+}
+
+// @public (undocumented)
+export interface TSInstantiationExpression extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSInstantiationExpression";
+ // (undocumented)
+ typeArguments: TSTypeParameterInstantiation;
+}
+
+// @public (undocumented)
+export interface TSInterfaceBody extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSInterfaceBody";
+}
+
+// @public (undocumented)
+export interface TSInterfaceDeclaration extends Span {
+ // (undocumented)
+ body: TSInterfaceBody;
+ // (undocumented)
+ declare: boolean;
+ // (undocumented)
+ extends: Array;
+ // (undocumented)
+ id: BindingIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSInterfaceDeclaration";
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface TSInterfaceHeritage extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSInterfaceHeritage";
+ // (undocumented)
+ typeArguments: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export interface TSIntersectionType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSIntersectionType";
+ // (undocumented)
+ types: Array;
+}
+
+// @public (undocumented)
+export interface TSIntrinsicKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSIntrinsicKeyword";
+}
+
+// @public (undocumented)
+export type TSLiteral = BooleanLiteral | NumericLiteral | BigIntLiteral | StringLiteral | TemplateLiteral | UnaryExpression;
+
+// @public (undocumented)
+export interface TSLiteralType extends Span {
+ // (undocumented)
+ literal: TSLiteral;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSLiteralType";
+}
+
+// @public (undocumented)
+export interface TSMappedType extends Span {
+ // (undocumented)
+ constraint: TSType;
+ // (undocumented)
+ key: BindingIdentifier;
+ // (undocumented)
+ nameType: TSType | null;
+ // (undocumented)
+ optional: TSMappedTypeModifierOperator | false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly: TSMappedTypeModifierOperator | null;
+ // (undocumented)
+ type: "TSMappedType";
+ // (undocumented)
+ typeAnnotation: TSType | null;
+}
+
+// @public (undocumented)
+export type TSMappedTypeModifierOperator = true | "+" | "-";
+
+// @public (undocumented)
+export interface TSMethodSignature extends Span {
+ // (undocumented)
+ accessibility: null;
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ kind: TSMethodSignatureKind;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ params: ParamPattern[];
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly: false;
+ // (undocumented)
+ returnType: TSTypeAnnotation | null;
+ // (undocumented)
+ static: false;
+ // (undocumented)
+ type: "TSMethodSignature";
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export type TSMethodSignatureKind = "method" | "get" | "set";
+
+// @public (undocumented)
+export interface TSModuleBlock extends Span {
+ // (undocumented)
+ body: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSModuleBlock";
+}
+
+// @public (undocumented)
+export interface TSModuleDeclaration extends Span {
+ // (undocumented)
+ body: TSModuleBlock | null;
+ // (undocumented)
+ declare: boolean;
+ // (undocumented)
+ global: false;
+ // (undocumented)
+ id: BindingIdentifier | StringLiteral | TSQualifiedName;
+ // (undocumented)
+ kind: TSModuleDeclarationKind;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSModuleDeclaration";
+}
+
+// @public (undocumented)
+export type TSModuleDeclarationKind = "module" | "namespace";
+
+// @public (undocumented)
+export type TSModuleReference = TSExternalModuleReference | IdentifierReference | TSQualifiedName;
+
+// @public (undocumented)
+export interface TSNamedTupleMember extends Span {
+ // (undocumented)
+ elementType: TSTupleElement;
+ // (undocumented)
+ label: IdentifierName;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSNamedTupleMember";
+}
+
+// @public (undocumented)
+export interface TSNamespaceExportDeclaration extends Span {
+ // (undocumented)
+ id: IdentifierName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSNamespaceExportDeclaration";
+}
+
+// @public (undocumented)
+export interface TSNeverKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSNeverKeyword";
+}
+
+// @public (undocumented)
+export interface TSNonNullExpression extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSNonNullExpression";
+}
+
+// @public (undocumented)
+export interface TSNullKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSNullKeyword";
+}
+
+// @public (undocumented)
+export interface TSNumberKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSNumberKeyword";
+}
+
+// @public (undocumented)
+export interface TSObjectKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSObjectKeyword";
+}
+
+// @public (undocumented)
+export interface TSOptionalType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSOptionalType";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface TSParameterProperty extends Span {
+ // (undocumented)
+ accessibility: TSAccessibility | null;
+ // (undocumented)
+ decorators: Array;
+ // (undocumented)
+ override: boolean;
+ // (undocumented)
+ parameter: FormalParameter;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly: boolean;
+ // (undocumented)
+ static: boolean;
+ // (undocumented)
+ type: "TSParameterProperty";
+}
+
+// @public (undocumented)
+export interface TSParenthesizedType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSParenthesizedType";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface TSPropertySignature extends Span {
+ // (undocumented)
+ accessibility: null;
+ // (undocumented)
+ computed: boolean;
+ // (undocumented)
+ key: PropertyKey_2;
+ // (undocumented)
+ optional: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ readonly: boolean;
+ // (undocumented)
+ static: false;
+ // (undocumented)
+ type: "TSPropertySignature";
+ // (undocumented)
+ typeAnnotation: TSTypeAnnotation | null;
+}
+
+// @public (undocumented)
+export interface TSQualifiedName extends Span {
+ // (undocumented)
+ left: TSTypeName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ right: IdentifierName;
+ // (undocumented)
+ type: "TSQualifiedName";
+}
+
+// @public (undocumented)
+export interface TSRestType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSRestType";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface TSSatisfiesExpression extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSSatisfiesExpression";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export type TSSignature = TSIndexSignature | TSPropertySignature | TSCallSignatureDeclaration | TSConstructSignatureDeclaration | TSMethodSignature;
+
+// @public (undocumented)
+export interface TSStringKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSStringKeyword";
+}
+
+// @public (undocumented)
+export interface TSSymbolKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSSymbolKeyword";
+}
+
+// @public (undocumented)
+export interface TSTemplateLiteralType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ quasis: Array;
+ // (undocumented)
+ type: "TSTemplateLiteralType";
+ // (undocumented)
+ types: Array;
+}
+
+// @public (undocumented)
+export interface TSThisParameter extends Span {
+ // (undocumented)
+ decorators: [];
+ // (undocumented)
+ name: "this";
+ // (undocumented)
+ optional: false;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "Identifier";
+ // (undocumented)
+ typeAnnotation: TSTypeAnnotation | null;
+}
+
+// @public (undocumented)
+export interface TSThisType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSThisType";
+}
+
+// @public (undocumented)
+export type TSTupleElement = TSOptionalType | TSRestType | TSType;
+
+// @public (undocumented)
+export interface TSTupleType extends Span {
+ // (undocumented)
+ elementTypes: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTupleType";
+}
+
+// @public (undocumented)
+export type TSType = TSAnyKeyword | TSBigIntKeyword | TSBooleanKeyword | TSIntrinsicKeyword | TSNeverKeyword | TSNullKeyword | TSNumberKeyword | TSObjectKeyword | TSStringKeyword | TSSymbolKeyword | TSUndefinedKeyword | TSUnknownKeyword | TSVoidKeyword | TSArrayType | TSConditionalType | TSConstructorType | TSFunctionType | TSImportType | TSIndexedAccessType | TSInferType | TSIntersectionType | TSLiteralType | TSMappedType | TSNamedTupleMember | TSTemplateLiteralType | TSThisType | TSTupleType | TSTypeLiteral | TSTypeOperator | TSTypePredicate | TSTypeQuery | TSTypeReference | TSUnionType | TSParenthesizedType | JSDocNullableType | JSDocNonNullableType | JSDocUnknownType;
+
+// @public (undocumented)
+export interface TSTypeAliasDeclaration extends Span {
+ // (undocumented)
+ declare: boolean;
+ // (undocumented)
+ id: BindingIdentifier;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeAliasDeclaration";
+ // (undocumented)
+ typeAnnotation: TSType;
+ // (undocumented)
+ typeParameters: TSTypeParameterDeclaration | null;
+}
+
+// @public (undocumented)
+export interface TSTypeAnnotation extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeAnnotation";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface TSTypeAssertion extends Span {
+ // (undocumented)
+ expression: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeAssertion";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export interface TSTypeLiteral extends Span {
+ // (undocumented)
+ members: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeLiteral";
+}
+
+// @public (undocumented)
+export type TSTypeName = IdentifierReference | TSQualifiedName | ThisExpression;
+
+// @public (undocumented)
+export interface TSTypeOperator extends Span {
+ // (undocumented)
+ operator: TSTypeOperatorOperator;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeOperator";
+ // (undocumented)
+ typeAnnotation: TSType;
+}
+
+// @public (undocumented)
+export type TSTypeOperatorOperator = "keyof" | "unique" | "readonly";
+
+// @public (undocumented)
+export interface TSTypeParameter extends Span {
+ // (undocumented)
+ const: boolean;
+ // (undocumented)
+ constraint: TSType | null;
+ // (undocumented)
+ default: TSType | null;
+ // (undocumented)
+ in: boolean;
+ // (undocumented)
+ name: BindingIdentifier;
+ // (undocumented)
+ out: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeParameter";
+}
+
+// @public (undocumented)
+export interface TSTypeParameterDeclaration extends Span {
+ // (undocumented)
+ params: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeParameterDeclaration";
+}
+
+// @public (undocumented)
+export interface TSTypeParameterInstantiation extends Span {
+ // (undocumented)
+ params: Array;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeParameterInstantiation";
+}
+
+// @public (undocumented)
+export interface TSTypePredicate extends Span {
+ // (undocumented)
+ asserts: boolean;
+ // (undocumented)
+ parameterName: TSTypePredicateName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypePredicate";
+ // (undocumented)
+ typeAnnotation: TSTypeAnnotation | null;
+}
+
+// @public (undocumented)
+export type TSTypePredicateName = IdentifierName | TSThisType;
+
+// @public (undocumented)
+export interface TSTypeQuery extends Span {
+ // (undocumented)
+ exprName: TSTypeQueryExprName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeQuery";
+ // (undocumented)
+ typeArguments: TSTypeParameterInstantiation | null;
+}
+
+// @public (undocumented)
+export type TSTypeQueryExprName = TSImportType | TSTypeName;
+
+// @public (undocumented)
+export interface TSTypeReference extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSTypeReference";
+ // (undocumented)
+ typeArguments: TSTypeParameterInstantiation | null;
+ // (undocumented)
+ typeName: TSTypeName;
+}
+
+// @public (undocumented)
+export interface TSUndefinedKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSUndefinedKeyword";
+}
+
+// @public (undocumented)
+export interface TSUnionType extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSUnionType";
+ // (undocumented)
+ types: Array;
+}
+
+// @public (undocumented)
+export interface TSUnknownKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSUnknownKeyword";
+}
+
+// @public (undocumented)
+export interface TSVoidKeyword extends Span {
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "TSVoidKeyword";
+}
+
+// @public (undocumented)
+export interface UnaryExpression extends Span {
+ // (undocumented)
+ argument: Expression;
+ // (undocumented)
+ operator: UnaryOperator;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ prefix: true;
+ // (undocumented)
+ type: "UnaryExpression";
+}
+
+// @public (undocumented)
+export type UnaryOperator = "+" | "-" | "!" | "~" | "typeof" | "void" | "delete";
+
+// @public (undocumented)
+export interface UpdateExpression extends Span {
+ // (undocumented)
+ argument: SimpleAssignmentTarget;
+ // (undocumented)
+ operator: UpdateOperator;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ prefix: boolean;
+ // (undocumented)
+ type: "UpdateExpression";
+}
+
+// @public (undocumented)
+export type UpdateOperator = "++" | "--";
+
+// @public (undocumented)
+export interface V8IntrinsicExpression extends Span {
+ // (undocumented)
+ arguments: Array;
+ // (undocumented)
+ name: IdentifierName;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "V8IntrinsicExpression";
+}
+
+// @public (undocumented)
+export interface VariableDeclaration extends Span {
+ // (undocumented)
+ declarations: Array;
+ // (undocumented)
+ declare?: boolean;
+ // (undocumented)
+ kind: VariableDeclarationKind;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "VariableDeclaration";
+}
+
+// @public (undocumented)
+export type VariableDeclarationKind = "var" | "let" | "const" | "using" | "await using";
+
+// @public (undocumented)
+export interface VariableDeclarator extends Span {
+ // (undocumented)
+ definite?: boolean;
+ // (undocumented)
+ id: BindingPattern;
+ // (undocumented)
+ init: Expression | null;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "VariableDeclarator";
+}
+
+// @public (undocumented)
+export interface WhileStatement extends Span {
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ test: Expression;
+ // (undocumented)
+ type: "WhileStatement";
+}
+
+// @public (undocumented)
+export interface WithStatement extends Span {
+ // (undocumented)
+ body: Statement;
+ // (undocumented)
+ object: Expression;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "WithStatement";
+}
+
+// @public (undocumented)
+export interface YieldExpression extends Span {
+ // (undocumented)
+ argument: Expression | null;
+ // (undocumented)
+ delegate: boolean;
+ // (undocumented)
+ parent?: Node_2;
+ // (undocumented)
+ type: "YieldExpression";
+}
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/ignorers/interface/oxlint.config.ts b/packages/ignorers/interface/oxlint.config.ts
new file mode 100644
index 000000000..879f8a966
--- /dev/null
+++ b/packages/ignorers/interface/oxlint.config.ts
@@ -0,0 +1,6 @@
+import preset from '@systemfsoftware/oxlint-ignorer-config'
+import { defineConfig } from 'oxlint'
+
+export default defineConfig({
+ extends: [preset],
+})
diff --git a/packages/ignorers/interface/package.json b/packages/ignorers/interface/package.json
new file mode 100644
index 000000000..d14e26bdb
--- /dev/null
+++ b/packages/ignorers/interface/package.json
@@ -0,0 +1,69 @@
+{
+ "name": "@systemfsoftware/stryker-ignorer-interface",
+ "license": "Apache-2.0",
+ "version": "0.0.0",
+ "author": "Ryan Lee ",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
+ "directory": "packages/ignorers/interface"
+ },
+ "homepage": "https://github.com/systemfsoftware/stryker-js-effect/tree/main/packages/ignorers/interface#readme",
+ "bugs": "https://github.com/systemfsoftware/stryker-js-effect/issues",
+ "description": "The ignorer interface for StrykerJS-style mutation tooling — the PlainIgnorer descriptor, the canonical AST node vocabulary, and the NodePath shape. Types only, with no runtime exports and no Effect dependency.",
+ "keywords": [
+ "stryker",
+ "mutation-testing",
+ "ignorer",
+ "plugin",
+ "ast",
+ "estree",
+ "test-quality"
+ ],
+ "type": "module",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "build": "tsdown && pnpm api:check",
+ "clean": "rimraf dist",
+ "typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
+ "test": "vitest run",
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
+ "attw": "attw --pack .",
+ "api:check": "tsdown && api-extractor run",
+ "api:update": "api-extractor run --local"
+ },
+ "devDependencies": {
+ "@microsoft/api-extractor": "catalog:",
+ "@oxc-project/types": "catalog:",
+ "@systemfsoftware/arethetypeswrong-cli": "catalog:",
+ "@systemfsoftware/oxlint-ignorer-config": "workspace:^",
+ "@systemfsoftware/tsconfig": "catalog:",
+ "@systemfsoftware/vitest-config": "workspace:^",
+ "@types/node": "catalog:",
+ "oxlint": "catalog:",
+ "rimraf": "catalog:",
+ "tsdown": "catalog:",
+ "typescript": "catalog:",
+ "vitest": "catalog:"
+ },
+ "publishConfig": {
+ "access": "public",
+ "provenance": true,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ }
+ }
+}
diff --git a/packages/ignorers/interface/src/mod.ts b/packages/ignorers/interface/src/mod.ts
new file mode 100644
index 000000000..81e169b1e
--- /dev/null
+++ b/packages/ignorers/interface/src/mod.ts
@@ -0,0 +1,11 @@
+export type * from '@oxc-project/types'
+
+export interface NodePath {
+ readonly node: unknown
+ readonly ancestors: readonly unknown[]
+}
+
+export interface PlainIgnorer {
+ readonly name: string
+ shouldIgnore(path: NodePath): string | undefined
+}
diff --git a/packages/ignorers/interface/tsconfig.build.json b/packages/ignorers/interface/tsconfig.build.json
new file mode 100644
index 000000000..869b9aaa9
--- /dev/null
+++ b/packages/ignorers/interface/tsconfig.build.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "declarationMap": false,
+ "sourceMap": false,
+ "stripInternal": true
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.tst.ts"]
+}
diff --git a/packages/ignorers/interface/tsconfig.json b/packages/ignorers/interface/tsconfig.json
new file mode 100644
index 000000000..04c392e87
--- /dev/null
+++ b/packages/ignorers/interface/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": ["@systemfsoftware/tsconfig/tsc/dom/library-monorepo"],
+ "compilerOptions": {
+ "types": []
+ },
+ "include": [
+ "src",
+ "tests"
+ ],
+ "exclude": [
+ "node_modules",
+ "dist"
+ ],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
+}
diff --git a/packages/ignorers/interface/tsconfig.node.json b/packages/ignorers/interface/tsconfig.node.json
new file mode 100644
index 000000000..4d32e1ec2
--- /dev/null
+++ b/packages/ignorers/interface/tsconfig.node.json
@@ -0,0 +1,4 @@
+{
+ "extends": "@systemfsoftware/tsconfig/node",
+ "include": ["oxlint.config.ts", "tsdown.config.ts", "vitest.config.ts"]
+}
diff --git a/packages/ignorers/interface/tsdown.config.ts b/packages/ignorers/interface/tsdown.config.ts
new file mode 100644
index 000000000..6a4bc1e03
--- /dev/null
+++ b/packages/ignorers/interface/tsdown.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'tsdown'
+
+export default defineConfig({
+ entry: {
+ index: './src/mod.ts',
+ },
+ format: 'esm',
+ dts: true,
+ tsconfig: './tsconfig.build.json',
+ clean: true,
+ outExtensions: () => ({ js: '.mjs', dts: '.d.ts' }),
+})
diff --git a/packages/ignorers/interface/vitest.config.ts b/packages/ignorers/interface/vitest.config.ts
new file mode 100644
index 000000000..6536c4494
--- /dev/null
+++ b/packages/ignorers/interface/vitest.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig, sharedConfig } from '@systemfsoftware/vitest-config'
+
+export default defineConfig({
+ ...sharedConfig,
+ test: {
+ ...sharedConfig.test,
+ include: ['src/**/*.test.ts', 'tests/**/*.test.ts'],
+ },
+})
diff --git a/packages/stryker-js-cli/package.json b/packages/stryker-js-cli/package.json
index 81d507f0a..a403a7201 100644
--- a/packages/stryker-js-cli/package.json
+++ b/packages/stryker-js-cli/package.json
@@ -36,10 +36,6 @@
"@systemfsoftware/stryker-js-html-reporter": "workspace:^",
"@systemfsoftware/stryker-js-language": "workspace:^",
"@systemfsoftware/stryker-js-plugin-interface": "workspace:^",
- "@systemfsoftware/stryker-js-typescript-checker": "workspace:^",
- "@systemfsoftware/stryker-js-vitest-runner": "workspace:^",
- "@systemfsoftware/stryker-plugins": "workspace:^",
- "@systemfsoftware/stryker-test-contribution": "workspace:^",
"@systemfsoftware/tsconfig": "catalog:",
"@systemfsoftware/vitest-config": "workspace:^",
"@types/node": "catalog:",
@@ -50,7 +46,6 @@
"oxlint": "catalog:",
"rimraf": "catalog:",
"tsdown": "catalog:",
- "vite-tsconfig-paths": "catalog:",
"vitest": "catalog:"
},
"engines": {
diff --git a/packages/stryker-js-engine/api-extractor.base.json b/packages/stryker-js-engine/api-extractor.base.json
new file mode 100644
index 000000000..cd855e92a
--- /dev/null
+++ b/packages/stryker-js-engine/api-extractor.base.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/config/base.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.api-base.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/base/",
+ "reportFileName": "base.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/config/base.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-engine/api-extractor.builtin-reporters.json b/packages/stryker-js-engine/api-extractor.builtin-reporters.json
new file mode 100644
index 000000000..2bde55f11
--- /dev/null
+++ b/packages/stryker-js-engine/api-extractor.builtin-reporters.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/builtin-reporters.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.api-builtin-reporters.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/builtin-reporters/",
+ "reportFileName": "builtin-reporters.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/builtin-reporters.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-engine/api-extractor.json b/packages/stryker-js-engine/api-extractor.json
new file mode 100644
index 000000000..d5a9a6623
--- /dev/null
+++ b/packages/stryker-js-engine/api-extractor.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/index.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/main/",
+ "reportFileName": "stryker-js-engine.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/index.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-engine/api-extractor.plugin-loader.json b/packages/stryker-js-engine/api-extractor.plugin-loader.json
new file mode 100644
index 000000000..9119095d8
--- /dev/null
+++ b/packages/stryker-js-engine/api-extractor.plugin-loader.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/plugin-loader.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.api-plugin-loader.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/plugin-loader/",
+ "reportFileName": "plugin-loader.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/plugin-loader.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-engine/api-extractor.worker.json b/packages/stryker-js-engine/api-extractor.worker.json
new file mode 100644
index 000000000..8f789dfcd
--- /dev/null
+++ b/packages/stryker-js-engine/api-extractor.worker.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/worker.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.api-worker.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/worker/",
+ "reportFileName": "worker.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/worker.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-engine/etc/base.api.md b/packages/stryker-js-engine/etc/base.api.md
new file mode 100644
index 000000000..68396656d
--- /dev/null
+++ b/packages/stryker-js-engine/etc/base.api.md
@@ -0,0 +1,18 @@
+## API Report File for "@systemfsoftware/stryker-js-engine"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import * as S from 'effect/Schema';
+import { StrykerOptionsSchema } from '@systemfsoftware/stryker-js-language';
+
+// Warning: (ae-forgotten-export) The symbol "BasePreset" needs to be exported by the entry point base.d.mts
+//
+// @public (undocumented)
+const basePreset: BasePreset;
+export default basePreset;
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-engine/etc/builtin-reporters.api.md b/packages/stryker-js-engine/etc/builtin-reporters.api.md
new file mode 100644
index 000000000..55a165249
--- /dev/null
+++ b/packages/stryker-js-engine/etc/builtin-reporters.api.md
@@ -0,0 +1,24 @@
+## API Report File for "@systemfsoftware/stryker-js-engine"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import * as FileSystem from 'effect/FileSystem';
+import * as Path from 'effect/Path';
+import { ReporterFactory } from '@systemfsoftware/stryker-js-language';
+
+// @public (undocumented)
+export interface JsonReporterDeps {
+ // (undocumented)
+ readonly fileSystem: FileSystem.FileSystem;
+ // (undocumented)
+ readonly path: Path.Path;
+}
+
+// @public (undocumented)
+export const makeBuiltinReporterFactories: (services: JsonReporterDeps) => Record;
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-engine/etc/plugin-loader.api.md b/packages/stryker-js-engine/etc/plugin-loader.api.md
new file mode 100644
index 000000000..2e9e7f29f
--- /dev/null
+++ b/packages/stryker-js-engine/etc/plugin-loader.api.md
@@ -0,0 +1,83 @@
+## API Report File for "@systemfsoftware/stryker-js-engine"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import { AnyPluginContribution } from '@systemfsoftware/stryker-js-plugin-interface';
+import { ContributionOf } from '@systemfsoftware/stryker-js-plugin-interface';
+import * as Effect from 'effect/Effect';
+import * as FileSystem from 'effect/FileSystem';
+import * as HashMap from 'effect/HashMap';
+import { Module } from '@systemfsoftware/stryker-js-language';
+import * as Path from 'effect/Path';
+import { PluginContribution } from '@systemfsoftware/stryker-js-plugin-interface';
+import { PluginKind } from '@systemfsoftware/stryker-js-plugin-interface';
+import { Schema } from 'effect';
+import { YieldableError } from 'effect/Cause';
+
+// @public (undocumented)
+export function create(pluginsByKind: HashMap.HashMap, kind: K, name: string): Effect.Effect, PluginNotFoundError>;
+
+// @public (undocumented)
+export function createAll(pluginsByKind: HashMap.HashMap, kind: K): Effect.Effect[]>;
+
+// @public (undocumented)
+export interface LoadedPlugins {
+ // (undocumented)
+ readonly pluginModulePaths: readonly string[];
+ // (undocumented)
+ readonly pluginsByKind: HashMap.HashMap[]>;
+ // (undocumented)
+ readonly schemaContributions: readonly Record[];
+}
+
+// @public (undocumented)
+export function loadPlugins(pluginDescriptors: readonly string[], basePath: string): Effect.Effect;
+
+// @public (undocumented)
+export interface PluginLoaderEntryLike {
+ // (undocumented)
+ readonly moduleName: string;
+ // (undocumented)
+ readonly plugins: readonly PluginContribution[] | undefined;
+ // (undocumented)
+ readonly schemaContribution: Record | undefined;
+}
+
+// Warning: (ae-forgotten-export) The symbol "PluginLoadFailedError_base" needs to be exported by the entry point plugin-loader.d.mts
+//
+// @public (undocumented)
+export class PluginLoadFailedError extends PluginLoadFailedError_base {
+ // (undocumented)
+ readonly exitClass: 'InternalError';
+}
+
+// @public (undocumented)
+export interface PluginLoadPlan {
+ // (undocumented)
+ readonly pluginModulePaths: readonly string[];
+ // (undocumented)
+ readonly pluginsByKind: HashMap.HashMap[]>;
+ // (undocumented)
+ readonly schemaContributions: readonly Record[];
+ // (undocumented)
+ readonly shadowings: readonly {
+ readonly kind: PluginKind;
+ readonly name: string;
+ readonly shadowedIndex: number;
+ readonly winnerIndex: number;
+ }[];
+}
+
+// Warning: (ae-forgotten-export) The symbol "PluginNotFoundError_base" needs to be exported by the entry point plugin-loader.d.mts
+//
+// @public (undocumented)
+export class PluginNotFoundError extends PluginNotFoundError_base {
+ // (undocumented)
+ readonly exitClass: 'ConfigError';
+}
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-engine/etc/stryker-js-engine.api.md b/packages/stryker-js-engine/etc/stryker-js-engine.api.md
new file mode 100644
index 000000000..3c6ff1ab9
--- /dev/null
+++ b/packages/stryker-js-engine/etc/stryker-js-engine.api.md
@@ -0,0 +1,607 @@
+## API Report File for "@systemfsoftware/stryker-js-engine"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import * as Cause from 'effect/Cause';
+import { CheckResult } from '@systemfsoftware/stryker-js-language';
+import * as ChildProcessSpawner from 'effect/unstable/process/ChildProcessSpawner';
+import { CompleteDryRunResult } from '@systemfsoftware/stryker-js-language';
+import { ComposedPlugins } from '@systemfsoftware/stryker-js-plugin-interface';
+import * as Context from 'effect/Context';
+import { CoverageData } from '@systemfsoftware/stryker-js-language';
+import { Duration } from 'effect/Duration';
+import * as Duration_2 from 'effect/Duration';
+import * as Effect from 'effect/Effect';
+import * as Exit from 'effect/Exit';
+import { ExitClass } from '@systemfsoftware/stryker-js-language';
+import { FileDescription } from '@systemfsoftware/stryker-js-language';
+import { FileDescriptions } from '@systemfsoftware/stryker-js-language';
+import * as FileSystem from 'effect/FileSystem';
+import * as HashMap from 'effect/HashMap';
+import { IgnorerService } from '@systemfsoftware/stryker-js-language';
+import * as Layer from 'effect/Layer';
+import { Module } from '@systemfsoftware/stryker-js-language';
+import * as MutableHashMap from 'effect/MutableHashMap';
+import * as MutableHashSet from 'effect/MutableHashSet';
+import { Mutant } from '@systemfsoftware/stryker-js-language';
+import { MutantStatus } from '@systemfsoftware/stryker-js-language';
+import { MutateDescription } from '@systemfsoftware/stryker-js-language';
+import { MutationTestResult } from '@systemfsoftware/stryker-js-language';
+import { PartialStrykerOptions } from '@systemfsoftware/stryker-js-language';
+import * as Path from 'effect/Path';
+import { PluginContribution } from '@systemfsoftware/stryker-js-plugin-interface';
+import { PluginKind } from '@systemfsoftware/stryker-js-plugin-interface';
+import * as Queue from 'effect/Queue';
+import * as RpcClient from 'effect/unstable/rpc/RpcClient';
+import { RunEvent } from '@systemfsoftware/stryker-js-language';
+import { RunEvents } from '@systemfsoftware/stryker-js-language';
+import { RunMutantResult } from '@systemfsoftware/stryker-js-language';
+import { RunPlan } from '@systemfsoftware/stryker-js-language';
+import * as S from 'effect/Schema';
+import * as Schedule from 'effect/Schedule';
+import { Schema } from 'effect';
+import * as schema from '@systemfsoftware/stryker-js-language';
+import * as Scope from 'effect/Scope';
+import * as Socket from 'effect/unstable/socket/Socket';
+import { StrykerOptions } from '@systemfsoftware/stryker-js-language';
+import { TestResult } from '@systemfsoftware/stryker-js-language';
+import { YieldableError } from 'effect/Cause';
+
+// @public (undocumented)
+export const ACTIONABLE_STATUSES: readonly ['Survived', 'NoCoverage', 'Timeout', 'RuntimeError'];
+
+// @public (undocumented)
+export function buildVerdictEnvelope(report: schema.MutationTestResult, mode: OutputMode, signal: ModeSignal, runId: string, basePath: string, pathService: Path.Path): VerdictEnvelope;
+
+// @public
+export interface CheckerResourceService {
+ // Warning: (ae-forgotten-export) The symbol "CheckerCrash" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly check: (checkerName: string, mutants: readonly Mutant[]) => Effect.Effect, CheckerCrash>;
+ readonly group: (checkerName: string, mutants: readonly Mutant[]) => Effect.Effect;
+}
+
+// Warning: (ae-forgotten-export) The symbol "CheckerContractBroken" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export const checkGroupedPlans: (checker: CheckerResourceService, checkerName: string, plans: readonly RunPlan[]) => Effect.Effect;
+
+// Warning: (ae-forgotten-export) The symbol "ChildProcessCrashedError_base" needs to be exported by the entry point index.d.mts
+//
+// @public
+export class ChildProcessCrashedError extends ChildProcessCrashedError_base {
+ // (undocumented)
+ readonly exitClass: 'InternalError';
+}
+
+// @public (undocumented)
+export const CONFIG_SYNTAX_HELP: string;
+
+// @public (undocumented)
+export const ConfigDocumentSchema: S.$Record;
+
+// Warning: (ae-forgotten-export) The symbol "ConfigError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class ConfigError extends ConfigError_base {
+ // (undocumented)
+ readonly exitClass: 'ConfigError';
+}
+
+// Warning: (ae-forgotten-export) The symbol "ConfigFileInvalidError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class ConfigFileInvalidError extends ConfigFileInvalidError_base {
+ // (undocumented)
+ readonly exitClass: 'ConfigError';
+}
+
+// Warning: (ae-forgotten-export) The symbol "ConfigFileNotFoundError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class ConfigFileNotFoundError extends ConfigFileNotFoundError_base {
+ // (undocumented)
+ readonly exitClass: 'ConfigError';
+}
+
+// Warning: (ae-forgotten-export) The symbol "ConfigFileUnreadableError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class ConfigFileUnreadableError extends ConfigFileUnreadableError_base {
+ // (undocumented)
+ readonly exitClass: 'ConfigError';
+}
+
+// @public
+export const connectRetry: Schedule.Schedule;
+
+// @public (undocumented)
+export function createDefaultOptions(): Effect.Effect;
+
+// @public (undocumented)
+export function createFileMatcher(pattern: boolean | string, pathService: Path.Path, allowHiddenFiles?: boolean): (fileName: string) => boolean;
+
+// @public (undocumented)
+export const decideExtendsStep: (state: ExtendsStepState, document: PartialStrykerOptions, file: string, pathService: Path.Path) => ExtendsStepDecision;
+
+// @public (undocumented)
+export function deepFreeze(target: T): Immutable;
+
+// @public (undocumented)
+export const DEFAULT_CONFIG_FILE_NAMES: Readonly<{
+ readonly JSON: 'stryker.config.json';
+ readonly JAVASCRIPT: 'stryker.config.mjs';
+}>;
+
+// @public (undocumented)
+export const defaultOptions: Effect.Effect, never, never>;
+
+// @public (undocumented)
+export function describeErrors(error: S.SchemaError): string[];
+
+// @public (undocumented)
+export interface DryRunDone extends InstrumentDone {
+ // (undocumented)
+ readonly dryRunResult: CompleteDryRunResult;
+ // Warning: (ae-forgotten-export) The symbol "TestCoverage" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly testCoverage: TestCoverage;
+ // (undocumented)
+ readonly timeOverhead: Duration_2.Duration;
+}
+
+// @public (undocumented)
+export type EnginePorts = ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Module | Path.Path | WorkerEntries | WorkerLauncher;
+
+// @public (undocumented)
+export const extendsPropertySchema: S.optionalKey;
+
+// @public (undocumented)
+export type ExtendsRefusalReason = 'cycle' | 'non-string-extends';
+
+// Warning: (ae-forgotten-export) The symbol "DoneTag" needs to be exported by the entry point index.d.mts
+// Warning: (ae-forgotten-export) The symbol "ReadTag" needs to be exported by the entry point index.d.mts
+// Warning: (ae-forgotten-export) The symbol "ResolveTag" needs to be exported by the entry point index.d.mts
+// Warning: (ae-forgotten-export) The symbol "RefusedTag" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export type ExtendsStepDecision = (DoneTag & {
+ readonly options: PartialStrykerOptions;
+}) | (ReadTag & {
+ readonly path: string;
+ readonly state: ExtendsStepState;
+}) | (ResolveTag & {
+ readonly specifier: string;
+ readonly directory: string;
+ readonly state: ExtendsStepState;
+}) | (RefusedTag & {
+ readonly reason: ExtendsRefusalReason;
+ readonly file: string;
+});
+
+// @public (undocumented)
+export interface ExtendsStepDocument {
+ // (undocumented)
+ readonly options: PartialStrykerOptions;
+ // (undocumented)
+ readonly path: string;
+}
+
+// @public (undocumented)
+export interface ExtendsStepState {
+ // (undocumented)
+ readonly documents: readonly ExtendsStepDocument[];
+ // (undocumented)
+ readonly visited: readonly string[];
+}
+
+// @public (undocumented)
+export function findUnserializables(thing: unknown): UnserializableDescription[] | undefined;
+
+// @public (undocumented)
+export const forkCoreSchema: Record;
+
+// @public (undocumented)
+export const forkOptionsSchema: S.StructWithRest;
+ readonly extends: S.optionalKey;
+}>, readonly [S.$Record]>;
+
+// @public (undocumented)
+export function generateRunId(): string;
+
+// Warning: (ae-forgotten-export) The symbol "ImmutablePrimitive" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export type Immutable = T extends ImmutablePrimitive ? T : T extends Array ? ReadonlyArray> : T extends Map ? ReadonlyMap, Immutable> : T extends Set ? ReadonlySet> : T extends RegExp ? Readonly : { readonly [K in keyof T]: Immutable; };
+
+// @public (undocumented)
+export const ImportedModuleSchema: S.Struct<{
+ readonly default: S.optional;
+}>;
+
+// @public (undocumented)
+export function importModule(moduleName: string, basePath: string): Effect.Effect;
+
+// @public (undocumented)
+export const IncrementalReportSchema: S.StructWithRest;
+ readonly files: S.$Record;
+ readonly end: S.Struct<{
+ readonly line: S.Finite;
+ readonly column: S.Finite;
+ }>;
+ }>;
+ readonly status: S.Literals;
+ readonly killedBy: S.optional>;
+ readonly coveredBy: S.optional>;
+ readonly static: S.optional;
+ readonly statusReason: S.optional;
+ readonly testsCompleted: S.optional;
+ readonly description: S.optional;
+ readonly duration: S.optional;
+ }>>;
+ }>>;
+ readonly testFiles: S.optional;
+ readonly tests: S.$Array;
+ readonly end: S.optional>;
+ }>>;
+ }>>;
+ }>>>;
+}>, readonly [S.$Record]>;
+
+// @public (undocumented)
+export const initialExtendsStepState: ExtendsStepState;
+
+// @public (undocumented)
+export interface InstrumentDone extends PrepareDone {
+ // (undocumented)
+ readonly concurrency: {
+ readonly testRunners: number;
+ readonly checkers: number;
+ };
+ // (undocumented)
+ readonly mutants: readonly Mutant[];
+ // Warning: (ae-forgotten-export) The symbol "SandboxHandle" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly sandbox: SandboxHandle;
+}
+
+// @public (undocumented)
+export function isActionableStatus(status: MutantStatus): boolean;
+
+// @public (undocumented)
+export function isModuleSpecifier(value: string): boolean;
+
+// Warning: (ae-forgotten-export) The symbol "KnownKeys" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export function isWarningEnabled(warningType: KnownKeys, warningOptions: WarningOptions | boolean): boolean;
+
+// Warning: (ae-forgotten-export) The symbol "IdGenerator" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export const makeRunLayer: (env: RunEnvironmentShape, events?: Queue.Queue) => Layer.Layer;
+
+// @public (undocumented)
+export function matchesFile(pattern: boolean | string, fileName: string, pathService: Path.Path, allowHiddenFiles?: boolean): boolean;
+
+// Warning: (ae-forgotten-export) The symbol "MergeCommand_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class MergeCommand extends MergeCommand_base {}
+
+// @public (undocumented)
+export function mergeConfigs(parent: PartialStrykerOptions, child: PartialStrykerOptions): PartialStrykerOptions;
+
+// @public (undocumented)
+export function mergeRecords(base: object, overrides: object): Record;
+
+// Warning: (ae-forgotten-export) The symbol "MergeResult_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class MergeResult extends MergeResult_base {}
+
+// @public (undocumented)
+export type ModeSignal = 'flag' | 'env' | 'tty' | 'agent' | 'tool';
+
+// @public (undocumented)
+export const optionsPath: (...path: string[]) => string;
+
+// Warning: (ae-forgotten-export) The symbol "OutOfMemoryError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class OutOfMemoryError extends OutOfMemoryError_base {
+ // (undocumented)
+ readonly exitClass: 'RuntimeError';
+}
+
+// @public
+export type OutputMode = 'human' | 'machine';
+
+// @public (undocumented)
+export interface PrepareDone {
+ // (undocumented)
+ readonly ignorers: readonly IgnorerService[];
+ // Warning: (ae-forgotten-export) The symbol "LoadedPlugins" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly loadedPlugins: LoadedPlugins;
+ // (undocumented)
+ readonly options: StrykerOptions;
+ // (undocumented)
+ readonly plugins: ComposedPlugins;
+ // Warning: (ae-forgotten-export) The symbol "Project" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly project: Project;
+ // Warning: (ae-forgotten-export) The symbol "ReporterStage" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly reporterStage: ReporterStage;
+ // (undocumented)
+ readonly temporaryDirectoryPath: string;
+}
+
+// @public (undocumented)
+export type Primitive = boolean | number | string | null | undefined;
+
+// @public (undocumented)
+export function readConfig(cliOptions: PartialStrykerOptions, basePath: string): Effect.Effect;
+
+// Warning: (ae-forgotten-export) The symbol "ReadConfigCommand_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class ReadConfigCommand extends ReadConfigCommand_base {}
+
+// @public (undocumented)
+export function readConfigFile(configFile: string): Effect.Effect;
+
+// @public (undocumented)
+export const REMOVED_OPTIONS: Record;
+
+// @public (undocumented)
+export interface ResolvedMode {
+ // (undocumented)
+ readonly mode: OutputMode;
+ // (undocumented)
+ readonly signal: ModeSignal;
+ // (undocumented)
+ readonly stdoutIsTTY: boolean;
+}
+
+// @public (undocumented)
+export function resolveExtends(configFile: string, document: PartialStrykerOptions): Effect.Effect;
+
+// Warning: (ae-forgotten-export) The symbol "RunEnvironment_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class RunEnvironment extends RunEnvironment_base {}
+
+// @public (undocumented)
+export interface RunEnvironmentShape {
+ // (undocumented)
+ readonly allowConsoleColors: boolean;
+ // (undocumented)
+ readonly basePath: string;
+ // (undocumented)
+ readonly reporterPluginModules: readonly string[];
+ // (undocumented)
+ readonly resolvedMode: ResolvedMode;
+ // (undocumented)
+ readonly runId: string;
+ // (undocumented)
+ readonly runStartedAt: number;
+}
+
+// Warning: (ae-forgotten-export) The symbol "StageServices" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export const runMutationTest: (cliOptions: PartialStrykerOptions, targetMutatePatterns?: string[]) => Effect.Effect;
+
+// @public (undocumented)
+export interface RunOutcome {
+ // (undocumented)
+ readonly results: readonly RunMutantResult[];
+ // (undocumented)
+ readonly verdict: ExitClass | null;
+}
+
+// @public (undocumented)
+export const shouldKeepTempDir: (exit: Exit.Exit, cleanTempDir: 'always' | boolean) => boolean;
+
+// @public
+export interface SpawnedSocketWorker {
+ // (undocumented)
+ readonly clientLayer: Layer.Layer;
+ readonly exited: Effect.Effect;
+ // (undocumented)
+ readonly pid: number;
+}
+
+// Warning: (ae-forgotten-export) The symbol "StageError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class StageError extends StageError_base {
+ // Warning: (ae-forgotten-export) The symbol "TypeId" needs to be exported by the entry point index.d.mts
+ //
+ // (undocumented)
+ readonly [TypeId]: "~stryker/mutation-run/StageError";
+ // (undocumented)
+ get exitClass(): ExitClass;
+ // (undocumented)
+ get message(): string;
+}
+
+// Warning: (ae-forgotten-export) The symbol "StrykerError_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class StrykerError extends StrykerError_base {}
+
+// @public (undocumented)
+export const strykerVersion: string;
+
+// @public (undocumented)
+export const SUPPORTED_CONFIG_FILE_NAMES: readonly string[];
+
+// @public (undocumented)
+export const survivorsPriorReport: S.optionalKey;
+
+// @public (undocumented)
+export const toRelativeNormalizedFileName: (fileName: string | undefined, basePath: string) => string;
+
+// @public (undocumented)
+export interface UnserializableDescription {
+ // (undocumented)
+ path: string[];
+ // (undocumented)
+ reason: string;
+}
+
+// @public (undocumented)
+export function validateOptions(options: Record, schema: ValidationSchemaDocument): Effect.Effect;
+
+// @public (undocumented)
+export type ValidationSchemaDocument = {
+ readonly properties?: unknown;
+ readonly [key: string]: unknown;
+};
+
+// @public (undocumented)
+export const VERDICT_ENVELOPE_SCHEMA_VERSION = "1.1";
+
+// @public (undocumented)
+export interface VerdictCounts {
+ // (undocumented)
+ readonly compileErrors: number;
+ // (undocumented)
+ readonly ignored: number;
+ // (undocumented)
+ readonly killed: number;
+ // (undocumented)
+ readonly noCoverage: number;
+ // (undocumented)
+ readonly pending: number;
+ // (undocumented)
+ readonly runtimeErrors: number;
+ // (undocumented)
+ readonly survived: number;
+ // (undocumented)
+ readonly timeout: number;
+}
+
+// @public (undocumented)
+export interface VerdictEnvelope {
+ // (undocumented)
+ readonly counts: VerdictCounts;
+ // (undocumented)
+ readonly mode: OutputMode;
+ // (undocumented)
+ readonly mutants: readonly VerdictMutant[];
+ // (undocumented)
+ readonly reportFile: string | null;
+ // (undocumented)
+ readonly runId: string;
+ // (undocumented)
+ readonly schemaVersion: string;
+ // (undocumented)
+ readonly score: number | null;
+ // (undocumented)
+ readonly signal: ModeSignal;
+ // (undocumented)
+ readonly thresholds: VerdictThresholds;
+}
+
+// @public (undocumented)
+export interface VerdictMutant {
+ // (undocumented)
+ readonly file: string;
+ // (undocumented)
+ readonly id: string;
+ // (undocumented)
+ readonly location: schema.Location;
+ // (undocumented)
+ readonly mutator: string;
+ // (undocumented)
+ readonly replacement: string | null;
+ // (undocumented)
+ readonly status: MutantStatus;
+}
+
+// @public (undocumented)
+export interface VerdictThresholds {
+ // (undocumented)
+ readonly break: number | null;
+ // (undocumented)
+ readonly high: number;
+ // (undocumented)
+ readonly low: number;
+}
+
+// @public (undocumented)
+export type WarningOptions = Exclude;
+
+// Warning: (ae-forgotten-export) The symbol "WorkerEntries_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class WorkerEntries extends WorkerEntries_base {}
+
+// @public
+export interface WorkerEntriesShape {
+ // (undocumented)
+ readonly checkerWorkerUrl: URL;
+ // (undocumented)
+ readonly testRunnerWorkerUrl: URL;
+}
+
+// Warning: (ae-forgotten-export) The symbol "WorkerLauncher_base" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export class WorkerLauncher extends WorkerLauncher_base {}
+
+// @public
+export interface WorkerLauncherShape {
+ // (undocumented)
+ readonly spawn: (params: {
+ readonly entryUrl: URL;
+ readonly workingDirectory: string;
+ readonly execArgv: readonly string[];
+ readonly optionsJson: string;
+ readonly tempDirPrefix: string;
+ }) => Effect.Effect;
+}
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-engine/etc/worker.api.md b/packages/stryker-js-engine/etc/worker.api.md
new file mode 100644
index 000000000..9fd3862b5
--- /dev/null
+++ b/packages/stryker-js-engine/etc/worker.api.md
@@ -0,0 +1,222 @@
+## API Report File for "@systemfsoftware/stryker-js-engine"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import { AnyPluginContribution } from '@systemfsoftware/stryker-js-plugin-interface';
+import { CheckerFailed } from '@systemfsoftware/stryker-js-language';
+import { ContributionOf } from '@systemfsoftware/stryker-js-plugin-interface';
+import * as Effect from 'effect/Effect';
+import * as FileSystem from 'effect/FileSystem';
+import * as HashMap from 'effect/HashMap';
+import { Module } from '@systemfsoftware/stryker-js-language';
+import { Mutant } from '@systemfsoftware/stryker-js-language';
+import * as Path from 'effect/Path';
+import { PluginContribution } from '@systemfsoftware/stryker-js-plugin-interface';
+import { PluginKind } from '@systemfsoftware/stryker-js-plugin-interface';
+import * as Rpc from 'effect/unstable/rpc/Rpc';
+import * as RpcGroup from 'effect/unstable/rpc/RpcGroup';
+import * as S from 'effect/Schema';
+import { Schema } from 'effect';
+import { StrykerOptions } from '@systemfsoftware/stryker-js-language';
+import { TestRunnerFailed } from '@systemfsoftware/stryker-js-language';
+import { YieldableError } from 'effect/Cause';
+
+// @public (undocumented)
+export const CheckerRpcs: RpcGroup.RpcGroup;
+}>, Schema.$Record;
+}>, Schema.Struct<{
+ readonly status: Schema.Literal<"compileError">;
+ readonly reason: Schema.String;
+}>]>>, typeof CheckerFailed, never, never> | Rpc.Rpc<"group", Schema.Struct<{
+ checkerName: Schema.String;
+ mutants: Schema.$Array;
+}>, Schema.$Array>, typeof CheckerFailed, never, never>>;
+
+// Warning: (ae-forgotten-export) The symbol "PluginNotFoundError" needs to be exported by the entry point worker.d.mts
+//
+// @public (undocumented)
+export function create(pluginsByKind: HashMap.HashMap, kind: K, name: string): Effect.Effect, PluginNotFoundError>;
+
+// @public (undocumented)
+export const decodeWorkerOptions: (raw: string) => Effect.Effect<{
+ readonly [x: string]: unknown;
+ readonly allowConsoleColors: boolean;
+ readonly buildCommand?: string | undefined;
+ readonly checkers: readonly string[];
+ readonly checkerNodeArgs: readonly string[];
+ readonly concurrency?: string | number | undefined;
+ readonly commandRunner: {
+ readonly [x: string]: unknown;
+ readonly command: string;
+ };
+ readonly coverageAnalysis: "all" | "off" | "perTest";
+ readonly clearTextReporter: {
+ readonly [x: string]: unknown;
+ readonly allowColor: boolean;
+ readonly allowEmojis: boolean;
+ readonly logTests: boolean;
+ readonly maxTestsToLog: number;
+ readonly reportTests: boolean;
+ readonly reportMutants: boolean;
+ readonly reportScoreTable: boolean;
+ readonly skipFull: boolean;
+ };
+ readonly dryRunOnly: boolean;
+ readonly ignorePatterns: readonly string[];
+ readonly ignoreStatic: boolean;
+ readonly incremental: boolean;
+ readonly incrementalFile: string;
+ readonly progressStreamFile: string;
+ readonly force: boolean;
+ readonly fileLogLevel: "debug" | "error" | "fatal" | "info" | "off" | "trace" | "warn";
+ readonly inPlace: boolean;
+ readonly logLevel: "debug" | "error" | "fatal" | "info" | "off" | "trace" | "warn";
+ readonly maxConcurrentTestRunners: number;
+ readonly maxTestRunnerReuse: number;
+ readonly mutate: readonly string[];
+ readonly mutator: {
+ readonly excludedMutations: readonly string[];
+ };
+ readonly packageManager?: "npm" | "pnpm" | "yarn" | undefined;
+ readonly plugins: readonly string[];
+ readonly appendPlugins: readonly string[];
+ readonly reporters: readonly string[];
+ readonly htmlReporter: {
+ readonly fileName: string;
+ };
+ readonly jsonReporter: {
+ readonly fileName: string;
+ };
+ readonly disableTypeChecks: string | boolean;
+ readonly symlinkNodeModules: boolean;
+ readonly tempDirName: string;
+ readonly cleanTempDir: "always" | boolean;
+ readonly testRunner: string;
+ readonly testRunnerNodeArgs: readonly string[];
+ readonly thresholds: {
+ readonly high: number;
+ readonly low: number;
+ readonly break: number | null;
+ };
+ readonly timeoutFactor: number;
+ readonly timeoutMS: number;
+ readonly dryRunTimeoutMinutes: number;
+ readonly tsconfigFile: string;
+ readonly warnings: boolean | {
+ readonly [x: string]: unknown;
+ readonly unknownOptions: boolean;
+ readonly preprocessorErrors: boolean;
+ readonly unserializableOptions: boolean;
+ readonly slow: boolean;
+ };
+ readonly disableBail: boolean;
+ readonly allowEmpty: boolean;
+ readonly ignorers: readonly string[];
+ readonly testFiles: readonly string[];
+}, Schema.SchemaError, never>;
+
+// @public (undocumented)
+export const encodeWorkerOptions: (options: StrykerOptions) => Effect.Effect;
+
+// Warning: (ae-forgotten-export) The symbol "LoadedPlugins" needs to be exported by the entry point worker.d.mts
+// Warning: (ae-forgotten-export) The symbol "PluginLoadFailedError" needs to be exported by the entry point worker.d.mts
+//
+// @public (undocumented)
+export function loadPlugins(pluginDescriptors: readonly string[], basePath: string): Effect.Effect;
+
+// @public
+export const MutantCoverageSchema: S.Struct<{
+ readonly static: S.$Record;
+ readonly perTest: S.$Record>;
+}>;
+
+// @public (undocumented)
+export const TestRunnerRpcs: RpcGroup.RpcGroup, typeof TestRunnerFailed, never, never> | Rpc.Rpc<"dryRun", Schema.Struct<{
+ options: Schema.Struct<{
+ readonly timeout: Schema.Finite;
+ readonly disableBail: Schema.Boolean;
+ readonly coverageAnalysis: Schema.Literals;
+ readonly files: Schema.optionalKey>;
+ readonly testFiles: Schema.optionalKey>;
+ }>;
+}>, Schema.Union;
+ readonly tests: Schema.$Array;
+ readonly startPosition: Schema.optionalKey>;
+ readonly status: Schema.Literal<"failed">;
+ readonly failureMessage: Schema.String;
+ }>, Schema.Struct<{
+ readonly id: Schema.String;
+ readonly name: Schema.String;
+ readonly timeSpentMs: Schema.Finite;
+ readonly fileName: Schema.optionalKey;
+ readonly startPosition: Schema.optionalKey>;
+ readonly status: Schema.Literal<"skipped">;
+ }>, Schema.Struct<{
+ readonly id: Schema.String;
+ readonly name: Schema.String;
+ readonly timeSpentMs: Schema.Finite;
+ readonly fileName: Schema.optionalKey;
+ readonly startPosition: Schema.optionalKey>;
+ readonly status: Schema.Literal<"success">;
+ }>]>>;
+ readonly mutantCoverage: Schema.optionalKey>;
+ readonly static: Schema.$Record;
+ }>>;
+}>, Schema.Struct<{
+ readonly status: Schema.Literal<"timeout">;
+ readonly reason: Schema.optionalKey;
+}>, Schema.Struct<{
+ readonly status: Schema.Literal<"error">;
+ readonly errorMessage: Schema.String;
+}>]>, typeof TestRunnerFailed, never, never> | Rpc.Rpc<"mutantRun", Schema.Struct<{
+ options: Schema.Struct<{
+ readonly timeout: Schema.Finite;
+ readonly disableBail: Schema.Boolean;
+ readonly activeMutant: typeof Mutant;
+ readonly sandboxFileName: Schema.String;
+ readonly mutantActivation: Schema.Literals;
+ readonly reloadEnvironment: Schema.Boolean;
+ readonly testFilter: Schema.optionalKey>;
+ readonly hitLimit: Schema.optionalKey;
+ }>;
+}>, Schema.Union;
+ readonly killedBy: Schema.$Array;
+ readonly failureMessage: Schema.String;
+ readonly nrOfTests: Schema.Finite;
+}>, Schema.Struct<{
+ readonly status: Schema.Literal<"survived">;
+ readonly nrOfTests: Schema.Finite;
+}>, Schema.Struct<{
+ readonly status: Schema.Literal<"timeout">;
+ readonly reason: Schema.optionalKey;
+}>, Schema.Struct<{
+ readonly status: Schema.Literal<"error">;
+ readonly errorMessage: Schema.String;
+}>]>, typeof TestRunnerFailed, never, never>>;
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-engine/package.json b/packages/stryker-js-engine/package.json
index 0671c7488..716f624bf 100644
--- a/packages/stryker-js-engine/package.json
+++ b/packages/stryker-js-engine/package.json
@@ -7,7 +7,9 @@
"directory": "packages/stryker-js-engine"
},
"homepage": "https://github.com/systemfsoftware/stryker-js-effect",
- "bugs": "https://github.com/systemfsoftware/stryker-js-effect/issues",
+ "bugs": {
+ "url": "https://github.com/systemfsoftware/stryker-js-effect/issues"
+ },
"description": "The host-neutral mutation engine — the run, its stages, and the ports a process entry binds to start it.",
"type": "module",
"files": [
@@ -16,9 +18,11 @@
"scripts": {
"typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
"test": "vitest run",
- "build": "tsdown",
+ "build": "tsdown && pnpm api:check",
"lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint --format=${f:-default}",
- "attw": "attw --pack ."
+ "attw": "attw --pack .",
+ "api:check": "tsdown && concurrently --kill-others-on-fail \"api-extractor run\" \"api-extractor run --config api-extractor.builtin-reporters.json\" \"api-extractor run --config api-extractor.base.json\" \"api-extractor run --config api-extractor.plugin-loader.json\" \"api-extractor run --config api-extractor.worker.json\"",
+ "api:update": "concurrently --kill-others-on-fail \"api-extractor run --local\" \"api-extractor run --local --config api-extractor.builtin-reporters.json\" \"api-extractor run --local --config api-extractor.base.json\" \"api-extractor run --local --config api-extractor.plugin-loader.json\" \"api-extractor run --local --config api-extractor.worker.json\""
},
"dependencies": {
"@noble/hashes": "catalog:",
@@ -35,6 +39,7 @@
},
"devDependencies": {
"@effect/vitest": "catalog:",
+ "@microsoft/api-extractor": "catalog:",
"@std/jsonc": "catalog:",
"@systemfsoftware/all": "catalog:",
"@systemfsoftware/arethetypeswrong-cli": "catalog:",
@@ -43,6 +48,7 @@
"@systemfsoftware/vitest-config": "workspace:^",
"@types/diff-match-patch": "1.0.36",
"@types/node": "catalog:",
+ "concurrently": "catalog:",
"effect": "catalog:",
"oxlint": "catalog:",
"oxlint-tsgolint": "catalog:",
@@ -57,6 +63,7 @@
"exports": {
".": "./dist/index.mjs",
"./builtin-reporters": "./dist/builtin-reporters.mjs",
+ "./plugin-loader": "./dist/plugin-loader.mjs",
"./config/base": "./dist/config/base.mjs",
"./worker": "./dist/worker.mjs",
"./package.json": "./package.json"
@@ -66,6 +73,7 @@
".": "./dist/index.mjs",
"./builtin-reporters": "./dist/builtin-reporters.mjs",
"./config/base": "./dist/config/base.mjs",
+ "./plugin-loader": "./dist/plugin-loader.mjs",
"./worker": "./dist/worker.mjs",
"./package.json": "./package.json"
},
diff --git a/packages/stryker-js-engine/src/Plugins.schema.ts b/packages/stryker-js-engine/src/Plugins.schema.ts
index a3148f901..5b2a0fd9a 100644
--- a/packages/stryker-js-engine/src/Plugins.schema.ts
+++ b/packages/stryker-js-engine/src/Plugins.schema.ts
@@ -2,31 +2,25 @@
* Plugins capability — declarations for plugin module shapes and load failures.
*/
-import { PluginKind } from '@systemfsoftware/stryker-js-plugin-interface'
-import type { PluginContribution } from '@systemfsoftware/stryker-js-plugin-interface'
import { Schema as S } from 'effect'
-import * as SSchema from 'effect/Schema'
-
-const isPluginContribution = (_value: unknown): _value is PluginContribution => true
-const PluginContributionSchema = SSchema.Unknown.pipe(SSchema.refine(isPluginContribution))
-
-export class PluginLoaderEntry extends SSchema.Class('PluginLoaderEntry')({
- moduleName: SSchema.String,
- plugins: SSchema.optional(SSchema.Array(PluginContributionSchema)),
- schemaContribution: SSchema.optional(SSchema.Record(SSchema.String, SSchema.Unknown)),
-}) {}
-
-export class LoadPluginsCommand extends SSchema.Class('LoadPluginsCommand')({
- entries: SSchema.Array(PluginLoaderEntry),
-}) {}
export const PluginModuleSchema = S.Struct({
strykerPlugins: S.Array(S.Unknown),
})
+const isShouldIgnore = (value: unknown): value is (path: unknown) => string | undefined => typeof value === 'function'
+
+export const PlainIgnorerSchema = S.Struct({
+ name: S.String,
+ shouldIgnore: S.declare(isShouldIgnore),
+})
+
export const SchemaValidationContributionSchema = S.Struct({
strykerValidationSchema: S.Record(S.String, S.Unknown),
})
+export const PlainIgnorerModuleSchema = S.Struct({
+ strykerIgnorers: S.Array(PlainIgnorerSchema),
+})
export class PluginNotFoundError extends S.TaggedError()(
'PluginNotFoundError',
diff --git a/packages/stryker-js-engine/src/Plugins.ts b/packages/stryker-js-engine/src/Plugins.ts
index b8f30acf1..ae99b4901 100644
--- a/packages/stryker-js-engine/src/Plugins.ts
+++ b/packages/stryker-js-engine/src/Plugins.ts
@@ -3,22 +3,27 @@ import * as Effect from 'effect/Effect'
import * as FileSystem from 'effect/FileSystem'
import * as HashMap from 'effect/HashMap'
import * as HashSet from 'effect/HashSet'
+import * as Layer from 'effect/Layer'
import * as Match from 'effect/Match'
import * as Option from 'effect/Option'
import * as Path from 'effect/Path'
import * as Predicate from 'effect/Predicate'
+import * as Result from 'effect/Result'
import { Module } from '@systemfsoftware/stryker-js-language'
+import { Ignorer, type NodePath } from '@systemfsoftware/stryker-js-language'
import type {
AnyPluginContribution,
ContributionOf,
PluginContribution,
} from '@systemfsoftware/stryker-js-plugin-interface'
+import { declarePlugin } from '@systemfsoftware/stryker-js-plugin-interface'
import type { PluginKind } from '@systemfsoftware/stryker-js-plugin-interface'
import { defaultOptions, importModule } from './Config.js'
import { StrykerError } from './stryker-error.schema.js'
import {
+ PlainIgnorerModuleSchema,
PluginLoadFailedError,
PluginModuleSchema,
PluginNotFoundError,
@@ -173,6 +178,15 @@ interface PluginModule {
strykerPlugins: readonly PluginContribution[]
}
+interface PlainIgnorerEntry {
+ readonly name: string
+ shouldIgnore(path: NodePath): string | undefined
+}
+
+interface PlainIgnorerModule {
+ strykerIgnorers: readonly PlainIgnorerEntry[]
+}
+
interface SchemaValidationContribution {
strykerValidationSchema: Record
}
@@ -412,12 +426,32 @@ const recoverPluginImportFailure = (
Match.orElse(() => failPluginLoad(descriptor, error)),
)
+const plainIgnorerContribution = (ignorer: PlainIgnorerEntry): PluginContribution<'Ignore'> =>
+ declarePlugin(
+ 'Ignore',
+ ignorer.name,
+ Layer.succeed(Ignorer, {
+ shouldIgnore: (path) => Option.fromUndefinedOr(ignorer.shouldIgnore(path)),
+ }),
+ )
+
const modulePluginContributions = (module: unknown): readonly PluginContribution[] | undefined =>
Match.value(module).pipe(
Match.when(isPluginModule, (pluginModule: PluginModule) => pluginModule.strykerPlugins),
Match.orElse((): undefined => undefined),
)
+const modulePlainIgnorers = (
+ module: unknown,
+): Result.Result[] | undefined, S.SchemaError> =>
+ Match.value(Predicate.hasProperty(module, 'strykerIgnorers')).pipe(
+ Match.when(true, () =>
+ S.decodeUnknownResult(PlainIgnorerModuleSchema)(module).pipe(
+ Result.map((plain: PlainIgnorerModule) => plain.strykerIgnorers.map(plainIgnorerContribution)),
+ )),
+ Match.orElse(() => Result.succeed(undefined)),
+ )
+
const moduleSchemaContribution = (module: unknown): Record | undefined =>
Match.value(module).pipe(
Match.when(
@@ -427,10 +461,28 @@ const moduleSchemaContribution = (module: unknown): Record | un
Match.orElse((): undefined => undefined),
)
-const pluginContributionsOf = (module: unknown): PluginContributions => ({
- plugins: modulePluginContributions(module),
- schemaContribution: moduleSchemaContribution(module),
-})
+const mergeContributions = (
+ native: readonly PluginContribution[] | undefined,
+ plain: readonly PluginContribution<'Ignore'>[] | undefined,
+): readonly PluginContribution[] | undefined =>
+ Option.match(Option.fromUndefinedOr(native), {
+ onNone: () => plain,
+ onSome: (plugins) =>
+ Option.match(Option.fromUndefinedOr(plain), {
+ onNone: () => plugins,
+ onSome: (wrapped) => [...plugins, ...wrapped],
+ }),
+ })
+
+const pluginContributionsOf = (
+ module: unknown,
+): Result.Result =>
+ modulePlainIgnorers(module).pipe(
+ Result.map((plain) => ({
+ plugins: mergeContributions(modulePluginContributions(module), plain),
+ schemaContribution: moduleSchemaContribution(module),
+ })),
+ )
const hasContribution = (contributions: PluginContributions): boolean =>
Match.value(contributions.plugins !== undefined).pipe(
@@ -440,17 +492,21 @@ const hasContribution = (contributions: PluginContributions): boolean =>
const warnUndescribedPluginModule = (descriptor: string): Effect.Effect =>
Effect.logWarning(
- `Module "${descriptor}" did not contribute a StrykerJS plugin. It didn't export a "strykerPlugins" or "strykerValidationSchema".`,
+ `Module "${descriptor}" did not contribute a StrykerJS plugin. It didn't export a "strykerPlugins", "strykerIgnorers", or "strykerValidationSchema".`,
).pipe(Effect.as(undefined))
const describeLoadedPlugin = (
descriptor: string,
module: unknown,
-): Effect.Effect =>
- Match.value(pluginContributionsOf(module)).pipe(
- Match.when(hasContribution, (contributions: PluginContributions) => Effect.succeed(contributions)),
- Match.orElse(() => warnUndescribedPluginModule(descriptor)),
- )
+): Effect.Effect =>
+ Result.match(pluginContributionsOf(module), {
+ onFailure: (cause) => failPluginLoad(descriptor, cause),
+ onSuccess: (contributions) =>
+ Match.value(hasContribution(contributions)).pipe(
+ Match.when(true, () => Effect.succeed(contributions)),
+ Match.orElse(() => warnUndescribedPluginModule(descriptor)),
+ ),
+ })
function loadPlugin(
descriptor: string,
diff --git a/packages/stryker-js-engine/src/config/base.ts b/packages/stryker-js-engine/src/config/base.ts
index 0b23c6be6..667784c61 100644
--- a/packages/stryker-js-engine/src/config/base.ts
+++ b/packages/stryker-js-engine/src/config/base.ts
@@ -17,8 +17,7 @@ const basePreset: BasePreset = {
plugins: [
'@systemfsoftware/stryker-js-vitest-runner',
'@systemfsoftware/stryker-js-typescript-checker',
- '@systemfsoftware/stryker-plugins/effect-schema-ignorer',
- '@systemfsoftware/stryker-plugins/workflow-make-ignorer',
+ '@systemfsoftware/stryker-ignorer-effect-schema-declarations',
'@systemfsoftware/stryker-test-contribution',
],
reporters: ['progress', 'html', 'json', 'progress-stream'],
diff --git a/packages/stryker-js-engine/src/mutation-reporting.ts b/packages/stryker-js-engine/src/mutation-reporting.ts
index e7d7467e4..b1672ece5 100644
--- a/packages/stryker-js-engine/src/mutation-reporting.ts
+++ b/packages/stryker-js-engine/src/mutation-reporting.ts
@@ -200,7 +200,7 @@ export const makeMutationReportingService = (input: MakeMutationReportingInput):
const MANIFEST_SPECIFIERS = [
'@systemfsoftware/stryker-js-vitest-runner',
'@systemfsoftware/stryker-js-typescript-checker',
- '@systemfsoftware/stryker-plugins',
+ '@systemfsoftware/stryker-ignorer-effect-schema-declarations',
'vitest',
'karma',
'karma-chai',
diff --git a/packages/stryker-js-engine/src/plugin-loader.ts b/packages/stryker-js-engine/src/plugin-loader.ts
new file mode 100644
index 000000000..fa04a09f5
--- /dev/null
+++ b/packages/stryker-js-engine/src/plugin-loader.ts
@@ -0,0 +1,3 @@
+export { create, createAll, loadPlugins } from './Plugins.js'
+export type { LoadedPlugins, PluginLoaderEntryLike, PluginLoadPlan } from './Plugins.js'
+export { PluginLoadFailedError, PluginNotFoundError } from './Plugins.schema.js'
diff --git a/packages/stryker-js-engine/tests/__fixtures__/both-protocols.fixture.mjs b/packages/stryker-js-engine/tests/__fixtures__/both-protocols.fixture.mjs
new file mode 100644
index 000000000..dcfc98a80
--- /dev/null
+++ b/packages/stryker-js-engine/tests/__fixtures__/both-protocols.fixture.mjs
@@ -0,0 +1,7 @@
+import { declarePlugin } from '@systemfsoftware/stryker-js-plugin-interface'
+
+export const strykerPlugins = [declarePlugin('Reporter', 'native-fixture-reporter', () => ({}))]
+
+export const strykerIgnorers = [
+ { name: 'plain-fixture-rule', shouldIgnore: () => 'plain reason' },
+]
diff --git a/packages/stryker-js-engine/tests/__fixtures__/invalid-plain-entry.fixture.mjs b/packages/stryker-js-engine/tests/__fixtures__/invalid-plain-entry.fixture.mjs
new file mode 100644
index 000000000..52728684c
--- /dev/null
+++ b/packages/stryker-js-engine/tests/__fixtures__/invalid-plain-entry.fixture.mjs
@@ -0,0 +1 @@
+export const strykerIgnorers = [{ shouldIgnore: 'not a function' }]
diff --git a/packages/stryker-js-engine/tests/__fixtures__/plain-ignorer-only.fixture.mjs b/packages/stryker-js-engine/tests/__fixtures__/plain-ignorer-only.fixture.mjs
new file mode 100644
index 000000000..d2f932aae
--- /dev/null
+++ b/packages/stryker-js-engine/tests/__fixtures__/plain-ignorer-only.fixture.mjs
@@ -0,0 +1,4 @@
+export const strykerIgnorers = [
+ { name: 'plain-fixture-rule', shouldIgnore: () => 'fixture reason' },
+ { name: 'plain-fixture-never', shouldIgnore: () => undefined },
+]
diff --git a/packages/stryker-js-engine/tests/__fixtures__/plain-ignorer-shadowed.fixture.mjs b/packages/stryker-js-engine/tests/__fixtures__/plain-ignorer-shadowed.fixture.mjs
new file mode 100644
index 000000000..4430726d6
--- /dev/null
+++ b/packages/stryker-js-engine/tests/__fixtures__/plain-ignorer-shadowed.fixture.mjs
@@ -0,0 +1,4 @@
+export const strykerIgnorers = [
+ { name: 'duplicated-rule', shouldIgnore: () => 'first' },
+ { name: 'duplicated-rule', shouldIgnore: () => 'last' },
+]
diff --git a/packages/stryker-js-engine/tests/plain-ignorer-loader.integration.test.ts b/packages/stryker-js-engine/tests/plain-ignorer-loader.integration.test.ts
new file mode 100644
index 000000000..2b4b5ed86
--- /dev/null
+++ b/packages/stryker-js-engine/tests/plain-ignorer-loader.integration.test.ts
@@ -0,0 +1,152 @@
+import { Gherkin, Given, it, layer, makeFeature, Then, When } from '@systemfsoftware/effect-gherkin-spec'
+import { createDefaultOptions } from '@systemfsoftware/stryker-js-engine'
+import { create, createAll, loadPlugins, PluginLoadFailedError } from '@systemfsoftware/stryker-js-engine/plugin-loader'
+import { Ignorer, Module, type NodePath } from '@systemfsoftware/stryker-js-language'
+import type { ModuleRequire } from '@systemfsoftware/stryker-js-language'
+import { RunConfiguration, SandboxDirectory } from '@systemfsoftware/stryker-js-plugin-interface'
+import * as Context from 'effect/Context'
+import * as Effect from 'effect/Effect'
+import * as FileSystem from 'effect/FileSystem'
+import * as Layer from 'effect/Layer'
+import * as Option from 'effect/Option'
+import * as Path from 'effect/Path'
+import { expect } from 'vitest'
+
+const Feature = makeFeature({ it, layer })
+
+interface NodeModuleShape {
+ createRequire(filename: string | URL): NodeRequire
+ isBuiltin(moduleName: string): boolean
+}
+
+const EMPTY_PATHS: readonly string[] = []
+
+const makeModuleRequire = (nodeModule: NodeModuleShape, filename: string | URL): ModuleRequire => {
+ const requireFrom: NodeRequire = nodeModule.createRequire(filename)
+ const requireFn: ModuleRequire = (request: string): unknown => requireFrom(request)
+ requireFn.resolve = (request, options) =>
+ Option.match(Option.fromUndefinedOr(options), {
+ onNone: () => requireFrom.resolve(request),
+ onSome: (present) =>
+ requireFrom.resolve(request, {
+ paths: [...Option.getOrElse(Option.fromNullishOr(present.paths), () => EMPTY_PATHS)],
+ }),
+ })
+ return requireFn
+}
+
+const moduleLayer = Layer.effect(
+ Module,
+ Effect.sync(() => {
+ const nodeModule: NodeModuleShape = process.getBuiltinModule('node:module')
+ return {
+ createRequire: (filename: string | URL) => makeModuleRequire(nodeModule, filename),
+ isBuiltin: (moduleName: string) => nodeModule.isBuiltin(moduleName),
+ }
+ }),
+)
+
+const pluginEnvironmentLayer = Layer.mergeAll(
+ FileSystem.layerNoop({}),
+ Path.layer,
+ moduleLayer,
+ Layer.succeed(RunConfiguration, Effect.runSync(createDefaultOptions())),
+ Layer.succeed(SandboxDirectory, '/tmp'),
+)
+
+const fixturePath = (name: string): string => `${process.cwd()}/tests/__fixtures__/${name}`
+
+const loadFixture = (name: string) =>
+ loadPlugins([fixturePath(name)], process.cwd()).pipe(
+ Effect.provide(Layer.mergeAll(FileSystem.layerNoop({}), Path.layer, moduleLayer)),
+ )
+
+const pathOf = (node: unknown): NodePath => ({ node, ancestors: [] })
+
+Feature('Loading plain ignorer plugins')
+ .body(({ scenario }) => {
+ scenario(
+ 'A module exporting only plain ignorers loads every entry as an ignore contribution',
+ Gherkin.Do.pipe(
+ Given('a module exporting one plain ignorer and nothing else')(
+ 'loaded',
+ () => loadFixture('plain-ignorer-only.fixture.mjs'),
+ ),
+ When('each entry is selected by kind and name and its layer is built')(
+ 'services',
+ (s) =>
+ Effect.gen(function*() {
+ const rule = yield* create(s.loaded.pluginsByKind, 'Ignore', 'plain-fixture-rule')
+ const never = yield* create(s.loaded.pluginsByKind, 'Ignore', 'plain-fixture-never')
+ const ruleContext = yield* Layer.build(rule.layer).pipe(Effect.provide(pluginEnvironmentLayer))
+ const neverContext = yield* Layer.build(never.layer).pipe(Effect.provide(pluginEnvironmentLayer))
+ return {
+ rule: Context.get(ruleContext, Ignorer),
+ never: Context.get(neverContext, Ignorer),
+ }
+ }),
+ ),
+ Then('every entry registers under its name and its decisions flow through')((s) =>
+ Effect.sync(() => {
+ expect(Option.getOrThrow(s.services.rule.shouldIgnore(pathOf('any-node')))).toBe('fixture reason')
+ expect(Option.isNone(s.services.never.shouldIgnore(pathOf('any-node')))).toBe(true)
+ })
+ ),
+ ),
+ )
+
+ scenario(
+ 'A module exporting both protocols keeps both kinds',
+ Gherkin.Do.pipe(
+ Given('a module exporting a native reporter and a plain ignorer')(
+ 'loaded',
+ () => loadFixture('both-protocols.fixture.mjs'),
+ ),
+ When('the loaded contributions are read back by kind')('names', (s) =>
+ Effect.sync(() => ({
+ reporters: createAll(s.loaded.pluginsByKind, 'Reporter').pipe(Effect.runSync).map((c) => c.name),
+ ignorers: createAll(s.loaded.pluginsByKind, 'Ignore').pipe(Effect.runSync).map((c) => c.name),
+ }))),
+ Then('both kinds are present')((s) =>
+ Effect.sync(() => {
+ expect(s.names.reporters).toStrictEqual(['native-fixture-reporter'])
+ expect(s.names.ignorers).toStrictEqual(['plain-fixture-rule'])
+ })
+ ),
+ ),
+ )
+
+ scenario(
+ 'A malformed plain entry fails the load with a named error',
+ Gherkin.Do.pipe(
+ Given('a module whose plain entry has no name and no callable decision')(
+ 'outcome',
+ () => loadFixture('invalid-plain-entry.fixture.mjs').pipe(Effect.flip),
+ ),
+ Then('the load is rejected naming the module')((s) =>
+ Effect.sync(() => {
+ expect(s.outcome).toBeInstanceOf(PluginLoadFailedError)
+ expect(s.outcome.descriptor).toContain('invalid-plain-entry.fixture.mjs')
+ })
+ ),
+ ),
+ )
+
+ scenario(
+ 'Two plain entries sharing a name both load under that name',
+ Gherkin.Do.pipe(
+ Given('a module exporting two plain entries with the same name')(
+ 'names',
+ () =>
+ loadFixture('plain-ignorer-shadowed.fixture.mjs').pipe(
+ Effect.map((loaded) => createAll(loaded.pluginsByKind, 'Ignore').pipe(Effect.runSync).map((c) => c.name)),
+ ),
+ ),
+ Then('both entries appear under the shared name')((s) =>
+ Effect.sync(() => {
+ expect(s.names).toStrictEqual(['duplicated-rule', 'duplicated-rule'])
+ })
+ ),
+ ),
+ )
+ })
diff --git a/packages/stryker-js-engine/tsconfig.api-base.json b/packages/stryker-js-engine/tsconfig.api-base.json
new file mode 100644
index 000000000..947ecd4d8
--- /dev/null
+++ b/packages/stryker-js-engine/tsconfig.api-base.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@systemfsoftware/tsconfig/tsc/no-dom/library",
+ "include": [
+ "dist/config/base.d.mts"
+ ]
+}
diff --git a/packages/stryker-js-engine/tsconfig.api-builtin-reporters.json b/packages/stryker-js-engine/tsconfig.api-builtin-reporters.json
new file mode 100644
index 000000000..f1ec2ad7e
--- /dev/null
+++ b/packages/stryker-js-engine/tsconfig.api-builtin-reporters.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@systemfsoftware/tsconfig/tsc/no-dom/library",
+ "include": [
+ "dist/builtin-reporters.d.mts"
+ ]
+}
diff --git a/packages/stryker-js-engine/tsconfig.api-plugin-loader.json b/packages/stryker-js-engine/tsconfig.api-plugin-loader.json
new file mode 100644
index 000000000..4c8e1dd98
--- /dev/null
+++ b/packages/stryker-js-engine/tsconfig.api-plugin-loader.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@systemfsoftware/tsconfig/tsc/no-dom/library",
+ "include": [
+ "dist/plugin-loader.d.mts"
+ ]
+}
diff --git a/packages/stryker-js-engine/tsconfig.api-worker.json b/packages/stryker-js-engine/tsconfig.api-worker.json
new file mode 100644
index 000000000..53b041b7d
--- /dev/null
+++ b/packages/stryker-js-engine/tsconfig.api-worker.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@systemfsoftware/tsconfig/tsc/no-dom/library",
+ "include": [
+ "dist/worker.d.mts"
+ ]
+}
diff --git a/packages/stryker-js-engine/tsdown.config.ts b/packages/stryker-js-engine/tsdown.config.ts
index 3d75ef6b9..1e229cdb1 100644
--- a/packages/stryker-js-engine/tsdown.config.ts
+++ b/packages/stryker-js-engine/tsdown.config.ts
@@ -5,6 +5,7 @@ export default defineConfig({
index: './src/index.ts',
'config/base': './src/config/base.ts',
'builtin-reporters': './src/builtin-reporters.ts',
+ 'plugin-loader': './src/plugin-loader.ts',
worker: './src/worker-wiring.ts',
},
format: 'esm',
diff --git a/packages/stryker-js-html-reporter/api-extractor.json b/packages/stryker-js-html-reporter/api-extractor.json
new file mode 100644
index 000000000..409ab944c
--- /dev/null
+++ b/packages/stryker-js-html-reporter/api-extractor.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/index.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/main/",
+ "reportFileName": "stryker-js-html-reporter.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/index.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-html-reporter/etc/stryker-js-html-reporter.api.md b/packages/stryker-js-html-reporter/etc/stryker-js-html-reporter.api.md
new file mode 100644
index 000000000..20071b071
--- /dev/null
+++ b/packages/stryker-js-html-reporter/etc/stryker-js-html-reporter.api.md
@@ -0,0 +1,21 @@
+## API Report File for "@systemfsoftware/stryker-js-html-reporter"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import { PluginReporterContribution } from '@systemfsoftware/stryker-js-plugin-interface';
+import { ReporterFactory } from '@systemfsoftware/stryker-js-language';
+
+// @public (undocumented)
+export const htmlReporterFactory: ReporterFactory;
+
+// @public (undocumented)
+export const makeHtmlReporter: ReporterFactory;
+
+// @public (undocumented)
+export const strykerPlugins: PluginReporterContribution[];
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-html-reporter/package.json b/packages/stryker-js-html-reporter/package.json
index f5a3f4f99..5a918a43e 100644
--- a/packages/stryker-js-html-reporter/package.json
+++ b/packages/stryker-js-html-reporter/package.json
@@ -15,11 +15,13 @@
],
"scripts": {
"clean": "rimraf dist",
- "build": "tsdown",
+ "build": "tsdown && pnpm api:check",
"typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
"attw": "attw --pack .",
"test": "vitest run",
- "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint --format=${f:-default}"
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint --format=${f:-default}",
+ "api:check": "tsdown && api-extractor run",
+ "api:update": "api-extractor run --local"
},
"dependencies": {
"@effect/platform-node-shared": "catalog:",
@@ -29,6 +31,7 @@
"mutation-testing-elements": "catalog:stryker"
},
"devDependencies": {
+ "@microsoft/api-extractor": "catalog:",
"@systemfsoftware/all": "catalog:",
"@systemfsoftware/arethetypeswrong-cli": "catalog:",
"@systemfsoftware/effect-gherkin-spec": "catalog:",
diff --git a/packages/stryker-js-instrumenter/api-extractor.json b/packages/stryker-js-instrumenter/api-extractor.json
new file mode 100644
index 000000000..a7336aeca
--- /dev/null
+++ b/packages/stryker-js-instrumenter/api-extractor.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
+ "projectFolder": ".",
+ "mainEntryPointFilePath": "/dist/index.d.mts",
+ "bundledPackages": [],
+ "compiler": {
+ "tsconfigFilePath": "/tsconfig.json"
+ },
+ "apiReport": {
+ "enabled": true,
+ "reportFolder": "/etc/",
+ "reportTempFolder": "/temp/main/",
+ "reportFileName": "stryker-js-instrumenter.api.md"
+ },
+ "docModel": {
+ "enabled": false
+ },
+ "dtsRollup": {
+ "enabled": true,
+ "untrimmedFilePath": "/dist/index.d.mts"
+ },
+ "tsdocMetadata": {
+ "enabled": false
+ },
+ "messages": {
+ "extractorMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "ae-forgotten-export": {
+ "logLevel": "error"
+ },
+ "ae-missing-release-tag": {
+ "logLevel": "none"
+ },
+ "ae-internal-missing-underscore": {
+ "logLevel": "none"
+ }
+ },
+ "tsdocMessageReporting": {
+ "default": {
+ "logLevel": "warning"
+ },
+ "tsdoc-undefined-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-unsupported-tag": {
+ "logLevel": "none"
+ },
+ "tsdoc-param-tag-missing-hyphen": {
+ "logLevel": "none"
+ }
+ }
+ }
+}
diff --git a/packages/stryker-js-instrumenter/etc/stryker-js-instrumenter.api.md b/packages/stryker-js-instrumenter/etc/stryker-js-instrumenter.api.md
new file mode 100644
index 000000000..10125ad81
--- /dev/null
+++ b/packages/stryker-js-instrumenter/etc/stryker-js-instrumenter.api.md
@@ -0,0 +1,59 @@
+## API Report File for "@systemfsoftware/stryker-js-instrumenter"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+
+import * as Effect from 'effect/Effect';
+import { FileDescription } from '@systemfsoftware/stryker-js-language';
+import { IgnorerService } from '@systemfsoftware/stryker-js-language';
+import { Mutant } from '@systemfsoftware/stryker-js-language';
+import * as S from 'effect/Schema';
+import { YieldableError } from 'effect/Cause';
+
+// @public (undocumented)
+export const angularIgnorer: IgnorerService;
+
+// @public (undocumented)
+export function disableTypeChecks(file: File_2): Promise;
+
+// @public (undocumented)
+interface File_2 extends FileDescription {
+ // (undocumented)
+ content: string;
+ // (undocumented)
+ name: string;
+}
+export { File_2 as File }
+
+// @public (undocumented)
+export const frameworkPluginsFileUrl: string;
+
+// Warning: (ae-forgotten-export) The symbol "InstrumentResult$1" needs to be exported by the entry point index.d.mts
+// Warning: (ae-forgotten-export) The symbol "InstrumentError" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export const instrument: (files: readonly File_2[], options: InstrumenterOptions) => Effect.Effect;
+
+// Warning: (ae-forgotten-export) The symbol "InstrumenterOptionsSchema" needs to be exported by the entry point index.d.mts
+//
+// @public (undocumented)
+export type InstrumenterOptions = typeof InstrumenterOptionsSchema.Type;
+
+// @public (undocumented)
+export interface InstrumentResult {
+ // (undocumented)
+ files: readonly File_2[];
+ // (undocumented)
+ mutants: readonly Mutant[];
+}
+
+// @public (undocumented)
+export interface ParserOptions {}
+
+// @public (undocumented)
+export const strykerPlugins: readonly unknown[];
+
+// (No @packageDocumentation comment for this package)
+
+```
diff --git a/packages/stryker-js-instrumenter/package.json b/packages/stryker-js-instrumenter/package.json
index e504005f0..2896d38c2 100644
--- a/packages/stryker-js-instrumenter/package.json
+++ b/packages/stryker-js-instrumenter/package.json
@@ -19,11 +19,13 @@
},
"scripts": {
"clean": "rimraf dist",
- "build": "tsdown",
+ "build": "tsdown && pnpm api:check",
"typecheck": "tsc --noEmit --incremental && tsc -p tsconfig.node.json --noEmit",
"attw": "attw --pack .",
"test": "vitest run",
- "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}"
+ "lint": "f=${OXLINT_FORMAT:-${AGENT:+agent}}; oxlint . --format=${f:-default}",
+ "api:check": "tsdown && api-extractor run",
+ "api:update": "api-extractor run --local"
},
"license": "Apache-2.0",
"dependencies": {
@@ -33,21 +35,22 @@
"@systemfsoftware/stryker-js-language": "workspace:^",
"angular-html-parser": "~10.4.0",
"effect": "catalog:",
- "oxc-parser": "catalog:"
+ "oxc-parser": "catalog:",
+ "oxc-walker": "catalog:"
},
"devDependencies": {
+ "@microsoft/api-extractor": "catalog:",
"@systemfsoftware/all": "catalog:",
"@systemfsoftware/arethetypeswrong-cli": "catalog:",
"@systemfsoftware/effect-gherkin-spec": "catalog:",
"@systemfsoftware/tsconfig": "catalog:",
"@systemfsoftware/vitest-config": "workspace:^",
- "@types/estree": "1.0.8",
"@types/node": "catalog:",
- "estree-walker": "3.0.3",
"oxlint": "catalog:",
"rimraf": "catalog:",
"svelte": "5.55.1",
"tsdown": "catalog:",
+ "type-fest": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
@@ -68,5 +71,8 @@
"svelte": {
"optional": true
}
+ },
+ "inlinedDependencies": {
+ "type-fest": "4.41.0"
}
}
diff --git a/packages/stryker-js-instrumenter/src/Ast.ts b/packages/stryker-js-instrumenter/src/Ast.ts
new file mode 100644
index 000000000..2a681eba5
--- /dev/null
+++ b/packages/stryker-js-instrumenter/src/Ast.ts
@@ -0,0 +1,708 @@
+// oxlint-disable typescript/no-unsafe-type-assertion typescript/no-unnecessary-type-assertion
+import type * as Oxc from '@oxc-project/types'
+import * as Arr from 'effect/Array'
+import * as Match from 'effect/Match'
+import * as Option from 'effect/Option'
+import * as Predicate from 'effect/Predicate'
+import { walk, type WalkerCallbackContext, type WalkerThisContextEnter } from 'oxc-walker'
+import type { Simplify } from 'type-fest'
+
+import { computeLineStarts, positionFromOffset } from './Syntax.js'
+import { TraversalStopped } from './Traversal.schema.js'
+
+type Built = T extends null | undefined ? T
+ : T extends readonly unknown[] ? (number extends T['length'] ? Array> : T)
+ : T extends Oxc.Span ?
+ & {
+ [K in keyof T as K extends keyof Oxc.Span ? never : K]: Child
+ }
+ & Partial>
+ : T
+
+type Child = T extends null | undefined ? T
+ : T extends readonly unknown[] ? (number extends T['length'] ? Array> : T)
+ : T extends Oxc.Span ? Built | T
+ : T
+
+export type Program = Simplify>
+export type Expression = Simplify> | Oxc.Expression
+export type Statement = Simplify> | Oxc.Statement
+export type Node = Simplify> | Oxc.Node
+
+export type AccessorProperty = Simplify>
+export type Argument = Simplify>
+export type ArrayExpression = Simplify>
+export type ArrayPattern = Simplify>
+export type ArrowFunctionExpression = Simplify>
+export type AssignmentExpression = Simplify>
+export type AssignmentPattern = Simplify>
+export type BigIntLiteral = Simplify>
+export type BinaryExpression = Simplify>
+export type BindingIdentifier = Simplify>
+export type BindingPattern = Simplify>
+export type BindingProperty = Simplify>
+export type BindingRestElement = Simplify