@phil-r/stats 2.0.0 → 2.1.0

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/README.md CHANGED
@@ -80,15 +80,30 @@ See [Nest examples and integration details](./docs/guide.md) or
80
80
 
81
81
  ## Collector API
82
82
 
83
- `createStats(options?)` returns an independent collector. All options default to
84
- false (or `[]`):
83
+ `createStats(options?)` returns an independent collector. Features are disabled
84
+ by default; paths are unchanged unless normalized or grouped:
85
85
 
86
86
  - `endpointStats`: per-method/path totals, excluding query strings.
87
+ - `endpointNormalizer`: synchronous `(pathname: string) => string`, called only
88
+ with `endpointStats` enabled. Receives the query-free pathname before
89
+ `complexEndpoints` matching and counter allocation; HTTP methods stay separate.
90
+ Exceptions are not caught, so keep the callback non-throwing.
87
91
  - `complexEndpoints`: ordered regexparam patterns, e.g. `['/user/:id']`; first
88
92
  match groups dynamic URLs. Use with `endpointStats` to limit unique keys.
89
93
  - `customStats`: include named custom measurements in output.
90
94
  - `addHeader`: add `X-Response-Time: Nms`, preserving existing values.
91
95
 
96
+ Normalize noisy bot paths before allocating counters, alongside dynamic routes:
97
+
98
+ ```ts
99
+ const stats = createStats({
100
+ endpointStats: true,
101
+ endpointNormalizer: (pathname) =>
102
+ pathname.endsWith('.php') ? '/*.php' : pathname,
103
+ complexEndpoints: ['/user/:id'],
104
+ });
105
+ ```
106
+
92
107
  `stats.getStats()` returns request count, total/average milliseconds, status
93
108
  counts, process identity/uptime and enabled endpoint/custom totals. Nested maps
94
109
  are live; treat them as read-only. Counters are process-local and unbounded.
@@ -109,4 +124,4 @@ precise adapter timing, supported versions, migration and development commands.
109
124
 
110
125
  ## License
111
126
 
112
- [MIT](LICENSE). Fork of zenmate/stats.
127
+ [MIT](LICENSE).
package/dist/index.js CHANGED
@@ -66,6 +66,8 @@ function createStats(opts = {}) {
66
66
  if (!opts.endpointStats)
67
67
  return;
68
68
  let path = url.startsWith("/") ? url.split(/[?#]/, 1)[0] : new URL(url, "http://localhost").pathname;
69
+ if (opts.endpointNormalizer)
70
+ path = opts.endpointNormalizer(path);
69
71
  const complexPath = complexEndpoints.find((endpoint) => endpoint.pattern.test(path));
70
72
  path = complexPath ? complexPath.path : path;
71
73
  const endpoint = `${method} ${path}`;
package/dist/types.d.ts CHANGED
@@ -1,7 +1,13 @@
1
- /** Collection features; every option defaults to false or an empty list. */
1
+ /** Collection features; disabled by default, with paths unchanged. */
2
2
  export interface StatsOptions {
3
3
  /** Include counters keyed by HTTP method and path, excluding query strings. */
4
4
  endpointStats?: boolean;
5
+ /**
6
+ * Normalize the query-free pathname before complexEndpoints matching and storage.
7
+ * Requires endpointStats; omitted means identity. HTTP methods remain separate.
8
+ * Must return a string synchronously; exceptions are not caught.
9
+ */
10
+ endpointNormalizer?: (pathname: string) => string;
5
11
  /** Ordered regexparam patterns; first match groups dynamic URLs. Requires endpointStats. */
6
12
  complexEndpoints?: string[];
7
13
  /** Include custom measurements in getStats(); measurement methods always work. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phil-r/stats",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "license": "MIT",
5
5
  "author": "Phil Rukin <philipp@rukin.me> (https://rukin.me)",
6
6
  "description": "Request statistics middleware",