@starci/jest-preset 2.5.1 → 2.5.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.5.3 - 2026-10-30
4
+
5
+ - Changed: the message that names the managed sync command says `starci app sync --execute`.
6
+
7
+ ## 2.5.2 - 2026-10-30
8
+
9
+ - Added: `coverage.files` of the rendered be/jest.config.js: the files a slot declares as logic (hfs 4.11.20 `logic`) are collected and held to 100 per file like the logic roles; a config rendered without `files` loads as before. Specs: `test/coverage-scoped.spec.ts`, `test/preset.spec.ts`.
10
+
3
11
  ## 2.5.1 - 2026-10-29
4
12
 
5
13
  - Fixed: `fakeTransaction` records a `query` that calls a migration's database function the way `callDbFunction` writes it (`SELECT * FROM "<schema>"."<fn>"(...)`, any schema but `pg_catalog`) as a write, so a rolled-back inbox claim or event send appears in `rolledBackWrites`; any other `SELECT` stays a read.
package/README.md CHANGED
@@ -5,13 +5,13 @@ The jest config of a Nest repository and the one shared unit-test double kit (`m
5
5
  ```js
6
6
  // jest.config.js (a managed file: `starci app sync` renders it, `starci app check` compares it; do not edit)
7
7
  module.exports = require("@starci/jest-preset").starciJestConfig({
8
- coverage: { roots, roles, excludes },
8
+ coverage: { roots, roles, files, excludes },
9
9
  })
10
10
  ```
11
11
 
12
12
  The only option is `coverage`: the measured scope `starci app sync` derives once from the slot manifest
13
- (`scripts/hfs/coverage-scope.mjs`, R204 `HFS_COVERAGE_SCOPE_DRIFT`) — the measured roots, the roles a measured file carries
14
- and the exclusions. A repository has no other jest setting to tune.
13
+ (`scripts/hfs/coverage-scope.mjs`, R204 `HFS_COVERAGE_SCOPE_DRIFT`) — the measured roots, the roles a measured file carries,
14
+ the logic files a slot declares (`logic`) and the exclusions. A repository has no other jest setting to tune.
15
15
 
16
16
  ## What it sets
17
17
 
@@ -32,9 +32,9 @@ and the exclusions. A repository has no other jest setting to tune.
32
32
  (`src/tests/tsconfig.json`), and `test:integration`/`test:e2e`/`test:contract` run `typecheck:tests` first, so no world spec
33
33
  runs on code that does not type-check.
34
34
  - **Coverage of the measured logic, per-file 100.** `collectCoverageFrom` is one glob per `coverage.roots` entry covering `<root>/**/*.{<roles>}.ts`
35
- (the `ruleParams.be.logicRoles` of the rendered scope), minus its `excludes`, `dist` and `coverage`, and `coverageThreshold` holds
36
- `{ lines: 100, branches: 100, functions: 100, statements: 100 }` on one union glob of those roots. Jest applies a glob threshold to each file on its own, so every file of a
37
- `ruleParams.be.logicRoles` role inside a measured slot must reach 100 on every metric (an average never passes). The
35
+ (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
36
+ `{ 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
37
+ `ruleParams.be.logicRoles` role inside a measured slot, and every declared logic file, must reach 100 on every metric (an average never passes). The
38
38
  managed `test` script runs the unit project with `--coverage` and fails below it. Coverage is measured by v8, which counts the
39
39
  real source. The unit project overlays `isolatedModules: true`, `importHelpers: true` and `sourceMap: true` on the repository tsconfig, so ts-jest
40
40
  transpiles each file without building a type program. `unit-metadata.js`, a ts-jest wrapper and after-transformer, maps
package/dist/index.d.ts CHANGED
@@ -19,10 +19,15 @@ export type { RecordingEventBus } from "./event-bus.js";
19
19
  export type { RecordingQueueOutbox } from "./queue.js";
20
20
  export type { FakeInbox } from "./inbox.js";
21
21
  export type { RefusalReason } from "./matchers.js";
22
- /** The coverage scope `starci app sync` renders from the slot manifest: the measured roots (a directory pattern per measured slot, a placeholder folder written as a star), the logic roles measured in them and the directories inside a root that are never measured (each ending in a double star). */
22
+ /**
23
+ * The coverage scope `starci app sync` renders from the slot manifest: the measured roots (a directory pattern per measured slot, a placeholder
24
+ * folder written as a star), the logic roles measured in them, the logic files a slot declares (`logic`: a file that carries logic without a
25
+ * logic-role suffix, measured like one) and the directories inside a root that are never measured (each ending in a double star).
26
+ */
23
27
  export interface StarciCoverageScope {
24
28
  readonly roots: readonly string[];
25
29
  readonly roles: readonly string[];
30
+ readonly files?: readonly string[];
26
31
  readonly excludes: readonly string[];
27
32
  }
28
33
  export interface StarciJestOptions {
@@ -46,10 +51,11 @@ export type StarciJestConfig = Omit<Config, "projects" | "coverageReporters" | "
46
51
  };
47
52
  /**
48
53
  * Coverage is measured where logic runs: every file `<name>.<role>.ts` of a LOGIC role (service, policy, projection, guard, mapper, client, ...)
49
- * inside the roots the slot manifest marks `coverage: required` (the modules of the back end: domain, integrations, platform, projections).
54
+ * inside the roots the slot manifest marks `coverage: required` (the modules of the back end: domain, integrations, platform, projections), and
55
+ * every logic file such a slot declares (`logic`: a file that carries logic without a logic-role suffix).
50
56
  * The features (handlers, resolvers, controllers, consumers, processors, steps, sagas, cli commands) are thin by rule (BE_FEATURE_THIN) and are
51
57
  * never measured. The scope is NOT written here: `starci app sync` renders it from the manifest (scripts/hfs/coverage-scope.mjs) into the repository's
52
- * jest.config.js as `starciJestConfig({ coverage: { roots, roles, excludes } })`, and the same derivation renders Sonar's
58
+ * jest.config.js as `starciJestConfig({ coverage: { roots, roles, files, excludes } })`, and the same derivation renders Sonar's
53
59
  * `sonar.coverage.exclusions` and the Codecov paths and components, so the three can never disagree. Every measured file must reach 100 on every metric.
54
60
  * The unit run also writes `coverage/lcov.info`: Sonar and Codecov import it.
55
61
  */
@@ -64,8 +70,8 @@ declare const COVERAGE_THRESHOLD: Readonly<{
64
70
  /** The glob one measured root stands for: every file of a logic role below it. */
65
71
  declare function rootGlob(root: string, roles: readonly string[]): string;
66
72
  /**
67
- * jest's `collectCoverageFrom` of a coverage scope `{ roots, roles, excludes }`: the glob of every root, then the none directories inside a
68
- * root (`excludes`, each a glob that ends in `**`) and the generated folders as negations.
73
+ * jest's `collectCoverageFrom` of a coverage scope `{ roots, roles, files, excludes }`: the glob of every root, every declared logic file, then
74
+ * the none directories inside a root (`excludes`, each a glob that ends in `**`) and the generated folders as negations.
69
75
  */
70
76
  declare function collectCoverageFrom(coverage: StarciCoverageScope): string[];
71
77
  /**
package/dist/index.js CHANGED
@@ -37,10 +37,11 @@ Object.defineProperty(exports, "FakeIds", { enumerable: true, get: function () {
37
37
  const SONAR_EXCLUSIONS = require("./sonar-exclusions.js");
38
38
  /**
39
39
  * Coverage is measured where logic runs: every file `<name>.<role>.ts` of a LOGIC role (service, policy, projection, guard, mapper, client, ...)
40
- * inside the roots the slot manifest marks `coverage: required` (the modules of the back end: domain, integrations, platform, projections).
40
+ * inside the roots the slot manifest marks `coverage: required` (the modules of the back end: domain, integrations, platform, projections), and
41
+ * every logic file such a slot declares (`logic`: a file that carries logic without a logic-role suffix).
41
42
  * The features (handlers, resolvers, controllers, consumers, processors, steps, sagas, cli commands) are thin by rule (BE_FEATURE_THIN) and are
42
43
  * never measured. The scope is NOT written here: `starci app sync` renders it from the manifest (scripts/hfs/coverage-scope.mjs) into the repository's
43
- * jest.config.js as `starciJestConfig({ coverage: { roots, roles, excludes } })`, and the same derivation renders Sonar's
44
+ * jest.config.js as `starciJestConfig({ coverage: { roots, roles, files, excludes } })`, and the same derivation renders Sonar's
44
45
  * `sonar.coverage.exclusions` and the Codecov paths and components, so the three can never disagree. Every measured file must reach 100 on every metric.
45
46
  * The unit run also writes `coverage/lcov.info`: Sonar and Codecov import it.
46
47
  */
@@ -54,15 +55,19 @@ function rootGlob(root, roles) {
54
55
  return `${root}**/*.${roles.length === 1 ? roles[0] : `{${roles.join(",")}}`}.ts`;
55
56
  }
56
57
  /**
57
- * jest's `collectCoverageFrom` of a coverage scope `{ roots, roles, excludes }`: the glob of every root, then the none directories inside a
58
- * root (`excludes`, each a glob that ends in `**`) and the generated folders as negations.
58
+ * jest's `collectCoverageFrom` of a coverage scope `{ roots, roles, files, excludes }`: the glob of every root, every declared logic file, then
59
+ * the none directories inside a root (`excludes`, each a glob that ends in `**`) and the generated folders as negations.
59
60
  */
60
61
  function collectCoverageFrom(coverage) {
61
- return [...coverage.roots.map((root) => rootGlob(root, coverage.roles)), ...[...coverage.excludes, ...COVERAGE_EXCLUDES].map((glob) => `!${glob}`)];
62
+ return [
63
+ ...coverage.roots.map((root) => rootGlob(root, coverage.roles)),
64
+ ...(coverage.files ?? []),
65
+ ...[...coverage.excludes, ...COVERAGE_EXCLUDES].map((glob) => `!${glob}`),
66
+ ];
62
67
  }
63
- /** One union glob keeps Jest's per-file checks while a scoped map may contain files from only one measured root. */
68
+ /** One union glob keeps Jest's per-file checks while a scoped map may contain files from only one measured root or declared logic file. */
64
69
  function coverageThresholdOf(coverage, root) {
65
- const rootGlobs = coverage.roots.map((entry) => rootGlob(entry, coverage.roles)).filter((glob) => hasCoverageSubjects(root, glob));
70
+ const rootGlobs = [...coverage.roots.map((entry) => rootGlob(entry, coverage.roles)), ...(coverage.files ?? [])].filter((glob) => hasCoverageSubjects(root, glob));
66
71
  if (rootGlobs.length === 0)
67
72
  return {};
68
73
  const glob = rootGlobs.length === 1 ? rootGlobs[0] : `{${rootGlobs.join(",")}}`;
@@ -215,16 +220,20 @@ function hasCoverageSubjects(root, glob) {
215
220
  const isList = (value) => Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0);
216
221
  /** A property of an unknown value, `undefined` when the value is not an object that has it. */
217
222
  const fieldOf = (value, name) => (typeof value === "object" && value !== null && name in value ? Reflect.get(value, name) : undefined);
218
- /** The scope `{ roots, roles, excludes }` of an options object, or a refusal: the scope is rendered by `starci app sync`, never defaulted. */
223
+ /**
224
+ * The scope `{ roots, roles, files, excludes }` of an options object, or a refusal: the scope is rendered by `starci app sync`, never defaulted.
225
+ * `files` (the declared logic files) is absent from a config rendered before slots could declare them, and is then empty.
226
+ */
219
227
  function coverageOf(options) {
220
228
  const coverage = fieldOf(options, "coverage");
221
229
  const roots = fieldOf(coverage, "roots");
222
230
  const roles = fieldOf(coverage, "roles");
231
+ const files = fieldOf(coverage, "files") ?? [];
223
232
  const excludes = fieldOf(coverage, "excludes");
224
- if (!coverage || !isList(roots) || !isList(roles) || !roles.length || !isList(excludes)) {
225
- throw new Error("starciJestConfig({ coverage: { roots, roles, excludes } }) needs the coverage scope starci app sync renders from the slot manifest; run \"npx starci app sync --write\"");
233
+ if (!coverage || !isList(roots) || !isList(roles) || !roles.length || !isList(files) || !isList(excludes)) {
234
+ throw new Error("starciJestConfig({ coverage: { roots, roles, files, excludes } }) needs the coverage scope starci app sync renders from the slot manifest; run \"npx starci app sync --execute\"");
226
235
  }
227
- return { roots, roles, excludes };
236
+ return { roots, roles, files, excludes };
228
237
  }
229
238
  /** The whole jest config: projects `unit`, `integration`, `e2e` and `contract`. It takes the coverage scope the managed repository config renders; without it it refuses. */
230
239
  function starciJestConfig(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starci/jest-preset",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "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(), fakeTransaction(), fakeQueryBuilder(), queueQueryBuilders(), fakeCache(), fakeLock(), recordingEventBus(), recordingQueueOutbox(), fakeInbox(), FakeClock, fakeIds(), builder() and the Outcome matchers toBeRefused/toSucceedWith.",
5
5
  "license": "MIT",
6
6
  "author": "starci183",