vitest-auto-spy 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/AGENTS.md +27 -22
  2. package/README.md +54 -72
  3. package/package.json +11 -2
package/AGENTS.md CHANGED
@@ -261,20 +261,23 @@ xhr.send.mockImplementation(() => respond(asInstance(xhr)));
261
261
 
262
262
  ### Cost, so it stops being a question
263
263
 
264
- Building a spy is not a thing to optimise. On a ten-method class, Node 24, `npm run bench`:
265
- `createSpyFromClass` plus two methods called ~2.3 µs, plus all ten ~6 µs, `createAutoMock` plus four
266
- members ~1.7 µs, a `calledWith` lookup ~0.2 µs — five providers across two thousand tests is a
267
- hundredth of a second. Call the factory in `beforeEach` and look at
268
- `TestBed` instead. The only two settings that cost: `{ lazySpies: false }` gives up the laziness
269
- `provideAutoSpy` defaults to, and `autoSpyAccessors: true` walks the prototype chain uncached on
270
- every call — name the accessors instead.
264
+ Building a spy is not a thing to optimise. On a ten-method class, `npm run bench` on Node v24.19.0
265
+ and Vitest 4.1.11, measured 2026-09-04: `createSpyFromClass` plus two methods called **2.25 µs**,
266
+ plus all ten 5.83 µs, `createAutoMock` plus four members 1.79 µs, a `calledWith` lookup 0.21 µs —
267
+ five providers across two thousand tests is about two hundredths of a second. Call the factory in
268
+ `beforeEach` and look at `TestBed` instead. The only two settings that cost:
269
+ `{ lazySpies: false }` gives up the laziness `provideAutoSpy` defaults to, and
270
+ `autoSpyAccessors: true` walks the prototype chain uncached on every call — name the accessors
271
+ instead.
271
272
 
272
273
  Memory is the exception, and only on classes wide enough for it to matter. `lazySpies: true` still
273
274
  defines one accessor per method, and that placeholder is nearly all of what an untouched double
274
- retains: 101.6 kB on a 400-method class against 11.8 kB with `lazySpies: 'proxy'`, which answers
275
- every method from a single trap object instead (25 B per method against 253 B). It is opt-in because
276
- a `Proxy` cannot remove itself — +30 ns per read and +43 ns per call, forever — and it loses on a
277
- five-method service. Reach for it on generated API clients and ngrx facades under `isolate: false`,
275
+ retains: an untouched 100-method double holds 25 601 B against 4 097 B with `lazySpies: 'proxy'`,
276
+ which answers every method from a single trap object instead — 256 B per method against 41 B
277
+ (`npm run bench:memory`, 2026-09-04). It is opt-in because a `Proxy` cannot remove itself: the
278
+ default leaves a plain data property behind once a method materialises, while the proxy goes through
279
+ a trap on every read and every call for the life of the double, and on a narrow class it is also
280
+ slower to create. Reach for it on generated API clients and ngrx facades under `isolate: false`,
278
281
  where the doubles of a whole file are alive at once; leave the default everywhere else.
279
282
 
280
283
  ---
@@ -2101,22 +2104,24 @@ thing to check by hand after the swap: Suites' double answers every property nam
2101
2104
  stubbed a since-renamed method was passing over nothing and will now fail. Full mapping:
2102
2105
  <https://asdalexey.github.io/vitest-auto-spy/migrating-suites>.
2103
2106
 
