@bymax-one/nest-core 1.0.0 → 1.0.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 +36 -0
- package/README.md +43 -20
- package/dist/index.cjs +17 -7
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.mjs +18 -8
- package/package.json +36 -23
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,41 @@ heading here.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [1.0.1] - 2026-08-04
|
|
15
|
+
|
|
16
|
+
**Behaviour change on the readiness endpoint.** A failing indicator's message no
|
|
17
|
+
longer appears in the HTTP response by default; it goes to the logger instead.
|
|
18
|
+
|
|
19
|
+
### Security
|
|
20
|
+
|
|
21
|
+
- **The readiness response no longer publishes an indicator's failure message.**
|
|
22
|
+
`GET /health/ready` returned `details.error` carrying the rejecting indicator's
|
|
23
|
+
`Error#message`. That endpoint is typically unauthenticated and reachable by
|
|
24
|
+
whatever probes it — and an indicator rarely authors its own failure text: it
|
|
25
|
+
writes `await this.redis.ping()` and lets the driver's error propagate. Driver
|
|
26
|
+
errors carry hosts, ports and, for a connection string, credentials. An
|
|
27
|
+
indicator failing with `connection refused: postgres://user:PASSWORD@db:5432`
|
|
28
|
+
served that string to anyone who could reach the probe.
|
|
29
|
+
|
|
30
|
+
A failing check is now `{ name, status: 'down' }` and nothing more. The message
|
|
31
|
+
is written to Nest's `Logger` instead, so the diagnostic survives in a channel
|
|
32
|
+
that already has access control rather than being lost.
|
|
33
|
+
|
|
34
|
+
`health.exposeIndicatorErrors` (default `false`) puts it back in the response
|
|
35
|
+
for local debugging — the same shape, and the same warning, as
|
|
36
|
+
`envelope.exposeInternals`. The library made opposite choices about the same
|
|
37
|
+
risk in two places; they now agree.
|
|
38
|
+
|
|
39
|
+
`timedOutAfterMs` is unaffected: that number is one this library chose, not text
|
|
40
|
+
an indicator produced.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **The Health and Security Model sections describe the split**, and the
|
|
45
|
+
configuration table documents the new option. The previous text said an
|
|
46
|
+
indicator "cannot leak more than it already chose to put in a message", which
|
|
47
|
+
assigned a choice the indicator's author usually never makes.
|
|
48
|
+
|
|
14
49
|
## [1.0.0] - 2026-08-03
|
|
15
50
|
|
|
16
51
|
First published release. Everything below ships in it.
|
|
@@ -73,5 +108,6 @@ have regressed from. They are kept because the reasoning is worth having.
|
|
|
73
108
|
cleanly and silently. Corrected before the first publish, so no released version
|
|
74
109
|
ever carried the permissive range. No runtime behaviour changed.
|
|
75
110
|
|
|
111
|
+
[1.0.1]: https://github.com/bymaxone/nest-core/compare/v1.0.0...v1.0.1
|
|
76
112
|
[1.0.0]: https://github.com/bymaxone/nest-core/releases/tag/v1.0.0
|
|
77
113
|
[Unreleased]: https://github.com/bymaxone/nest-core/compare/v1.0.0...HEAD
|
package/README.md
CHANGED
|
@@ -199,11 +199,12 @@ falls back to the documented default. Pass only what you want to change.
|
|
|
199
199
|
|
|
200
200
|
### `health`
|
|
201
201
|
|
|
202
|
-
| Option
|
|
203
|
-
|
|
|
204
|
-
| `enabled`
|
|
205
|
-
| `path`
|
|
206
|
-
| `indicatorTimeoutMs`
|
|
202
|
+
| Option | Type | Default | Description |
|
|
203
|
+
| ----------------------- | --------- | ---------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `enabled` | `boolean` | `true` | Registers the health controller. |
|
|
205
|
+
| `path` | `string` | `'health'` | Route prefix: `GET /<path>/live`, `GET /<path>/ready`. |
|
|
206
|
+
| `indicatorTimeoutMs` | `number` | `5000` | Per-indicator timeout before a check reports down. |
|
|
207
|
+
| `exposeIndicatorErrors` | `boolean` | `false` | Includes the failing indicator's message in the response under `details.error`. Never enable in production — see below. |
|
|
207
208
|
|
|
208
209
|
On `forRoot`, `enabled` and `path` are applied at module-definition time: a
|
|
209
210
|
disabled feature registers no controller, and a custom `path` mounts the routes.
|
|
@@ -391,6 +392,12 @@ Liveness always replies `200` with an empty checks array; readiness runs
|
|
|
391
392
|
every registered indicator concurrently and replies `200` only when every
|
|
392
393
|
indicator reports `up`, `503` otherwise, naming every check either way.
|
|
393
394
|
|
|
395
|
+
A failing indicator is named but not quoted: the response says which check is
|
|
396
|
+
down, and the reason goes to the logger. See
|
|
397
|
+
[the security model](#-security-model) for why, and
|
|
398
|
+
`health.exposeIndicatorErrors` if you want the message in the response while
|
|
399
|
+
debugging locally.
|
|
400
|
+
|
|
394
401
|
```json
|
|
395
402
|
{ "status": "ok", "checks": [{ "name": "redis", "status": "up" }] }
|
|
396
403
|
```
|
|
@@ -558,12 +565,28 @@ original message and stack are captured for your logger, not for the response.
|
|
|
558
565
|
`envelope.exposeInternals` puts them in the body and exists for local debugging — its own
|
|
559
566
|
documentation says never to enable it in production, and it defaults to `false`.
|
|
560
567
|
|
|
561
|
-
###
|
|
568
|
+
### The readiness response names the failure, it does not describe it
|
|
569
|
+
|
|
570
|
+
A failing indicator produces `{ name, status: 'down' }` and nothing else. The reason goes
|
|
571
|
+
to the logger.
|
|
572
|
+
|
|
573
|
+
That split is deliberate. Readiness is usually unauthenticated and reachable by whatever
|
|
574
|
+
probes it, and an indicator rarely authors its own failure text — it writes
|
|
575
|
+
`await this.redis.ping()` and lets the driver's error propagate. Driver errors carry hosts,
|
|
576
|
+
ports, and in the case of a connection string, credentials. Putting that text in the
|
|
577
|
+
response publishes it to everyone who can reach the endpoint; putting it in the log keeps
|
|
578
|
+
it where access is already controlled, without losing the diagnostic.
|
|
579
|
+
|
|
580
|
+
`health.exposeIndicatorErrors` puts the message back in the response for local debugging.
|
|
581
|
+
It defaults to `false`, and its documentation says the same thing `envelope.exposeInternals`
|
|
582
|
+
does: never enable it in production. The two options are the same decision, made the same
|
|
583
|
+
way, about the same risk.
|
|
562
584
|
|
|
563
|
-
|
|
564
|
-
only — never the raw error, its stack, or a nested cause —
|
|
565
|
-
|
|
566
|
-
|
|
585
|
+
What reaches the log is bounded the same way it always was: the top-level `Error#message`
|
|
586
|
+
only — never the raw error, its stack, or a nested cause — truncated at 300 characters.
|
|
587
|
+
A slow indicator is converted to `down` by the aggregator rather than hanging the probe,
|
|
588
|
+
and its `timedOutAfterMs` stays in the response either way, because that number is one this
|
|
589
|
+
library chose rather than text an indicator produced.
|
|
567
590
|
|
|
568
591
|
### Cursors are opaque, not secret
|
|
569
592
|
|
|
@@ -580,16 +603,16 @@ guard you would apply to any internal endpoint, or keep it off the public listen
|
|
|
580
603
|
|
|
581
604
|
## 🛡️ Security Table
|
|
582
605
|
|
|
583
|
-
| Layer | Implementation
|
|
584
|
-
| ------------------ |
|
|
585
|
-
| Error responses | One shape for everything; unknown errors become a generic 500
|
|
586
|
-
| Internals | Message and stack captured for logging, in the body only under `exposeInternals` (default `false`)
|
|
587
|
-
| Health output |
|
|
588
|
-
| Slow indicators | Converted to `down` by the aggregator, so a probe cannot hang on one
|
|
589
|
-
| Correlation | Resolved through `BYMAX_CORRELATION_PROVIDER` — the app decides where the id comes from
|
|
590
|
-
| Pagination cursors | Opaque, not authenticated; treated as client-supplied input on the way back in
|
|
591
|
-
| Metrics | Opt-in; `prom-client` never imported while it is off
|
|
592
|
-
| Supply chain | `dependencies: {}`; third-party Actions pinned by commit SHA (org-internal reusables by tag); CodeQL and OpenSSF Scorecard
|
|
606
|
+
| Layer | Implementation |
|
|
607
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
608
|
+
| Error responses | One shape for everything; unknown errors become a generic 500 |
|
|
609
|
+
| Internals | Message and stack captured for logging, in the body only under `exposeInternals` (default `false`) |
|
|
610
|
+
| Health output | The response names which indicator is down and nothing more; the reason goes to the logger. `exposeIndicatorErrors` (default `false`) puts it back in the response for debugging |
|
|
611
|
+
| Slow indicators | Converted to `down` by the aggregator, so a probe cannot hang on one |
|
|
612
|
+
| Correlation | Resolved through `BYMAX_CORRELATION_PROVIDER` — the app decides where the id comes from |
|
|
613
|
+
| Pagination cursors | Opaque, not authenticated; treated as client-supplied input on the way back in |
|
|
614
|
+
| Metrics | Opt-in; `prom-client` never imported while it is off |
|
|
615
|
+
| Supply chain | `dependencies: {}`; third-party Actions pinned by commit SHA (org-internal reusables by tag); CodeQL and OpenSSF Scorecard |
|
|
593
616
|
|
|
594
617
|
> [!IMPORTANT]
|
|
595
618
|
> **`exposeInternals` is a debugging switch, not a verbosity setting.** With it on,
|
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ function resolveHealth(raw) {
|
|
|
44
44
|
return {
|
|
45
45
|
enabled: raw?.enabled ?? true,
|
|
46
46
|
path: raw?.path ?? DEFAULT_HEALTH_PATH,
|
|
47
|
+
exposeIndicatorErrors: raw?.exposeIndicatorErrors ?? false,
|
|
47
48
|
indicatorTimeoutMs: raw?.indicatorTimeoutMs ?? DEFAULT_INDICATOR_TIMEOUT_MS
|
|
48
49
|
};
|
|
49
50
|
}
|
|
@@ -529,18 +530,18 @@ function summarizeRejection(reason) {
|
|
|
529
530
|
}
|
|
530
531
|
return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH - TRUNCATION_ELLIPSIS.length)}${TRUNCATION_ELLIPSIS}`;
|
|
531
532
|
}
|
|
532
|
-
async function runIndicator(indicator, timeoutMs) {
|
|
533
|
+
async function runIndicator(indicator, timeoutMs, exposeErrors, logger) {
|
|
533
534
|
let timer;
|
|
534
535
|
const timedOut = new Promise((resolve) => {
|
|
535
536
|
timer = setTimeout(() => {
|
|
536
537
|
resolve({ name: indicator.name, status: "down", details: { timedOutAfterMs: timeoutMs } });
|
|
537
538
|
}, timeoutMs);
|
|
538
539
|
});
|
|
539
|
-
const checked = Promise.resolve().then(() => indicator.check()).then((result) => ({ ...result, name: indicator.name })).catch((reason) =>
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
details: { error:
|
|
543
|
-
})
|
|
540
|
+
const checked = Promise.resolve().then(() => indicator.check()).then((result) => ({ ...result, name: indicator.name })).catch((reason) => {
|
|
541
|
+
const message = summarizeRejection(reason);
|
|
542
|
+
logger.warn(`Health indicator "${indicator.name}" reported down: ${message}`);
|
|
543
|
+
return exposeErrors ? { name: indicator.name, status: "down", details: { error: message } } : { name: indicator.name, status: "down" };
|
|
544
|
+
});
|
|
544
545
|
try {
|
|
545
546
|
return await Promise.race([checked, timedOut]);
|
|
546
547
|
} finally {
|
|
@@ -559,6 +560,12 @@ var HealthService = class {
|
|
|
559
560
|
constructor(indicators = [], options) {
|
|
560
561
|
this.indicators = indicators;
|
|
561
562
|
this.options = options;
|
|
563
|
+
/**
|
|
564
|
+
* Nest's own logger, scoped to this class. The failure reason of a `down`
|
|
565
|
+
* indicator is written here rather than into the HTTP response, so the
|
|
566
|
+
* diagnostic survives without being served to whoever can reach the probe.
|
|
567
|
+
*/
|
|
568
|
+
this.logger = new common.Logger(HealthService.name);
|
|
562
569
|
}
|
|
563
570
|
/**
|
|
564
571
|
* Liveness check: the process is up and able to respond. Runs no
|
|
@@ -578,8 +585,11 @@ var HealthService = class {
|
|
|
578
585
|
*/
|
|
579
586
|
async checkReadiness() {
|
|
580
587
|
const timeoutMs = this.options.health.indicatorTimeoutMs;
|
|
588
|
+
const exposeErrors = this.options.health.exposeIndicatorErrors;
|
|
581
589
|
const checks = await Promise.all(
|
|
582
|
-
this.indicators.map(
|
|
590
|
+
this.indicators.map(
|
|
591
|
+
(indicator) => runIndicator(indicator, timeoutMs, exposeErrors, this.logger)
|
|
592
|
+
)
|
|
583
593
|
);
|
|
584
594
|
const status = checks.every((check) => check.status === "up") ? "ok" : "error";
|
|
585
595
|
return { status, checks };
|
package/dist/index.d.cts
CHANGED
|
@@ -35,6 +35,18 @@ interface HealthOptions {
|
|
|
35
35
|
path?: string;
|
|
36
36
|
/** Per-indicator timeout before a check is reported as down. Default: `5000`. */
|
|
37
37
|
indicatorTimeoutMs?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Include the failing indicator's message in the readiness response under
|
|
40
|
+
* `details.error`. Never enable in production. Default: `false`.
|
|
41
|
+
*
|
|
42
|
+
* Readiness is typically unauthenticated and reachable by whatever probes it,
|
|
43
|
+
* and an indicator usually does not author its own failure message — it lets a
|
|
44
|
+
* driver's error propagate, and driver errors carry hosts, ports and sometimes
|
|
45
|
+
* credentials. With this off, the response names which indicator is down and
|
|
46
|
+
* nothing else; the message goes to the logger, where access is already
|
|
47
|
+
* controlled.
|
|
48
|
+
*/
|
|
49
|
+
exposeIndicatorErrors?: boolean;
|
|
38
50
|
}
|
|
39
51
|
/** Prometheus metrics endpoint configuration. */
|
|
40
52
|
interface MetricsOptions {
|
|
@@ -76,6 +88,7 @@ interface ResolvedHealthOptions {
|
|
|
76
88
|
enabled: boolean;
|
|
77
89
|
path: string;
|
|
78
90
|
indicatorTimeoutMs: number;
|
|
91
|
+
exposeIndicatorErrors: boolean;
|
|
79
92
|
}
|
|
80
93
|
/** Fully-resolved metrics options. */
|
|
81
94
|
interface ResolvedMetricsOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,18 @@ interface HealthOptions {
|
|
|
35
35
|
path?: string;
|
|
36
36
|
/** Per-indicator timeout before a check is reported as down. Default: `5000`. */
|
|
37
37
|
indicatorTimeoutMs?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Include the failing indicator's message in the readiness response under
|
|
40
|
+
* `details.error`. Never enable in production. Default: `false`.
|
|
41
|
+
*
|
|
42
|
+
* Readiness is typically unauthenticated and reachable by whatever probes it,
|
|
43
|
+
* and an indicator usually does not author its own failure message — it lets a
|
|
44
|
+
* driver's error propagate, and driver errors carry hosts, ports and sometimes
|
|
45
|
+
* credentials. With this off, the response names which indicator is down and
|
|
46
|
+
* nothing else; the message goes to the logger, where access is already
|
|
47
|
+
* controlled.
|
|
48
|
+
*/
|
|
49
|
+
exposeIndicatorErrors?: boolean;
|
|
38
50
|
}
|
|
39
51
|
/** Prometheus metrics endpoint configuration. */
|
|
40
52
|
interface MetricsOptions {
|
|
@@ -76,6 +88,7 @@ interface ResolvedHealthOptions {
|
|
|
76
88
|
enabled: boolean;
|
|
77
89
|
path: string;
|
|
78
90
|
indicatorTimeoutMs: number;
|
|
91
|
+
exposeIndicatorErrors: boolean;
|
|
79
92
|
}
|
|
80
93
|
/** Fully-resolved metrics options. */
|
|
81
94
|
interface ResolvedMetricsOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Catch, Inject, Optional, Injectable, ConfigurableModuleBuilder, Module, HttpException, Get, Res, Controller, HttpStatus } from '@nestjs/common';
|
|
1
|
+
import { Catch, Inject, Optional, Injectable, ConfigurableModuleBuilder, Module, HttpException, Logger, Get, Res, Controller, HttpStatus } from '@nestjs/common';
|
|
2
2
|
import { HttpAdapterHost, BaseExceptionFilter, APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
|
|
3
3
|
import { tap, catchError, throwError } from 'rxjs';
|
|
4
4
|
|
|
@@ -42,6 +42,7 @@ function resolveHealth(raw) {
|
|
|
42
42
|
return {
|
|
43
43
|
enabled: raw?.enabled ?? true,
|
|
44
44
|
path: raw?.path ?? DEFAULT_HEALTH_PATH,
|
|
45
|
+
exposeIndicatorErrors: raw?.exposeIndicatorErrors ?? false,
|
|
45
46
|
indicatorTimeoutMs: raw?.indicatorTimeoutMs ?? DEFAULT_INDICATOR_TIMEOUT_MS
|
|
46
47
|
};
|
|
47
48
|
}
|
|
@@ -527,18 +528,18 @@ function summarizeRejection(reason) {
|
|
|
527
528
|
}
|
|
528
529
|
return `${message.slice(0, MAX_ERROR_MESSAGE_LENGTH - TRUNCATION_ELLIPSIS.length)}${TRUNCATION_ELLIPSIS}`;
|
|
529
530
|
}
|
|
530
|
-
async function runIndicator(indicator, timeoutMs) {
|
|
531
|
+
async function runIndicator(indicator, timeoutMs, exposeErrors, logger) {
|
|
531
532
|
let timer;
|
|
532
533
|
const timedOut = new Promise((resolve) => {
|
|
533
534
|
timer = setTimeout(() => {
|
|
534
535
|
resolve({ name: indicator.name, status: "down", details: { timedOutAfterMs: timeoutMs } });
|
|
535
536
|
}, timeoutMs);
|
|
536
537
|
});
|
|
537
|
-
const checked = Promise.resolve().then(() => indicator.check()).then((result) => ({ ...result, name: indicator.name })).catch((reason) =>
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
details: { error:
|
|
541
|
-
})
|
|
538
|
+
const checked = Promise.resolve().then(() => indicator.check()).then((result) => ({ ...result, name: indicator.name })).catch((reason) => {
|
|
539
|
+
const message = summarizeRejection(reason);
|
|
540
|
+
logger.warn(`Health indicator "${indicator.name}" reported down: ${message}`);
|
|
541
|
+
return exposeErrors ? { name: indicator.name, status: "down", details: { error: message } } : { name: indicator.name, status: "down" };
|
|
542
|
+
});
|
|
542
543
|
try {
|
|
543
544
|
return await Promise.race([checked, timedOut]);
|
|
544
545
|
} finally {
|
|
@@ -557,6 +558,12 @@ var HealthService = class {
|
|
|
557
558
|
constructor(indicators = [], options) {
|
|
558
559
|
this.indicators = indicators;
|
|
559
560
|
this.options = options;
|
|
561
|
+
/**
|
|
562
|
+
* Nest's own logger, scoped to this class. The failure reason of a `down`
|
|
563
|
+
* indicator is written here rather than into the HTTP response, so the
|
|
564
|
+
* diagnostic survives without being served to whoever can reach the probe.
|
|
565
|
+
*/
|
|
566
|
+
this.logger = new Logger(HealthService.name);
|
|
560
567
|
}
|
|
561
568
|
/**
|
|
562
569
|
* Liveness check: the process is up and able to respond. Runs no
|
|
@@ -576,8 +583,11 @@ var HealthService = class {
|
|
|
576
583
|
*/
|
|
577
584
|
async checkReadiness() {
|
|
578
585
|
const timeoutMs = this.options.health.indicatorTimeoutMs;
|
|
586
|
+
const exposeErrors = this.options.health.exposeIndicatorErrors;
|
|
579
587
|
const checks = await Promise.all(
|
|
580
|
-
this.indicators.map(
|
|
588
|
+
this.indicators.map(
|
|
589
|
+
(indicator) => runIndicator(indicator, timeoutMs, exposeErrors, this.logger)
|
|
590
|
+
)
|
|
581
591
|
);
|
|
582
592
|
const status = checks.every((check) => check.status === "up") ? "ok" : "error";
|
|
583
593
|
return { status, checks };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bymax-one/nest-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Zero-dependency NestJS 11 application foundation kit: error-envelope exception filter, request-timing interceptor, pagination helpers, health endpoints, and an optional Prometheus metrics endpoint.",
|
|
5
5
|
"author": "Bymax One <support@bymax.one>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,6 +55,29 @@
|
|
|
55
55
|
},
|
|
56
56
|
"./package.json": "./package.json"
|
|
57
57
|
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "pnpm clean && tsup",
|
|
60
|
+
"check:exports": "attw --pack . --profile strict",
|
|
61
|
+
"check:published": "node scripts/check-published-surface.mjs",
|
|
62
|
+
"check:runtime": "node scripts/check-consumer-runtime.mjs",
|
|
63
|
+
"clean": "node -e \"const fs=require('node:fs');for(const d of ['dist','coverage'])fs.rmSync(d,{recursive:true,force:true})\"",
|
|
64
|
+
"dogfood": "node scripts/dogfood-smoke-test.mjs",
|
|
65
|
+
"lint": "eslint --no-error-on-unmatched-pattern src scripts test",
|
|
66
|
+
"lint:fix": "eslint --no-error-on-unmatched-pattern src scripts test --fix",
|
|
67
|
+
"mutation": "stryker run",
|
|
68
|
+
"mutation:dry-run": "stryker run --dryRunOnly",
|
|
69
|
+
"mutation:incremental": "stryker run --incremental",
|
|
70
|
+
"prepare": "husky",
|
|
71
|
+
"prepublishOnly": "pnpm clean && pnpm typecheck && pnpm lint && pnpm test:cov:all && pnpm build && pnpm size && pnpm check:published",
|
|
72
|
+
"release": "npm publish --provenance --access public",
|
|
73
|
+
"size": "node scripts/check-size.mjs",
|
|
74
|
+
"test": "jest",
|
|
75
|
+
"test:cov": "jest --coverage",
|
|
76
|
+
"test:cov:all": "jest --config jest.coverage.config.ts --coverage",
|
|
77
|
+
"test:e2e": "jest --config jest.e2e.config.ts",
|
|
78
|
+
"test:watch": "jest --watch",
|
|
79
|
+
"typecheck": "tsc --noEmit"
|
|
80
|
+
},
|
|
58
81
|
"lint-staged": {
|
|
59
82
|
"*.{ts,tsx,js,mjs,cjs}": [
|
|
60
83
|
"eslint --fix",
|
|
@@ -126,6 +149,7 @@
|
|
|
126
149
|
"metrics",
|
|
127
150
|
"typescript"
|
|
128
151
|
],
|
|
152
|
+
"packageManager": "pnpm@10.8.1",
|
|
129
153
|
"engines": {
|
|
130
154
|
"node": ">=24.0.0"
|
|
131
155
|
},
|
|
@@ -134,6 +158,16 @@
|
|
|
134
158
|
"provenance": true,
|
|
135
159
|
"registry": "https://registry.npmjs.org/"
|
|
136
160
|
},
|
|
161
|
+
"pnpm": {
|
|
162
|
+
"overrides": {
|
|
163
|
+
"brace-expansion@1": "^1.1.18",
|
|
164
|
+
"brace-expansion@2": "^2.1.4",
|
|
165
|
+
"brace-expansion@5": "^5.0.9",
|
|
166
|
+
"esbuild": "^0.28.1",
|
|
167
|
+
"fast-uri": "^3.1.5",
|
|
168
|
+
"qs": "^6.15.2"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
137
171
|
"module": "./dist/index.mjs",
|
|
138
172
|
"typesVersions": {
|
|
139
173
|
"*": {
|
|
@@ -144,26 +178,5 @@
|
|
|
144
178
|
"./dist/health/index.d.cts"
|
|
145
179
|
]
|
|
146
180
|
}
|
|
147
|
-
},
|
|
148
|
-
"scripts": {
|
|
149
|
-
"build": "pnpm clean && tsup",
|
|
150
|
-
"check:exports": "attw --pack . --profile strict",
|
|
151
|
-
"check:published": "node scripts/check-published-surface.mjs",
|
|
152
|
-
"check:runtime": "node scripts/check-consumer-runtime.mjs",
|
|
153
|
-
"clean": "node -e \"const fs=require('node:fs');for(const d of ['dist','coverage'])fs.rmSync(d,{recursive:true,force:true})\"",
|
|
154
|
-
"dogfood": "node scripts/dogfood-smoke-test.mjs",
|
|
155
|
-
"lint": "eslint --no-error-on-unmatched-pattern src scripts test",
|
|
156
|
-
"lint:fix": "eslint --no-error-on-unmatched-pattern src scripts test --fix",
|
|
157
|
-
"mutation": "stryker run",
|
|
158
|
-
"mutation:dry-run": "stryker run --dryRunOnly",
|
|
159
|
-
"mutation:incremental": "stryker run --incremental",
|
|
160
|
-
"release": "npm publish --provenance --access public",
|
|
161
|
-
"size": "node scripts/check-size.mjs",
|
|
162
|
-
"test": "jest",
|
|
163
|
-
"test:cov": "jest --coverage",
|
|
164
|
-
"test:cov:all": "jest --config jest.coverage.config.ts --coverage",
|
|
165
|
-
"test:e2e": "jest --config jest.e2e.config.ts",
|
|
166
|
-
"test:watch": "jest --watch",
|
|
167
|
-
"typecheck": "tsc --noEmit"
|
|
168
181
|
}
|
|
169
|
-
}
|
|
182
|
+
}
|