@produtype/core 1.20.0 → 1.21.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.
|
@@ -33,15 +33,57 @@ const LOGGING_PACKAGES = [
|
|
|
33
33
|
];
|
|
34
34
|
/** Elixir reaches for a backend rather than a logger: Logger itself is in OTP. */
|
|
35
35
|
const ELIXIR_LOGGING_PACKAGES = ['logger_json', 'logger_file_backend', 'sentry'];
|
|
36
|
+
/**
|
|
37
|
+
* The route, in every spelling somebody writes it.
|
|
38
|
+
*
|
|
39
|
+
* `\/(health|healthz|readyz|livez|alive)\b` does not match `/healthcheck`: after
|
|
40
|
+
* `health` comes a `c`, and the boundary fails. Netflix's dispatch declares
|
|
41
|
+
* `@api_router.get("/healthcheck")` and was told it has no health endpoint — the
|
|
42
|
+
* plainest spelling there is, missed by the pattern meant to find it.
|
|
43
|
+
*
|
|
44
|
+
* This does not disturb the decision recorded below about Dropwizard. That one is
|
|
45
|
+
* about an endpoint the *framework* publishes, which no Dropwizard application writes
|
|
46
|
+
* in its own source; a line like dispatch's is somebody declaring the route
|
|
47
|
+
* themselves.
|
|
48
|
+
*/
|
|
49
|
+
const HEALTH_ROUTE = /\/(health|healthz|healthcheck|health[-_]check|readyz|livez|alive)\b/i;
|
|
36
50
|
async function detectObservability(ctx) {
|
|
37
51
|
const evidence = [];
|
|
38
|
-
|
|
52
|
+
/**
|
|
53
|
+
* .NET names its logging in the project file and nothing here was reading it.
|
|
54
|
+
*
|
|
55
|
+
* Radarr declares `NLog`, `NLog.Extensions.Logging` and
|
|
56
|
+
* `NLog.Layouts.ClefJsonLayout` — CLEF being the compact JSON event format — and
|
|
57
|
+
* was reported as logging something but not structurally. The npm list has had
|
|
58
|
+
* winston and pino since the beginning and treats the dependency alone as enough;
|
|
59
|
+
* these are the same statement in another manifest.
|
|
60
|
+
*/
|
|
61
|
+
const DOTNET_LOGGING_PACKAGES = ['NLog', 'NLog.Extensions.Logging', 'Serilog', 'Serilog.AspNetCore', 'Microsoft.Extensions.Logging', 'log4net'];
|
|
62
|
+
const logDeps = [
|
|
63
|
+
...(0, detectContext_1.hasAnyDep)(ctx, LOGGING_PACKAGES),
|
|
64
|
+
...(0, detectContext_1.hasAnyElixirDep)(ctx, ELIXIR_LOGGING_PACKAGES),
|
|
65
|
+
...(0, detectContext_1.hasAnyDotnetDep)(ctx, DOTNET_LOGGING_PACKAGES),
|
|
66
|
+
];
|
|
39
67
|
const sentryDeps = (0, detectContext_1.hasAnyDep)(ctx, ['@sentry/node', 'sentry-sdk']);
|
|
40
68
|
for (const d of logDeps)
|
|
41
69
|
evidence.push({ type: 'dependency', value: d, claim: 'logging' });
|
|
42
70
|
for (const d of sentryDeps)
|
|
43
71
|
evidence.push({ type: 'dependency', value: d, claim: 'logging' });
|
|
44
|
-
|
|
72
|
+
/**
|
|
73
|
+
* An import path is not a route.
|
|
74
|
+
*
|
|
75
|
+
* `import Health from 'typings/Health';` is a TypeScript type in Radarr's frontend,
|
|
76
|
+
* and `/Health'` matches the route pattern exactly as `/health` in a URL does. It
|
|
77
|
+
* was the evidence behind Radarr's passing health check — a false pass, which is
|
|
78
|
+
* the direction that raises a score rather than lowering one.
|
|
79
|
+
*
|
|
80
|
+
* Imports are good evidence elsewhere and are left alone there; this is the search
|
|
81
|
+
* for a *route*, and a module specifier is never one. Filtered inside the search so
|
|
82
|
+
* that a frontend full of them cannot spend the budget before a real route is
|
|
83
|
+
* reached.
|
|
84
|
+
*/
|
|
85
|
+
const IMPORT_LINE = /^\s*(?:import\b|from\s+['"]|const\s+\{?[\w\s,}]*\}?\s*=\s*require\()/;
|
|
86
|
+
const hits = await (0, textSearch_1.searchInFiles)(ctx.root, ctx.files.source, [HEALTH_ROUTE, /x-request-id/i, /correlation-id/i, /error\s*handler/i, /RotatingFileHandler/i], 25, (match) => !IMPORT_LINE.test(match.snippet));
|
|
45
87
|
/**
|
|
46
88
|
* One search, three answers, so the claim is decided per hit rather than per search.
|
|
47
89
|
*
|
|
@@ -50,7 +92,7 @@ async function detectObservability(ctx) {
|
|
|
50
92
|
* is what the line is evidence of.
|
|
51
93
|
*/
|
|
52
94
|
for (const m of hits) {
|
|
53
|
-
const claim =
|
|
95
|
+
const claim = HEALTH_ROUTE.test(m.snippet)
|
|
54
96
|
? 'health'
|
|
55
97
|
: /x-request-id|correlation-id/i.test(m.snippet)
|
|
56
98
|
? 'request-id'
|
|
@@ -116,7 +158,7 @@ async function detectObservability(ctx) {
|
|
|
116
158
|
const hasHealth = declaredProbes.length > 0
|
|
117
159
|
|| healthFiles.length > 0
|
|
118
160
|
|| actuator.length > 0
|
|
119
|
-
|| hits.some((h) =>
|
|
161
|
+
|| hits.some((h) => HEALTH_ROUTE.test(h.snippet));
|
|
120
162
|
const hasReqId = hits.some((h) => /x-request-id|correlation-id/i.test(h.snippet));
|
|
121
163
|
// Structured logging without a logging library is still structured logging. What
|
|
122
164
|
// matters is that entries are machine-readable and correlated, not which package
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.1",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|