2104
- - **`renderShallow` has no single speed number, and quoting one is a bug.** Per render it ranges
2105
- from 1.2× (no children) to 16.2× (400 child instances) — `docs-site/core/performance.md`,
2106
- measured 2026-08-26. Per spec file, where imports and the `TestBed` module are also on the clock,
2107
- the published conversion is 1.7× over three specs with one of them a 0.8× regression. The two are
2108
- not interchangeable: the per-render ratio is the upper bound on what a file can gain. Any surface
2109
- that mentions the helper's speed quotes the range and links to
2110
- `/core/performance#_2-rendering-the-child-subtree`. A figure of **4.1×** appears in
2111
- `PRIORITIES.md` and is backed by nothing in this repository — do not publish it until someone
2112
- produces the measurement.
2107
+ - **`renderShallow` has no single speed number, and quoting one is a bug.** This repository commits
2108
+ no harness for the helper, so no surface publishes a ratio for it and none may be invented.
2109
+ Describe the shape instead: `renderShallow` is flat in the number of children because it never
2110
+ builds the subtree, while `TestBed.createComponent` scales linearly with it — so the win is
2111
+ however much markup the component under test happens to own, nothing on a leaf component and a
2112
+ great deal on a table or a dashboard. A per-render ratio would in any case be the upper bound on
2113
+ what a spec file can gain rather than a prediction of it, because a file also pays for imports,
2114
+ the `TestBed` module and the assertions. Link to
2115
+ `/core/performance#_2-rendering-the-child-subtree`. A speed figure for the helper also sits in
2116
+ `PRIORITIES.md`, backed by nothing in this repository — do not publish it until someone produces
2117
+ the measurement.
2113
2118
  - **Competitor defects are quoted from the published tarball, with a file, a line and a date.** The
2114
2119
  `@testing-library/angular` `createMock` claims in `docs-site/comparison.md` are pinned to
2115
2120
  `fesm2022/testing-library-angular-vitest-utils.mjs` lines 14 and 18 at 19.4.2. Re-`npm pack` and
2116
2121
  re-read before restating them against a new version; the "Where the numbers come from" box records
2117
2122
  each re-verification date rather than replacing the previous one.
2118
2123
  - **Head-to-head benchmark numbers are quoted from the measurement, not from memory.** `npm run
2119
- bench:vs` and `npm run bench:suite` (see `CONTRIBUTING.md` for setup) back the claims in
2124
+ bench:vs` and `npm run bench:suite` (see `CONTRIBUTING.md` for setup) back the claims in
2120
2125
  `core/performance.md`; point at that page rather than retyping a figure here, so it lives in one
2121
2126
  place. Micro-benchmark ratios do not transfer to suite scale: double construction is on the order
2122
2127
  of 1% of a test's cost, and an advantage that looks like several times faster per double can be
@@ -2586,7 +2591,7 @@ totals are CPU time summed across every worker, not wall clock** — a report sh
2586
2591
  time` under `986ms wall clock` is not a bug, it is the work spread over workers.
2587
2592
 
2588
2593
  When `environment` dominates, the `perf-environment` finding names spec files that could run under
2589
- the `node` environment instead — but only the ones it could *prove* reach no DOM: the spec, its
2594
+ the `node` environment instead — but only the ones it could _prove_ reach no DOM: the spec, its
2590
2595
  configured setup files, and every repository module any of them imports were all read, none
2591
2596
  mentions a DOM name, and every package they import is on a short allowlist (`vitest`, `rxjs`,
2592
2597
  `date-fns`, `lodash`, `zod`, …). Anything it could not resolve is reported **undecided**, never
