npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@starci/jest-preset

v2.6.0

Published

The StarCi jest config for Nest repositories (unit, integration, e2e and contract projects, ts-jest with diagnostics off, per-file 100 coverage of *.service.ts only) and the one shared unit-test double kit: typed mock<T>(), mockEntityManager(), fakeTransa

Readme

@starci/jest-preset

The jest config of a Nest repository and the one shared unit-test double kit (mock<T>(), mockEntityManager, fakeTransaction, fakeQueryBuilder, queueQueryBuilders, fakeCache, fakeLock, recordingEventBus, recordingQueueOutbox, fakeInbox, builder, FakeClock, fakeIds and the Outcome matchers). Install it from the npm registry at the exact version in the StarCi runtime's knowledge/stack/pins.yaml (see packages/README.md).

// jest.config.js (a managed file: `starci app sync` renders it, `starci app check` compares it; do not edit)
module.exports = require("@starci/jest-preset").starciJestConfig({
  coverage: { roots, roles, files, excludes },
})

The only option is coverage: the measured scope starci app sync derives once from the slot manifest (scripts/hfs/coverage-scope.mjs, R204 HFS_COVERAGE_SCOPE_DRIFT) — the measured roots, the roles a measured file carries, the logic files a slot declares (logic) and the exclusions. A repository has no other jest setting to tune.

