@bymax-one/nest-core 1.0.1 → 1.1.1

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
@@ -11,6 +11,158 @@ heading here.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [1.1.1] - 2026-08-07
15
+
16
+ **Documentation and tooling.** `dist/` differs from `1.1.0` only in the text of the comments
17
+ described below; no runtime code changed.
18
+
19
+ ### Changed
20
+
21
+ - **Equivalent mutants are documented in the source instead of only in the report.** The nine
22
+ now carry `// Stryker disable next-line <Mutator>: <reason>` on the line they apply to,
23
+ which is the convention now shared across the `@bymax-one/nest-*` libraries. The measured
24
+ score moves from **98.76%** to **100%** — no test and no production logic changed; Stryker
25
+ excludes an ignored mutant from the denominator instead of counting it as one the suite
26
+ failed to kill.
27
+
28
+ Two needed the block `disable`/`restore` form, because `next-line` binds to the following
29
+ statement and those mutants do not sit on one: the cursor-parse catch body, and
30
+ `setExtras({ isGlobal: true }, …)` inside the builder chain. The second was already known —
31
+ the note above that call said a directive does not attach there and left the mutant counted.
32
+ That prose is now the directive's reason, and the block brackets the builder statement alone
33
+ so nothing else in the file loses its `ObjectLiteral` mutants. Both were confirmed by
34
+ running them: the pass reports zero survivors where it reported nine.
35
+
36
+ - The README claimed **Zero suppressions** as a rule. It states what is true now: every
37
+ suppression carries its reason, in the grammar Stryker parses.
38
+
39
+ ### Added
40
+
41
+ - `check:mutants` gate (`scripts/check-mutation-directives.mjs`) — validates every
42
+ `// Stryker` comment against the parser's own regular expression, rejecting a reason
43
+ written after `--` instead of a colon, a reason wrapped onto a second comment line, a stray
44
+ comma in the mutator list, and a mutator name Stryker does not know, which matches nothing
45
+ and so silences nothing. Wired into CI and `prepublishOnly`.
46
+
47
+ ## [1.1.0] - 2026-08-05
48
+
49
+ Four optional integrations, each off by default and each loading nothing until it
50
+ is turned on: OpenAPI documents in development, health-indicator discovery, a
51
+ metrics contribution contract, and OpenTelemetry trace correlation.
52
+
53
+ ### Added
54
+
55
+ - **OpenAPI documents, development only.** A new `openapi` option block and a new
56
+ `./openapi` subpath exporting `applyBymaxOpenApi`. Enabling the block and calling
57
+ the helper once during bootstrap serves an interactive UI and the raw document,
58
+ carrying the schemas this package already owns — the error envelope and its code
59
+ catalog, the health response, and the offset and cursor page shapes with their
60
+ query parameters.
61
+
62
+ `@nestjs/swagger` is an optional peer, reached only through a lazy dynamic import,
63
+ exactly like `prom-client`: an application that leaves the feature off never loads
64
+ it, and enabling the feature without installing it fails at boot with a message
65
+ naming the package and the install command.
66
+
67
+ The schemas are contributed as plain specification objects rather than decorated
68
+ classes. A decorator runs when its class is defined, so describing these contracts
69
+ with `@ApiProperty` would load the peer in every application that imports this
70
+ package, including the ones that never enable the feature.
71
+
72
+ A contributed entry never overwrites one the document already defines.
73
+
74
+ - **Health-indicator discovery.** A new `health.autoDiscover` option and a
75
+ `@BymaxHealthIndicator()` marker, exported from the `./health` subpath alongside
76
+ the contract it belongs to. With discovery enabled, every marked provider in the
77
+ application joins readiness — so a library an application merely imports can
78
+ contribute its own check without the application registering anything.
79
+
80
+ Discovery matches the marker, never the shape of an object: a provider that
81
+ happens to expose a `name` and a `check` is not a readiness probe, and scraping
82
+ one in would let an unrelated failure take an application out of rotation. A
83
+ provider that is marked but does not implement `IHealthIndicator` fails the boot
84
+ naming the class, rather than being skipped silently.
85
+
86
+ Explicit registration still wins: an indicator bound under
87
+ `BYMAX_HEALTH_INDICATORS` keeps its name and its position, and a discovered one
88
+ with the same name is dropped. Discovered indicators are sorted by name, so the
89
+ `checks` array is stable across restarts. The provider graph is walked once, at
90
+ bootstrap.
91
+
92
+ Off by default. It changes which failures can fail a readiness probe, which is
93
+ a decision an application makes rather than one it inherits from its imports.
94
+
95
+ - **A metrics contribution contract.** A new `./metrics` subpath exporting
96
+ `IMetricsContributor` and a `@BymaxMetricsContributor()` marker. A marked provider
97
+ is handed the registry once at bootstrap and registers its own collectors, so a
98
+ library's metrics appear on the application's existing scrape endpoint with
99
+ nothing wired.
100
+
101
+ Contributors receive the registry rather than injecting `BYMAX_METRICS_REGISTRY`:
102
+ a library that injected the token would depend on this package's DI tokens, and
103
+ therefore on the module. Receiving it as an argument means the only thing a
104
+ contributing library imports is the contract and the marker.
105
+
106
+ Registration failures are rethrown with the contributor named and the original
107
+ error chained. `prom-client` reports a duplicate metric name but not who
108
+ registered it, which in an application composing several libraries is the hard
109
+ half of the question. Contributors run sorted by class name, so a collision fails
110
+ the same way on every boot.
111
+
112
+ No separate flag: contribution rides on the metrics feature. With metrics
113
+ disabled no contributor runs and `prom-client` is still never loaded.
114
+
115
+ This is the one subpath whose types name `prom-client`. Implementing the contract
116
+ means constructing `prom-client` collectors, so anyone importing it already
117
+ depends on the peer; every other subpath stays free of it.
118
+
119
+ - **Trace correlation.** A new `telemetry` option block reads the active OpenTelemetry
120
+ span and carries its identifiers into the request-timing sample, into the exception
121
+ filter's observability seam, and — behind `telemetry.exposeTraceId` — into the error
122
+ envelope served to the client.
123
+
124
+ `@opentelemetry/api` is an optional peer. Unlike the other two it is read on every
125
+ request, so it is loaded once at bootstrap rather than at the point of use, and only
126
+ when the feature is enabled.
127
+
128
+ This package reads; it never traces. No span is created, no SDK configured, no
129
+ exporter registered: all of that belongs to the instrumentation an operator already
130
+ runs, and duplicating it would produce two spans per request.
131
+
132
+ A request with nothing recording, or an all-zero span context, resolves to no trace:
133
+ the fields are absent rather than set to a sentinel. Trace identifiers are never used
134
+ as metric labels — a trace id is unbounded, and one unbounded label is enough to make
135
+ a scrape endpoint the most expensive route in a service.
136
+
137
+ ### Changed
138
+
139
+ - **The marker-based provider scan is now shared.** Readiness discovery and metrics
140
+ contribution use one scan, which reads Nest's provider graph, matches a literal
141
+ metadata key, and labels each match by class name — falling back to the provider
142
+ token for an anonymous class. Behavior is unchanged for readiness.
143
+
144
+ ### Security
145
+
146
+ - **A trace id is not published in a response body by default.** `telemetry.exposeTraceId`
147
+ is off: a trace id is not a secret, but in a response it tells a caller that a tracing
148
+ backend exists and hands them the identifier correlating their request with everything
149
+ else in that trace. With the option off the identifiers still reach the timing sample and
150
+ the logging seam.
151
+ - **The OpenAPI document is never served in production.** `NODE_ENV` decides, and
152
+ the decision is fail-closed: only `development` and `test` are non-production, so
153
+ an unset or unrecognized value is production. The guard runs in two independent
154
+ layers — the option resolver forces the feature off, and the bootstrap helper
155
+ refuses again without trusting that resolution — and there is no override. Asking
156
+ for the document in production is a no-op with a warning, not an error, so one
157
+ configuration can be shared across environments.
158
+
159
+ ### Notes
160
+
161
+ - `applyBymaxOpenApi` must be called **before** `app.listen()`. Mounting the document
162
+ re-registers routes on the HTTP adapter, and doing that against an
163
+ already-initialized Express 5 application replaces the router: every other route,
164
+ including this package's health endpoints, stops resolving.
165
+
14
166
  ## [1.0.1] - 2026-08-04
15
167
 
16
168
  **Behaviour change on the readiness endpoint.** A failing indicator's message no
@@ -108,6 +260,8 @@ have regressed from. They are kept because the reasoning is worth having.
108
260
  cleanly and silently. Corrected before the first publish, so no released version
109
261
  ever carried the permissive range. No runtime behaviour changed.
110
262
 
263
+ [1.1.0]: https://github.com/bymaxone/nest-core/compare/v1.0.1...v1.1.0
111
264
  [1.0.1]: https://github.com/bymaxone/nest-core/compare/v1.0.0...v1.0.1
112
265
  [1.0.0]: https://github.com/bymaxone/nest-core/releases/tag/v1.0.0
113
- [Unreleased]: https://github.com/bymaxone/nest-core/compare/v1.0.0...HEAD
266
+ [1.1.1]: https://github.com/bymaxone/nest-core/compare/v1.1.0...v1.1.1
267
+ [Unreleased]: https://github.com/bymaxone/nest-core/compare/v1.1.1...HEAD