package/README.md CHANGED
@@ -55,7 +55,7 @@ faster at suite scale ([benchmarks](#benchmarks)) — and for
55
55
  - 📡 First-class RxJS `Observable` spying (`nextWith`, `nextWithValues`, `throwWith`, …)
56
56
  - ⚙️ Getter / setter spies via `accessorSpies` — **including on Bun**, where `bun:test`'s own `spyOn(obj, 'prop', 'get')` throws _"does not support accessor properties yet"_ (verified on Bun 1.4.0); no library that generates a double from a class or a type has accessor spies on any runner
57
57
  - 🧰 DI & mocking utilities — `provideAutoSpy` / `injectSpy` (Angular, NestJS, Vue), `createFunctionSpy`, `mockReadonlyProp` for signals
58
- - ⚡ Angular speed & zoneless helpers — `renderShallow` (**1.7×** across three converted specs of a real suite; per render, **1.2×** on a childless leaf up to **16.2×** on a 400-child tree — [both measurements below](#shallow-component-rendering)), `createWithAutoSpies`, `stable` / `flushEffects`, `settleResource` for `httpResource()`, `toHaveSignalValue`, per-file `TestBed` timings
58
+ - ⚡ Angular speed & zoneless helpers — [`renderShallow`](#shallow-component-rendering), which brings a component up through the real `TestBed` without its child subtree, `createWithAutoSpies`, `stable` / `flushEffects`, `settleResource` for `httpResource()`, `toHaveSignalValue`, per-file `TestBed` timings
59
59
  - **`node:test` stops retaining every spy.** Node's built-in runner keeps every `mock.fn()` in one
60
60
  process-wide `MockTracker` and has no way to drop a single entry, so a long suite accumulates every
61
61
  spy it ever made along with every argument they recorded. `trackNodeMocks()` gives this library a
@@ -223,7 +223,7 @@ all**, because Vite 7 calls `crypto.hash` (added in Node 20.12) and the run dies
223
223
  `TypeError: crypto.hash is not a function` before a spec loads. On Vitest ≤ 3 Node 18 is fine. Which
224
224
  version to actually run is measured in
225
225
  [Performance → Which Node version](https://vitest-auto-spy.dev/core/performance#which-node-version):
226
- the core is 9–15% faster from Node 24 on, and a cold import more than halves.
226
+ the break is between 22 and 24, where a cold import more than halves.
227
227
 
228
228
  Ships **ESM with bundled `.d.ts` types**. Two subpaths additionally ship a CommonJS build —
229
229
  `vitest-auto-spy/node` (a `node --test` suite written in CJS) and `vitest-auto-spy/eslint-plugin`
@@ -1228,16 +1228,19 @@ runner's own mocks, so those entries keep building spies from `mock()` and `t.mo
1228
1228
 
1229
1229
  ## Benchmarks
1230
1230
 
1231
- Wall-clock at suite scale varies **15-20% between invocations** of the identical configuration —
1232
- the same cell measured 1.56 s and 1.33 s at 1 000 tests, 10.94 s and 12.51 s at 10 000. Peak RSS, by
1233
- contrast, reproduces tightly and separates arms by multiples. Treat the ratios below as the durable
1234
- figure and any single wall-clock number as describing one machine, not a promise about yours.
1231
+ Wall-clock at suite scale is the noisiest thing here, and the noise does not disappear inside a
1232
+ single invocation: the suite harness prints its own widest round-to-round spread, and in the run
1233
+ below that was **16%** of the cell's median — the three rounds of the 1 000-test cell came out at
1234
+ 1.35 / 1.33 / 1.32 s and the 10 000-test cell at 10.02 / 10.65 / 9.72 s. Peak RSS, by contrast,
1235
+ reproduces tightly and separates arms by multiples. Treat the ratios below as the durable figure and
1236
+ any single wall-clock number as describing one machine, not a promise about yours.
1235
1237
 
1236
1238
  **The micro-benchmark below has the same problem, measured directly.** Every micro figure is the
1237
1239
  **median p75 of seven independent runs**, each its own process, at doubled iteration budgets, run
1238
1240
  with `npm run bench:vs:precise` — not a single run. What each row's ± column reports is how far that
1239
- published median can be off: across the 47 rows of the canonical run, a median of **±1.1%** and at
1240
- worst **±3.8%**. The blunter rule still holds for reading a single local run — **a difference under
1241
+ published median can be off: across the 47 rows of the 2026-09-04 run, a median of **±0.9%** and at
1242
+ worst **±6.3%** — on the `calledWith` dispatch row, the fastest and therefore the one closest to the
1243
+ stand's own floor. The blunter rule still holds for reading a single local run — **a difference under
1241
1244
  about 20% is not worth quoting** — and the narrowest margin below is 2.19×.
1242
1245
 
1243
1246
  `jest-auto-spies@3.0.1`, `jasmine-auto-spies@8.0.1` and `@bugsplat/vitest-auto-spies@1.0.0` are all
@@ -1284,18 +1287,18 @@ of the lead is that this library no longer pays the runner's per-mock cost while
1284
1287
  still does — a difference in the product rather than in the measurement, and the `hand-written
1285
1288
  vi.fn() per method` arm is in the table precisely so its size stays visible.
1286
1289
 
1287
- **Where it still loses, same weight, same table** (`npm run bench:suite`, re-measured 2026-09-03 on
1288
- the 4.1 build, 20-method class, `isolate: true`, coverage on, two runs of 3 rounds each, medians of
1289
- all six):
1290
+ **Where it still loses, same weight, same table** (`npm run bench:suite`, re-measured 2026-09-04 on
1291
+ the 4.1 build, 20-method class, `isolate: true`, coverage on, 3 rounds per cell after a discarded
1292
+ warm-up, medians of the three):
1290
1293
 
1291
1294
  | Comparison | 1 000 tests | 3 000 tests | 10 000 tests |
1292
1295
  | --- | ---: | ---: | ---: |
1293
- | vitest-auto-spy | 1.33 s | 3.19 s | 10.66 s |
1294
- | hand-written `vi.fn()` | 1.33 s (0.99×) | 3.08 s (0.97×) | 10.07 s (0.94×) |
1295
- | @bugsplat/vitest-auto-spies | 2.21 s (1.66×) | 5.50 s (1.72×) | 17.40 s (1.63×) |
1296
+ | vitest-auto-spy | 1.33 s | 2.86 s | 10.02 s |
1297
+ | hand-written `vi.fn()` | 1.31 s (0.99×) | 2.85 s (1.00×) | 9.25 s (0.92×) |
1298
+ | @bugsplat/vitest-auto-spies | 2.00 s (1.50×) | 4.61 s (1.61×) | 15.45 s (1.54×) |
1296
1299
 
1297
- Hand-written doubles are still cheaper — **about 5% at the median**, down from 10-15% before 4.1,
1298
- with the 18 per-round ratios spread 0.81-1.01 — and that row is the proof that micro-benchmark
1300
+ Hand-written doubles are still cheaper — **about 3% at the median**, down from 10-15% before 4.1,
1301
+ with the nine per-round ratios spread 0.84-1.00 — and that row is the proof that micro-benchmark
1299
1302
  multipliers do not transfer to suite scale: building a double is on the order of 1% of a test's cost,
1300
1303
  so a 10× win on the double shows up as a few per cent on the run, and nearly everything else in a
1301
1304
  suite swamps it.
@@ -1304,11 +1307,11 @@ suite swamps it.
1304
1307
 
1305
1308
  | Arm | Peak RSS |
1306
1309
  | --- | ---: |
1307
- | hand-written `vi.fn()` | 6733 MB |
1308
- | vitest-auto-spy, default | 2475 MB |
1309
- | vitest-auto-spy, `lazySpies: 'proxy'` | 2109 MB |
1310
+ | hand-written `vi.fn()` | 6366 MB |
1311
+ | vitest-auto-spy, default | 2103 MB |
1312
+ | vitest-auto-spy, `lazySpies: 'proxy'` | 1851 MB |
1310
1313
 
1311
- Hand-written doubles use 2.7x the default's peak RSS here — the difference between finishing and an
1314
+ Hand-written doubles use 3.0x the default's peak RSS here — the difference between finishing and an
1312
1315
  OOM'd worker on a large suite.
1313
1316
 
1314
1317
  That gap traces down to a single double: on the same 100-method class, untouched, `lazySpies:
@@ -1317,11 +1320,11 @@ per-double number that gets multiplied by every double a file holds alive, and t
1317
1320
  decides whether a large suite survives `isolate: false`. Full retained-memory tables:
1318
1321
  [Performance](https://asdalexey.github.io/vitest-auto-spy/core/performance#retained-memory-per-double).
1319
1322
 
1320
- **Guidance keyed to suite size:** `isolate: false` is worth about 3x on its own — more than any
1323
+ **Guidance keyed to suite size:** `isolate: false` is worth about 4x on its own — more than any
1321
1324
  library choice measured here — and it makes memory, not wall-clock, the binding constraint. On a
1322
- wide class under `isolate: false`, add `lazySpies: 'proxy'` for about 15% less peak RSS. Do not turn
1323
- `'proxy'` on under normal isolation (`isolate: true`): there it measurably does nothing (5/7 rounds
1324
- either side of parity).
1325
+ wide class under `isolate: false`, add `lazySpies: 'proxy'` for roughly another 12% off peak RSS. Do
1326
+ not turn `'proxy'` on under normal isolation (`isolate: true`): there it measurably does nothing — a
1327
+ median of exactly 1.00× across five rounds.
1325
1328
 
1326
1329
  Full write-up, methodology and the complete suite-scale tables:
1327
1330
  [Performance](https://asdalexey.github.io/vitest-auto-spy/core/performance).
@@ -1345,9 +1348,9 @@ root so it carries nothing it doesn't ship or test with, and `bench/.npmrc` pins
1345
1348
  `legacy-peer-deps=true` so npm doesn't install a second copy of `vitest` beside the root one — two
1346
1349
  copies would mean two mock registries and the run dies out of memory.
1347
1350
 
1348
- `npm run bench:vs` is the micro-benchmark, a single run in about 55 seconds — the right form for
1349
- local iteration. `npm run bench:vs -- --repeat 5` is what produced the median p75 figures published
1350
- above, about 4.5 minutes for five independent runs in five processes. For the suite-scale tables, see
1351
+ `npm run bench:vs` is the micro-benchmark, a single run in about a minute — the right form for
1352
+ local iteration. `npm run bench:vs:precise` is what produced the median p75 figures published
1353
+ above, about eleven minutes for seven independent runs in seven processes. For the suite-scale tables, see
1351
1354
  `npm run bench:suite --help` — it takes **tens of minutes at 10 000 tests**, so budget for that
1352
1355
  before running it locally.
1353
1356
 
@@ -1874,9 +1877,9 @@ beforeEach(() => {
1874
1877
 
1875
1878
  > **Lazy by default, everywhere.** Every factory builds each method spy on first access
1876
1879
  > (`lazySpies: true`), since Angular tests typically spy a wide service but call
1877
- > only a few of its methods — roughly **4× faster** spy assembly (≈8× on a
1878
- > 20-method service). Behaviour is unchanged; pass `{ lazySpies: false }` to build
1879
- > every spy eagerly.
1880
+ > only a few of its methods — and the margin widens as the class does: **1.4×** on a ten-method
1881
+ > class a test calls twice, **1.9×** on a forty-method one it calls three times. Behaviour is
1882
+ > unchanged; pass `{ lazySpies: false }` to build every spy eagerly.
1880
1883
 
1881
1884
  #### Signal / readonly property mocking (bonus)
1882
1885
 
@@ -1943,46 +1946,24 @@ const { fixture, component } = renderShallow(TaskListComponent, {
1943
1946
 
1944
1947
  `fixture` is a real `ComponentFixture`; nothing here replaces `@angular/core/testing`.
1945
1948
 
1946
- **What it saves, measured.** On a private Angular 22 zoneless suite (784 specs, the AOT
1947
- `@angular/build:unit-test` builder), three of its most expensive component specs were converted and
1948
- the ten-file batch re-run three times — medians, same batch, same machine:
1949
-
1950
- | Spec (479 tests in the batch, all still green) | Before | After | Change |
1951
- | ---------------------------------------------- | ------ | ------ | -------- |
1952
- | a container with a deep child tree (34 tests) | 129 ms | 61 ms | **2.1×** |
1953
- | a list rendering 58 fixtures | 133 ms | 75 ms | **1.8×** |
1954
- | a small leaf component (20 tests) | 29 ms | 38 ms | **0.8×** |
1955
- | the three together | 291 ms | 174 ms | **1.7×** |
1956
-
1957
- The third row is the honest half of the result: a leaf component has almost no subtree to remove, so
1958
- the per-test `overrideComponent` costs more than it saves. **Shallow rendering
1959
- pays where there is a real child tree to skip** — the seven untouched files in the same batch moved
1960
- by ±10%, which is this suite's run-to-run noise, so the two wins are outside it and the one
1961
- regression is only just outside.
1962
-
1963
- **Why 1.7× and not more.** That row is a whole spec file's wall clock — imports, the `TestBed`
1964
- module, the assertions — of which rendering is only a part. Isolated to the render itself, the
1965
- saving tracks the size of the subtree removed, measured on 2026-08-26 on a component holding two
1966
- `@for` tables whose row count is varied:
1967
-
1968
- | Child instances | `TestBed.createComponent` | `renderShallow` | Ratio |
1969
- | --------------: | ------------------------: | --------------: | ----: |
1970
- | 0 | 0.65 ms | 0.55 ms | 1.2× |
1971
- | 10 | 1.00 ms | 0.55 ms | 1.8× |
1972
- | 100 | 2.72 ms | 0.48 ms | 5.7× |
1973
- | 400 | 8.52 ms | 0.53 ms | 16.2× |
1974
-
1975
- `renderShallow` is flat at ~0.5 ms because it never builds the subtree; `createComponent` scales
1976
- linearly with it. So there is no single "renderShallow is N× faster" number — a leaf component has
1977
- nothing to save, and a table or a dashboard is an order of magnitude. Treat the ratios as the
1978
- result, not the absolute times, and pick files to convert with the diagnostics below rather than by
1979
- guessing. `keepTemplate: true` is the middle rung — the component's own template with an empty
1980
- subtree, 1.933 ms → 1.074 ms (1.8×), measured 2026-08-30. Full write-up:
1949
+ **What it saves.** The shape is the point: `renderShallow` is flat in the number of children,
1950
+ because it never builds the subtree, while `TestBed.createComponent` scales linearly with it. So the
1951
+ win is not a fixed percentage and there is no single "renderShallow is N× faster" number — on a leaf
1952
+ component with no children there is nothing to remove, and the per-test `overrideComponent` can cost
1953
+ more than it saves; on a table or a dashboard the two curves are nowhere near each other. A
1954
+ per-render ratio is also the upper bound on what a whole spec file can gain rather than a prediction
1955
+ of it: a file's wall clock pays for imports, the `TestBed` module and the assertions too, none of
1956
+ which shallow rendering touches.
1957
+
1958
+ `keepTemplate: true` is the middle rung — the component's own template with an empty subtree.
1959
+ `buildOverride` applies `imports: keepChildren ?? []` whether or not the template is kept, so with
1960
+ `keepTemplate: true` the template renders while every child in it resolves to nothing under
1961
+ `NO_ERRORS_SCHEMA`. Reach for it when the spec reads something the template creates, and keep
1962
+ `keepChildren` for the handful of children it genuinely needs resolvable. Full write-up:
1981
1963
  [Performance](https://asdalexey.github.io/vitest-auto-spy/core/performance#_2-rendering-the-child-subtree).
1982
1964
 
1983
1965
  Use [the diagnostics](#where-a-spec-spends-its-time) to find the files worth converting rather than
1984
- guessing: across those ten files `TestBed` accounted for 820 ms of 3231 ms (25%), but per file the
1985
- share ranged from 13% to 66%.
1966
+ guessing.
1986
1967
 
1987
1968
  #### Building a class with auto-spied dependencies
1988
1969
 
@@ -2366,11 +2347,12 @@ Reach for `provideAutoSpy` on Angular and `createSpyFromClass` everywhere else;
2366
2347
  whose members live on the instance) and `createMock<T>(partial)` for a data shape the code only
2367
2348
  reads.
2368
2349
 
2369
- None of it is worth optimising. On a ten-method class: `provideAutoSpy` ~8 µs per call,
2370
- `createSpyFromClass` ~29 µs, `createAutoMock` ~33 µs, a `calledWith` lookup ~0.7 µs — five
2371
- providers across two thousand tests come to under a tenth of a second for the whole suite.
2372
- `provideAutoSpy` leads because it defaults to `lazySpies: true`, and prototype discovery is
2373
- cached per class either way. Full numbers and the two settings that do cost something are in
2350
+ None of it is worth optimising. On a ten-method class a test calls two methods of:
2351
+ `createSpyFromClass` **2.25 µs** per call, `createAutoMock` plus four accesses **1.79 µs**, a
2352
+ `calledWith` dispatch of three configured calls **0.21 µs** — five providers across two thousand
2353
+ tests is ten thousand calls, about two hundredths of a second for the whole run. `provideAutoSpy`
2354
+ is the same factory with the same lazy default, and prototype discovery is cached per class either
2355
+ way. Full numbers and the two settings that do cost something are in
2374
2356
  [Performance](https://asdalexey.github.io/vitest-auto-spy/core/performance).
2375
2357
 
2376
2358
  ## Utilities
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-auto-spy",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Auto-generate fully-typed test spies from a class — across Vitest, Bun and node:test, with Angular/NestJS/React/Vue/Svelte recipes. Drop-in replacement for jest-auto-spies and jasmine-auto-spies.",
5
5
  "keywords": [
6
6
  "auto-mock",
@@ -220,6 +220,12 @@
220
220
  "build": "tsup && node scripts/thin-node-cts.mjs && node scripts/check-dist.mjs",
221
221
  "size:badge": "node scripts/size-badge.mjs",
222
222
  "size:badge:check": "node scripts/size-badge.mjs --check",
223
+ "size:entries": "node scripts/size-entries.mjs",
224
+ "size:entries:check": "node scripts/size-entries.mjs --check",
225
+ "size:entries:update": "node scripts/size-entries.mjs --update",
226
+ "cold-import": "node scripts/cold-import.mjs",
227
+ "cold-import:check": "node scripts/cold-import.mjs --check",
228
+ "cold-import:update": "node scripts/cold-import.mjs --update",
223
229
  "llms": "node scripts/generate-llms-txt.mjs",
224
230
  "llms:check": "node scripts/generate-llms-txt.mjs --check",
225
231
  "docs:check": "node scripts/check-docs-frontmatter.mjs",
@@ -236,6 +242,7 @@
236
242
  "test:coverage": "vitest run --coverage",
237
243
  "test:shared-env": "vitest run --config vitest.shared-env.config.mts",
238
244
  "test:zone": "vitest run --config vitest.zone.config.mts",
245
+ "test:invariants": "vitest run --config vitest.invariants.config.mts",
239
246
  "test:types": "vitest run --config vitest.types.config.mts",
240
247
  "types:budget": "node scripts/check-type-budget.mjs",
241
248
  "test:bun": "bun test ./src/bun-tests/core.bun.test.ts ./src/bun-tests/rxjs.bun.test.ts ./src/bun-tests/jasmine.bun.test.ts",
@@ -247,12 +254,14 @@
247
254
  "bench:vs:precise": "node scripts/bench-run.mjs --precise",
248
255
  "bench:memory": "vitest run --config vitest.bench.memory.config.mts",
249
256
  "bench:suite": "node scripts/bench-suite.mjs",
257
+ "bench:check": "node scripts/bench-check.mjs",
250
258
  "lint": "eslint . --ext .ts",
251
259
  "lint:fix": "eslint . --ext .ts --fix",
252
260
  "format": "prettier -w \"src/**/*.ts\"",
253
261
  "format:check": "prettier -c \"src/**/*.ts\"",
262
+ "deps:check": "node scripts/check-dep-drift.mjs",
254
263
  "jscpd": "jscpd",
255
- "check": "npm run typecheck && npm run lint && npm run jscpd && npm run llms:check && npm run docs:check && npm run plugin:sync:check && npm run skill:check && npm run alias:sync:check && npm run test:coverage && npm run test:types && npm run types:budget && npm run test:shared-env && npm run test:zone && npm run test:bun && npm run test:bun:angular",
264
+ "check": "npm run deps:check && npm run typecheck && npm run lint && npm run format:check && npm run jscpd && npm run llms:check && npm run docs:check && npm run plugin:sync:check && npm run skill:check && npm run alias:sync:check && npm run test:coverage && npm run test:types && npm run types:budget && npm run test:shared-env && npm run test:zone && npm run test:invariants && npm run test:bun && npm run test:bun:angular",
256
265
  "prepublishOnly": "npm run build",
257
266
  "version": "node scripts/sync-plugin-version.mjs && node scripts/sync-alias.mjs && git add .claude-plugin alias"
258
267
  },