What it sets

  • Four projects, one per test kind: unit (**/*.spec.ts, which by the unit standard means one <name>.<role>.spec.ts beside each spec-required subject — a *.service.ts, a *.cli.ts of src/features/cli — and beside any other measured logic file that chooses one; clearMocks; the Outcome matchers are installed through setupFilesAfterEnv; ignores src/tests/{world,integration,e2e,contract}/), integration (src/tests/integration/**/*.integration-spec.ts), e2e (src/tests/e2e/**/*.e2e-spec.ts) and contract (src/tests/contract/**/*.contract-spec.ts). The last three compile against src/tests/tsconfig.json, start the one test world through globalSetup/globalTeardown (src/tests/world/global-setup.ts, global-teardown.ts) and run on the world runner (world-runner.js, WORLD_RUNNER): every spec file in a worker process of its own and never in band, so nothing the globalSetup or another file left on a process global (a framework registry such as @nestjs/graphql's type metadata) reaches the file; and up to min(--maxWorkers, slots) files at once, each bound to its own data slot of @starci/test-world 1.1.0 (STARCI_TEST_WORLD_SLOT), never two files on one slot, because the test world resets a slot's data when a file boots. The runner reads test-world state protocol 2 only: pin the two packages together (knowledge/stack/pins.yaml); another protocol fails with JEST_PRESET_WORLD_PAIR_MISMATCH. Only the unit project has a setupFilesAfterEnv. The managed scripts select one project each: test, test:integration, test:e2e, test:contract; a contract spec skips itself without sandbox config.
  • The three path aliases @features/*, @modules/*, @tests/* (MODULE_NAME_MAPPER), the same ones the managed tsconfig.json declares.
  • ts-jest with diagnostics: false in every project. isolatedModules: true comes from @starci/tsconfig (ts-jest 29.4 reads it there; its own option is deprecated). Types are checked once, by typecheck (unit specs included) and typecheck:tests (src/tests/tsconfig.json), and test:integration/test:e2e/test:contract run typecheck:tests first, so no world spec runs on code that does not type-check.
  • Coverage of the measured logic, per-file 100. collectCoverageFrom is one glob per coverage.roots entry covering <root>/**/*.{<roles>}.ts (the ruleParams.be.logicRoles of the rendered scope) plus each coverage.files entry (a logic file its slot declares), minus its excludes, dist and coverage, and coverageThreshold holds { lines: 100, branches: 100, functions: 100, statements: 100 } on one union glob of those roots and files. Jest applies a glob threshold to each file on its own, so every file of a ruleParams.be.logicRoles role inside a measured slot, and every declared logic file, must reach 100 on every metric (an average never passes). The managed test script runs the unit project with --coverage and fails below it. Coverage is measured by v8, which counts the real source. The unit project overlays isolatedModules: true, importHelpers: true and sourceMap: true on the repository tsconfig, so ts-jest transpiles each file without building a type program. unit-metadata.js, a ts-jest wrapper and after-transformer, maps only synthetic typeof value === "function" ? value : Object guards inside TypeScript's generated __metadata("design:paramtypes", ...) calls to the dedicated unit-metadata.generated.js coverage anchor. Their runtime guards remain unchanged, including the fallback to Object for an undefined or non-class dependency; generated branches are not attributed to source parameters. The wrapper restores that second source after ts-jest emits its map. The anchor ignores generated guards only and is omitted from LCOV. Application source calls and guards remain measured. Source maps are enabled even when the build config disables them. Source conditionals, including branches on the same constructor line, still count toward per-file 100 coverage. Decorator helpers come from tslib, which the repository lists in its devDependencies (UNIT_COMPILER_OPTIONS). Reporters are text-summary, text and the bundled LCOV reporter (which gives same-named callbacks unique function identities): the run writes coverage/lcov.info under the repository (be/) root.
  • Sonar and Codecov import that lcov, scoped to the same measured files. starci app sync renders sonar.coverage.exclusions (the complement of the measured files: SonarQube has no coverage inclusions) and the codecov.yml status paths and components from the one derivation (scripts/hfs/coverage-scope.mjs), so jest, Sonar and Codecov measure the same files; the Sonar gate holds coverage at 100 overall, on new code and per file. sonarExclusions() still renders sonar.exclusions (**/*.spec.ts,**/*.e2e-spec.ts,**/dist/**,**/coverage/**). There is no coverage exclusion list.

The unit standard in one page

Every spec-required file is unit-tested beside its subject — a *.service.ts, a cli command of src/features/cli — through Test.createTestingModule, with the doubles of this package and nothing else, and any other measured logic file may carry the same spec beside it:

import { Test } from "@nestjs/testing"
import { FakeClock, mockEntityManager } from "@starci/jest-preset"

const clock = new FakeClock("2026-01-01T00:00:00.000Z")
const entityManager = mockEntityManager({ findOne: [Accrual, null] })
const moduleRef = await Test.createTestingModule({
  providers: [
    CommissionService,
    { provide: PRIMARY_ENTITY_MANAGER, useValue: entityManager },   // exactly the constructor dependencies
    { provide: CLOCK, useValue: clock },
  ],
}).compile()
const service = moduleRef.get(CommissionService)

No new of the service, no imports, no overrideProvider, no jest.mock, no casts, no Date.now() and no process.env. The full rules and the dependency-to-double-to-cases table are contexts/code/be/test.md (the BE-TEST rules). Every export below is on the package root (@starci/jest-preset); the sub-paths ./mock, ./clock, ./entity-manager, ./ids, ./query-builder, ./matchers, ./cache, ./lock, ./event-bus, ./queue, ./inbox and ./builders exist as well.

mockEntityManager and fakeTransaction

A strict, typed EntityManager double. Describe what the subject should see; every method is a jest.fn, and any method the subject calls that the spec did not stub throws an error naming the method, so the absence of a stub proves the absence of that database call. A wrong entity or SQL text throws too. create(Entity, plain) works without a stub.

import { mockEntityManager, fakeTransaction } from "@starci/jest-preset"

// [Entity, result], typed against the entity: `findOne: [Order, 5]` is a compile error
const em = mockEntityManager({ findOne: [Order, order], save: [Order, saved] })
expect(em.save).toHaveBeenCalledWith(expect.objectContaining({ status: "paid" }))

// a raw query: [sql, rows]
const reader = mockEntityManager({ query: ["select 1", [{ n: 1 }]] })

// lists: a list of pairs answers several calls in order
const two = mockEntityManager({ find: [[Order, [orderA]], [Order, [orderB]]] })

// a transaction: work runs with a scoped view; commits, rollbacks and writes are recorded
const tx = fakeTransaction(mockEntityManager({ save: [Order, saved] }))
await service.place(params)                       // service calls em.transaction(async (manager) => ...)
expect(tx.commits).toBe(1)
expect(tx.committedWrites).toHaveLength(1)        // after a throwing body: tx.rollbacks, tx.rolledBackWrites, tx.outcomes

The stubbable methods are findOne, findOneBy, findOneOrFail, findOneByOrFail, find, findBy, findAndCount, findAndCountBy, count, countBy, exists, existsBy, save, remove, softRemove, recover, insert, update, upsert, delete, softDelete, restore, increment, decrement and query. fakeTransaction(em) returns { em, outcomes, commits, rollbacks, committedWrites, rolledBackWrites }. A write is a write method, a query of a DML statement, or a query that calls a migration's database function the way callDbFunction does (SELECT * FROM "public"."inbox_claim_event"($1, $2), any schema but pg_catalog); any other SELECT is a read.

fakeQueryBuilder and queueQueryBuilders

The double of createQueryBuilder(Entity, "alias"), the builder a lite service reads with. Every chained method records { method, args } and returns the builder; a terminal (execute, getMany, getOne, getRawMany, getRawOne, getCount, getManyAndCount) resolves the answer the spec gave it, and a function answer is called when the terminal runs.

import { mockEntityManager, queueQueryBuilders } from "@starci/jest-preset"

const em = mockEntityManager()
const [open] = queueQueryBuilders(em, [{ getMany: [order] }])   // one fake per createQueryBuilder call, in order
await service.listOpen(owner)
expect(open.callsOf("where")).toEqual([["o.ownerId = :owner", { owner }]])

fakeCache(clock), fakeLock(clock), recordingEventBus() and recordingQueueOutbox()

const clock = new FakeClock("2026-01-01T00:00:00.000Z")

const cache = fakeCache(clock)     // JSON round trip; TTL from the clock
await cache.set({ key, args: ["u1"], value: profile })   // the typed-key port: get/set/del({ key, args, value })
await cache.get({ key, args: ["u1"] })
await cache.set("raw-key", value, 60_000)                // the cache-manager shape: get(text), set(text, value, ttlMs)
cache.has({ key, args: ["u1"] })                          // a live entry exists
cache.ttlOf({ key, args: ["u1"] })                        // seconds left (rounded up), null when absent or expired
cache.keys()                                              // the texts of the live entries
clock.advance(301_000)                                    // entries with a 300 s TTL are now expired

const lock = fakeLock(clock)       // acquire, contention, release, expiry, growing fence
const grant = await lock.acquire({ name: "writer", holder: "a", ttlMs: 30_000 })   // null when held by another
lock.isHeld("writer"); lock.holderOf("writer"); lock.fenceOf("writer")
await lock.release({ grant })

const bus = recordingEventBus()   // publish(event, tx): the EventBus port
bus.events          // what the outbox would hold: the first publication of each (event name, event id)
bus.writes          // every event published, duplicates included
bus.entries         // every publication with its tx and `inTransaction`
bus.allInTransaction; bus.eventsOf(OrderPlacedEvent); bus.clear()
bus.setPendingRetries(2)                    // what pendingRetries(eventClass) answers
bus.queueDeadLetters(letter)                // what deadLetters(eventClass) hands out; bus.requeued keeps requeue(id) calls
bus.failNext("publish", new Error("down"))  // the next call of that operation rejects once

const queueOutbox = recordingQueueOutbox()  // write(tx, queue, payload): the QueueOutbox port
queueOutbox.jobs; queueOutbox.jobsOf("mail"); queueOutbox.entries; queueOutbox.allInTransaction

const inbox = fakeInbox()                // the delivery side, the twin of the recording outboxes
await inbox.claim("sepay", "e-1")        // true the first time, false for the same pair until it is released
await inbox.release("sepay", "e-1")      // gives the claim back: the next claim answers true
inbox.seen("sepay", "e-2")               // a redelivery: the next claim of the pair answers false
inbox.claims; inbox.claimed; inbox.released; inbox.failNext("claim", new Error("db down")); inbox.clear()
// claimRow: the EntityInbox claim that inserts the receipt row only for the winning claim and answers its id
await inbox.claimRow("webhook.sepay", "e-3", tx, { entity: SepayInboxEntity, row: (deliveryId) => ({ deliveryId }) })  // "receipt-1"
inbox.receipts                           // every inserted receipt: id, source, eventId, entity, row, tx, inTransaction
inbox.receiptExists("webhook.sepay", "e-4") // the receipt row is already there: the winning claim inserts nothing, answers undefined

const clientInbox = fakeInbox(clock)     // the driven clock stamps client claims for retention tests
await clientInbox.runClient({ principalId: "p-1", operation: "checkout.place", idempotencyKey: "key-1", request: { quantity: 2 } }, tx, async () => ({ kind: "ok", value: { orderId: "o-1" } }))
clientInbox.clientEntries               // every client claim with its tx and inTransaction flag
await clientInbox.sweepClients(cutoff, tx)
clientInbox.sweeps                      // every sweep attempt's cutoff, tx and inTransaction flag, failures included

Client repeats replay the stored JSON outcome, including a refusal; a different request for the same namespace and key rejects with IDEMPOTENCY_KEY_REUSED. Concurrent repeats wait for the first effect. A rejected effect gives its claim back. Delivery release preserves client receipts and their outcomes. The double records the transaction passed to it; a later outer transaction rollback needs real database integration proof. Without a clock, client claims use the Unix epoch. The sweep removes one bounded batch of completed expired client claims and preserves delivery claims.

builder, fakeIds and the Outcome matchers

import { builder, fakeIds } from "@starci/jest-preset"

const options = builder<CommissionOptions>({ bps: 3000, enabled: true })   // a function returning the defaults with overrides applied
const disabled = options({ enabled: false })
const ids = fakeIds()             // ids.next() === "00000000-0000-4000-8000-000000000001", then ...0002; ids.issued, ids.reset()

expect(outcome).toBeRefused("COMMISSION_SELF_REFERRAL")                     // { kind: "refused" } with this code
expect(outcome).toBeRefused({ code: "PLAN_LIMIT", params: { max: 3 } })    // and these params
expect(outcome).toSucceedWith(accrual)                                      // { kind: "ok" } whose value equals `accrual`

The matchers are installed in the unit project only (setupFilesAfterEnv); their types are declared globally by importing the package.

mock<T>()

Replaces { ... } as unknown as T.

import { mock } from "@starci/jest-preset"

const repo = mock<UserRepository>({ name: "users" })       // overrides may be partial
repo.find.mockResolvedValue({ id: "a" })                   // typed against UserRepository["find"]
const service = new UserService(repo)                       // assignable wherever a UserRepository is required
expect(repo.find).toHaveBeenCalledWith("a")

Every property read yields one stable jest.fn(), created on first read. The double is not thenable, so returning it from an async function does not hang; then, toJSON and Symbol keys read as absent. A value you pass in overrides wins. Wrong stubs (repo.find.mockResolvedValue(1)) and members the type does not have are compile errors. Fixtures use it instead of as never and as unknown as.

FakeClock

The test double for the injected Clock port (platform/clock). Business code asks clock.now() instead of reading Date.now() or new Date() ambiently (no-ambient-clock, @starci/eslint-canon-be R79 BE_AMBIENT_CLOCK); a spec drives time explicitly instead of sleeping for real or racing the wall clock.

import { FakeClock } from "@starci/jest-preset/clock"

const clock = new FakeClock("2026-01-01T00:00:00.000Z")   // starts at a chosen instant; no argument starts at real now
const service = new PlanService(clock)

clock.advance(60_000)          // move the clock forward (or back, with a negative value) without a real sleep
clock.set("2026-06-15T00:00:00.000Z")   // jump to a chosen instant, e.g. to drive a scheduled job
expect(clock.now().toISOString()).toBe("2026-06-15T00:00:00.000Z")

FakeClock satisfies the Clock port structurally ({ now(): Date }); no cast is needed to inject it in place of the real clock.