@selvajs/server 1.0.0-beta.9 → 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.
Files changed (91) hide show
  1. package/README.md +18 -20
  2. package/dist/access/project-access-input.d.ts +13 -16
  3. package/dist/access/project-access-input.d.ts.map +1 -1
  4. package/dist/access/project-access-input.js +2 -2
  5. package/dist/access/project-access-input.js.map +1 -1
  6. package/dist/compute/idempotency.d.ts +18 -22
  7. package/dist/compute/idempotency.d.ts.map +1 -1
  8. package/dist/compute/idempotency.js +10 -14
  9. package/dist/compute/idempotency.js.map +1 -1
  10. package/dist/compute/index.d.ts.map +1 -1
  11. package/dist/compute/index.js +3 -5
  12. package/dist/compute/index.js.map +1 -1
  13. package/dist/compute/limits.d.ts +70 -86
  14. package/dist/compute/limits.d.ts.map +1 -1
  15. package/dist/compute/limits.js +27 -56
  16. package/dist/compute/limits.js.map +1 -1
  17. package/dist/compute/rate-limit.d.ts +41 -53
  18. package/dist/compute/rate-limit.d.ts.map +1 -1
  19. package/dist/compute/rate-limit.js +27 -35
  20. package/dist/compute/rate-limit.js.map +1 -1
  21. package/dist/compute/remote-definition.d.ts +8 -22
  22. package/dist/compute/remote-definition.d.ts.map +1 -1
  23. package/dist/compute/remote-definition.js +9 -19
  24. package/dist/compute/remote-definition.js.map +1 -1
  25. package/dist/compute/safe-url.d.ts +7 -5
  26. package/dist/compute/safe-url.d.ts.map +1 -1
  27. package/dist/compute/safe-url.js +26 -28
  28. package/dist/compute/safe-url.js.map +1 -1
  29. package/dist/definitions/definition-service.d.ts +13 -38
  30. package/dist/definitions/definition-service.d.ts.map +1 -1
  31. package/dist/definitions/definition-service.js +25 -53
  32. package/dist/definitions/definition-service.js.map +1 -1
  33. package/dist/definitions/index.d.ts +2 -2
  34. package/dist/definitions/index.d.ts.map +1 -1
  35. package/dist/definitions/index.js +1 -5
  36. package/dist/definitions/index.js.map +1 -1
  37. package/dist/definitions/load-for-render.d.ts +23 -20
  38. package/dist/definitions/load-for-render.d.ts.map +1 -1
  39. package/dist/definitions/load-for-render.js +55 -41
  40. package/dist/definitions/load-for-render.js.map +1 -1
  41. package/dist/definitions/schema-extraction.d.ts +36 -16
  42. package/dist/definitions/schema-extraction.d.ts.map +1 -1
  43. package/dist/definitions/schema-extraction.js +65 -28
  44. package/dist/definitions/schema-extraction.js.map +1 -1
  45. package/dist/errors/SentryErrorReporter.d.ts +2 -3
  46. package/dist/errors/SentryErrorReporter.d.ts.map +1 -1
  47. package/dist/errors/SentryErrorReporter.js +10 -14
  48. package/dist/errors/SentryErrorReporter.js.map +1 -1
  49. package/dist/http/body-size.d.ts +8 -11
  50. package/dist/http/body-size.d.ts.map +1 -1
  51. package/dist/http/body-size.js +8 -11
  52. package/dist/http/body-size.js.map +1 -1
  53. package/dist/http/redirect.d.ts +3 -7
  54. package/dist/http/redirect.d.ts.map +1 -1
  55. package/dist/http/redirect.js +8 -8
  56. package/dist/http/redirect.js.map +1 -1
  57. package/dist/http/route-classifier.d.ts +20 -24
  58. package/dist/http/route-classifier.d.ts.map +1 -1
  59. package/dist/http/route-classifier.js +6 -7
  60. package/dist/http/route-classifier.js.map +1 -1
  61. package/dist/http/security-headers.d.ts +13 -15
  62. package/dist/http/security-headers.d.ts.map +1 -1
  63. package/dist/http/security-headers.js +10 -12
  64. package/dist/http/security-headers.js.map +1 -1
  65. package/dist/logging/PinoLogger.d.ts +8 -14
  66. package/dist/logging/PinoLogger.d.ts.map +1 -1
  67. package/dist/logging/PinoLogger.js +29 -35
  68. package/dist/logging/PinoLogger.js.map +1 -1
  69. package/dist/logging/requestId.d.ts +20 -28
  70. package/dist/logging/requestId.d.ts.map +1 -1
  71. package/dist/logging/requestId.js +29 -37
  72. package/dist/logging/requestId.js.map +1 -1
  73. package/dist/ops/index.d.ts.map +1 -1
  74. package/dist/ops/index.js +2 -1
  75. package/dist/ops/index.js.map +1 -1
  76. package/dist/ops/semver.d.ts +22 -24
  77. package/dist/ops/semver.d.ts.map +1 -1
  78. package/dist/ops/semver.js +22 -25
  79. package/dist/ops/semver.js.map +1 -1
  80. package/dist/providers/create-selva-providers.d.ts +19 -27
  81. package/dist/providers/create-selva-providers.d.ts.map +1 -1
  82. package/dist/providers/create-selva-providers.js +28 -34
  83. package/dist/providers/create-selva-providers.js.map +1 -1
  84. package/dist/providers/index.d.ts.map +1 -1
  85. package/dist/providers/index.js +2 -4
  86. package/dist/providers/index.js.map +1 -1
  87. package/dist/tokens/token-codec.d.ts +24 -27
  88. package/dist/tokens/token-codec.d.ts.map +1 -1
  89. package/dist/tokens/token-codec.js +18 -21
  90. package/dist/tokens/token-codec.js.map +1 -1
  91. package/package.json +5 -5
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Validate a user-supplied post-login redirect target. Accepts only same-origin
3
- * relative paths starting with `/` followed by a non-`/` character, so
4
- * `//evil.com/path` (protocol-relative URL — browser treats as cross-origin)
5
- * and `/\evil.com` (back-slash variants some browsers normalize) are rejected.
6
- *
7
- * Always returns a safe path: the validated target on success, the fallback
8
- * otherwise. Routes call this with `redirectTo` from form data or query string.
2
+ * Validate a user-supplied post-login redirect target (`redirectTo` from form
3
+ * data or a query string) against open redirects. Returns the target when it is
4
+ * a same-origin relative path, the fallback otherwise — never a raw target.
9
5
  */
10
6
  export function safeRedirectTarget(raw, fallback) {
7
+ // A valid target is `/` plus at least one more character, so the two guards
8
+ // below always have a real `raw[1]` to inspect.
11
9
  if (typeof raw !== 'string' || raw.length < 2)
12
10
  return fallback;
11
+ // Absolute URLs (`https://evil.com`) leave this origin.
13
12
  if (raw[0] !== '/')
14
13
  return fallback;
15
- // Reject protocol-relative (`//host`) and back-slash bypass (`/\host`).
14
+ // `//evil.com` is protocol-relative — a browser treats it as cross-origin —
15
+ // and some browsers normalize `/\evil.com` into the same thing.
16
16
  if (raw[1] === '/' || raw[1] === '\\')
17
17
  return fallback;
18
18
  return raw;
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/http/redirect.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA8B,EAAE,QAAgB;IAClF,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/D,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IACpC,wEAAwE;IACxE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACvD,OAAO,GAAG,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../src/http/redirect.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA8B,EAAE,QAAgB;IAClF,4EAA4E;IAC5E,gDAAgD;IAChD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/D,wDAAwD;IACxD,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAC;IACpC,4EAA4E;IAC5E,gEAAgE;IAChE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACvD,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -1,46 +1,42 @@
1
1
  /**
2
- * Deny-by-default route classification. Every request is gated unless its
3
- * path matches one of the explicit allowlists supplied here — so adding a new
4
- * top-level route is safe: forgetting to update the lists makes it gated (a
5
- * loud 401/redirect), not silently public.
2
+ * Deny-by-default route classification. A request is gated unless its path
3
+ * matches one of the allowlists supplied here, so forgetting to list a new
4
+ * route makes it gated — a loud 401/redirect, not silently public.
6
5
  *
7
- * The *pattern* is reusable; the *values* are app policy — a consuming app
8
- * supplies its own route sets. All matching is plain exact/prefix string
9
- * comparison: cheap enough for the hooks hot path, and no regex to get wrong.
6
+ * The pattern is reusable; the values are app policy, supplied by the consuming
7
+ * app. Matching is plain exact/prefix string comparison: cheap enough for the
8
+ * hooks hot path, and no regex to get wrong.
10
9
  */
11
10
  export interface RouteClassifierConfig {
12
- /** Exact-match public pages — no session needed (e.g. `/`, `/login`). */
11
+ /** Exact-match pages needing no session (e.g. `/`, `/login`). */
13
12
  publicPages?: Iterable<string>;
14
- /**
15
- * Public path prefixes (e.g. `/auth/` for OAuth start + callback — the
16
- * user has no session yet by definition).
17
- */
13
+ /** Public path prefixes (e.g. `/auth/` — OAuth start and callback precede any session). */
18
14
  publicPrefixes?: readonly string[];
19
15
  /**
20
- * Exact-match API endpoints that must answer without a session (e.g. a
21
- * load-balancer health probe). Anything added here must be safe to expose
22
- * to anonymous callers.
16
+ * Exact-match API endpoints that answer without a session (e.g. a
17
+ * load-balancer health probe). Anything listed here is exposed to anonymous
18
+ * callers.
23
19
  */
24
20
  publicApis?: Iterable<string>;
25
21
  /**
26
- * A single prefix whose routes apply their own per-request authorization
27
- * (e.g. a blob proxy classifying each asset path). The hook must NOT deny
28
- * these up front; they classify as public (best-effort session attach) and
29
- * the route makes the real decision. Keep this to ONE prefix — every other
30
- * API route stays deny-by-default.
22
+ * One prefix whose routes authorize each request themselves (e.g. a blob
23
+ * proxy classifying per asset path). These classify as public so the hook
24
+ * attaches a session best-effort and does not deny up front; the route makes
25
+ * the real decision. Keep it to ONE prefix — every other API route stays
26
+ * deny-by-default.
31
27
  */
32
28
  selfGatingPrefix?: string;
33
- /** Static-asset path prefixes the adapter serves directly (e.g. `/_app/`). */
29
+ /** Static-asset prefixes the adapter serves directly (e.g. `/_app/`). */
34
30
  staticPrefixes?: readonly string[];
35
31
  /** Exact static-asset paths (e.g. `/favicon.svg`, `/robots.txt`). */
36
32
  staticPaths?: Iterable<string>;
37
33
  }
38
34
  export interface RouteClassifier {
39
- /** Static assets bypass auth gating; callers may add cache headers. */
35
+ /** Matches `staticPaths`/`staticPrefixes`. These bypass auth gating entirely. */
40
36
  isStaticAsset(pathname: string): boolean;
41
- /** True for paths under `selfGatingPrefix` — the route gates itself. */
37
+ /** Matches `selfGatingPrefix`. The hook must not deny these — the route gates itself. */
42
38
  isSelfGatingApiRoute(pathname: string): boolean;
43
- /** True when the path needs no session: pages, APIs, prefixes, self-gating. */
39
+ /** Needs no session: a public page, API, prefix, or a self-gating route. */
44
40
  isPublicRoute(pathname: string): boolean;
45
41
  }
46
42
  export declare function createRouteClassifier(config: RouteClassifierConfig): RouteClassifier;
@@ -1 +1 @@
1
- {"version":3,"file":"route-classifier.d.ts","sourceRoot":"","sources":["../../src/http/route-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,qBAAqB;IACrC,yEAAyE;IACzE,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,qEAAqE;IACrE,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC/B,uEAAuE;IACvE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,wEAAwE;IACxE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,+EAA+E;IAC/E,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAyBpF"}
1
+ {"version":3,"file":"route-classifier.d.ts","sourceRoot":"","sources":["../../src/http/route-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,qBAAqB;IACrC,iEAAiE;IACjE,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,2FAA2F;IAC3F,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,qEAAqE;IACrE,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC/B,iFAAiF;IACjF,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,yFAAyF;IACzF,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,4EAA4E;IAC5E,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAyBpF"}
@@ -1,12 +1,11 @@
1
1
  /**
2
- * Deny-by-default route classification. Every request is gated unless its
3
- * path matches one of the explicit allowlists supplied here — so adding a new
4
- * top-level route is safe: forgetting to update the lists makes it gated (a
5
- * loud 401/redirect), not silently public.
2
+ * Deny-by-default route classification. A request is gated unless its path
3
+ * matches one of the allowlists supplied here, so forgetting to list a new
4
+ * route makes it gated — a loud 401/redirect, not silently public.
6
5
  *
7
- * The *pattern* is reusable; the *values* are app policy — a consuming app
8
- * supplies its own route sets. All matching is plain exact/prefix string
9
- * comparison: cheap enough for the hooks hot path, and no regex to get wrong.
6
+ * The pattern is reusable; the values are app policy, supplied by the consuming
7
+ * app. Matching is plain exact/prefix string comparison: cheap enough for the
8
+ * hooks hot path, and no regex to get wrong.
10
9
  */
11
10
  export function createRouteClassifier(config) {
12
11
  const publicPages = new Set(config.publicPages ?? []);
@@ -1 +1 @@
1
- {"version":3,"file":"route-classifier.js","sourceRoot":"","sources":["../../src/http/route-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuCH,MAAM,UAAU,qBAAqB,CAAC,MAA6B;IAClE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACjD,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACnD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAEtD,SAAS,aAAa,CAAC,QAAgB;QACtC,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,SAAS,oBAAoB,CAAC,QAAgB;QAC7C,OAAO,gBAAgB,KAAK,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAChF,CAAC;IAED,SAAS,aAAa,CAAC,QAAgB;QACtC,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,IAAI,oBAAoB,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAChD,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC;AAC/D,CAAC"}
1
+ {"version":3,"file":"route-classifier.js","sourceRoot":"","sources":["../../src/http/route-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,MAAM,UAAU,qBAAqB,CAAC,MAA6B;IAClE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACjD,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACnD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAEtD,SAAS,aAAa,CAAC,QAAgB;QACtC,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,SAAS,oBAAoB,CAAC,QAAgB;QAC7C,OAAO,gBAAgB,KAAK,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAChF,CAAC;IAED,SAAS,aAAa,CAAC,QAAgB;QACtC,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,IAAI,oBAAoB,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAChD,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC;AAC/D,CAAC"}
@@ -1,31 +1,29 @@
1
1
  export interface SecurityHeaderOptions {
2
2
  /**
3
- * Set `Strict-Transport-Security` — production only. Locking dev
4
- * (http://localhost) into HTTPS would brick local development, so the
5
- * caller decides what "production" means (e.g. `NODE_ENV`).
3
+ * Set `Strict-Transport-Security`. Production only — pinning
4
+ * http://localhost to HTTPS bricks local development. The caller decides
5
+ * what "production" means (e.g. `NODE_ENV`).
6
6
  */
7
7
  hsts: boolean;
8
8
  }
9
9
  /**
10
- * Apply baseline browser-hardening headers to a response. Cheap wins that
11
- * don't require UI verification:
10
+ * Apply baseline browser-hardening headers to a response and return it for
11
+ * chaining. Each is a cheap win that needs no UI verification:
12
12
  *
13
13
  * - X-Content-Type-Options: nosniff — disables MIME sniffing.
14
- * - Referrer-Policy: strict-origin-when-cross-origin — strips the path
15
- * and query from cross-origin Referer headers; matches modern browser
16
- * defaults but pins the behavior in case the default ever drifts.
17
- * - Permissions-Policy: opt out of browser APIs Selva-engine apps don't
18
- * use, so XSS can't enable them.
19
- * - HSTS when `opts.hsts` (production).
14
+ * - Referrer-Policy: strict-origin-when-cross-origin — strips path and query
15
+ * from cross-origin Referer headers. Matches modern browser defaults, but
16
+ * pins the behavior in case a default drifts.
17
+ * - Permissions-Policy: opts out of browser APIs Selva-engine apps don't use,
18
+ * so XSS can't enable them.
19
+ * - HSTS when `opts.hsts`.
20
20
  *
21
21
  * **Intentionally NOT set here:**
22
22
  * - Content-Security-Policy + frame-ancestors. Selva-engine apps are built
23
- * for iframe embedding, so a strict CSP needs UI-phase validation against
24
- * real consumer sites before it can ship.
23
+ * for iframe embedding, so a strict CSP needs validating against real
24
+ * consumer sites before it can ship.
25
25
  * - X-Frame-Options. Same iframe-embedding constraint.
26
26
  * - Cache-Control. Asset-path layout is app policy; set it in the caller.
27
- *
28
- * Returns the same response for chaining.
29
27
  */
30
28
  export declare function applySecurityHeaders(response: Response, opts: SecurityHeaderOptions): Response;
31
29
  //# sourceMappingURL=security-headers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACrC;;;;OAIG;IACH,IAAI,EAAE,OAAO,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,GAAG,QAAQ,CAW9F"}
1
+ {"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACrC;;;;OAIG;IACH,IAAI,EAAE,OAAO,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,GAAG,QAAQ,CAW9F"}
@@ -1,23 +1,21 @@
1
1
  /**
2
- * Apply baseline browser-hardening headers to a response. Cheap wins that
3
- * don't require UI verification:
2
+ * Apply baseline browser-hardening headers to a response and return it for
3
+ * chaining. Each is a cheap win that needs no UI verification:
4
4
  *
5
5
  * - X-Content-Type-Options: nosniff — disables MIME sniffing.
6
- * - Referrer-Policy: strict-origin-when-cross-origin — strips the path
7
- * and query from cross-origin Referer headers; matches modern browser
8
- * defaults but pins the behavior in case the default ever drifts.
9
- * - Permissions-Policy: opt out of browser APIs Selva-engine apps don't
10
- * use, so XSS can't enable them.
11
- * - HSTS when `opts.hsts` (production).
6
+ * - Referrer-Policy: strict-origin-when-cross-origin — strips path and query
7
+ * from cross-origin Referer headers. Matches modern browser defaults, but
8
+ * pins the behavior in case a default drifts.
9
+ * - Permissions-Policy: opts out of browser APIs Selva-engine apps don't use,
10
+ * so XSS can't enable them.
11
+ * - HSTS when `opts.hsts`.
12
12
  *
13
13
  * **Intentionally NOT set here:**
14
14
  * - Content-Security-Policy + frame-ancestors. Selva-engine apps are built
15
- * for iframe embedding, so a strict CSP needs UI-phase validation against
16
- * real consumer sites before it can ship.
15
+ * for iframe embedding, so a strict CSP needs validating against real
16
+ * consumer sites before it can ship.
17
17
  * - X-Frame-Options. Same iframe-embedding constraint.
18
18
  * - Cache-Control. Asset-path layout is app policy; set it in the caller.
19
- *
20
- * Returns the same response for chaining.
21
19
  */
22
20
  export function applySecurityHeaders(response, opts) {
23
21
  response.headers.set('X-Content-Type-Options', 'nosniff');
@@ -1 +1 @@
1
- {"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAkB,EAAE,IAA2B;IACnF,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;IAC1D,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,iCAAiC,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,CAAC,GAAG,CACnB,oBAAoB,EACpB,+GAA+G,CAC/G,CAAC;IACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,qCAAqC,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/http/security-headers.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAkB,EAAE,IAA2B;IACnF,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;IAC1D,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,iCAAiC,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,CAAC,GAAG,CACnB,oBAAoB,EACpB,+GAA+G,CAC/G,CAAC;IACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,qCAAqC,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC"}
@@ -1,10 +1,8 @@
1
1
  import type { ILogger, LogFields, LogLevel } from '@selvajs/platform';
2
2
  /**
3
- * Minimal `ILogger` over `console.*`, used when pino isn't installed.
4
- *
5
- * Renders as `LEVEL message {fields}` — readable in a terminal, greppable, and
6
- * honest about not being machine-parseable. `child` fields are merged eagerly
7
- * so correlation still works without pino.
3
+ * Minimal `ILogger` over `console.*`, used when pino isn't installed. Renders
4
+ * as `LEVEL message {fields}` — greppable, and honest about not being
5
+ * machine-parseable. `child` fields merge eagerly so correlation still works.
8
6
  */
9
7
  export declare class ConsoleLogger implements ILogger {
10
8
  private readonly bound;
@@ -19,21 +17,17 @@ export declare class ConsoleLogger implements ILogger {
19
17
  child(fields: LogFields): ILogger;
20
18
  }
21
19
  export interface CreateLoggerOptions {
22
- /** Minimum level written. Below this, records are dropped. */
20
+ /** Minimum level written; records below it are dropped. Defaults to `info`. */
23
21
  level?: LogLevel;
24
22
  /**
25
- * Pretty-print via `pino-pretty` (human-readable, colorized) instead of JSON.
26
- * For local development — production wants JSON for the collector. Silently
27
- * ignored if `pino-pretty` isn't installed.
23
+ * Pretty-print via `pino-pretty` instead of JSON — for local development;
24
+ * production wants JSON for the collector. Asking for this without
25
+ * `pino-pretty` installed drops the whole logger to {@link ConsoleLogger}.
28
26
  */
29
27
  pretty?: boolean;
30
28
  /** Fields stamped on every record (e.g. service name, release). */
31
29
  base?: LogFields;
32
30
  }
33
- /**
34
- * Build the app's root logger. Never throws and never returns `null`: a
35
- * deployment with no pino installed gets {@link ConsoleLogger}, because losing
36
- * warnings and errors is a worse outcome than losing JSON formatting.
37
- */
31
+ /** Build the app's root logger. Never throws, never returns `null`. */
38
32
  export declare function createLogger(opts?: CreateLoggerOptions): Promise<ILogger>;
39
33
  //# sourceMappingURL=PinoLogger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PinoLogger.d.ts","sourceRoot":"","sources":["../../src/logging/PinoLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAwDtE;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,OAAO;IAE3C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,KAAK,GAAE,SAAc,EACrB,QAAQ,GAAE,QAAiB;IAG7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAK3B;IAEF,OAAO,CAAC,KAAK;IAab,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAGhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAG/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAG/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAGhD,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO;CAGjC;AAqDD,MAAM,WAAW,mBAAmB;IACnC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,IAAI,CAAC,EAAE,SAAS,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBnF"}
1
+ {"version":3,"file":"PinoLogger.d.ts","sourceRoot":"","sources":["../../src/logging/PinoLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AA2DtE;;;;GAIG;AACH,qBAAa,aAAc,YAAW,OAAO;IAE3C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,KAAK,GAAE,SAAc,EACrB,QAAQ,GAAE,QAAiB;IAG7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAK3B;IAEF,OAAO,CAAC,KAAK;IAYb,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAGhD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAG/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAG/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI;IAGhD,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO;CAGjC;AAqDD,MAAM,WAAW,mBAAmB;IACnC,+EAA+E;IAC/E,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,IAAI,CAAC,EAAE,SAAS,CAAC;CACjB;AAED,uEAAuE;AACvE,wBAAsB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuBnF"}
@@ -1,8 +1,13 @@
1
1
  /**
2
- * Field paths scrubbed before a record is written. Defense-in-depth only — call
3
- * sites are required not to log secrets in the first place (see `LogFields`).
4
- * This catches the accident where a whole object is spread into fields and
5
- * happens to carry a token, which is exactly how credentials reach log indexes.
2
+ * Field paths scrubbed before a record is written. A backstop for accidents,
3
+ * NOT a licence to log objects: call sites are required not to log secrets in
4
+ * the first place (see `LogFields`). This catches the case where a whole object
5
+ * is spread into fields and happens to carry a credential.
6
+ *
7
+ * It matches by FIELD NAME, and only at the top level or one level deep (the
8
+ * `*.` entries). It will not catch personal data — an email nested in a payload
9
+ * sails straight through, and a log line reaches a collector that erasure can
10
+ * never reach. Note the nested `*.` set covers only five of the eight names.
6
11
  */
7
12
  const REDACTED_PATHS = [
8
13
  'token',
@@ -20,11 +25,9 @@ const REDACTED_PATHS = [
20
25
  '*.password'
21
26
  ];
22
27
  /**
23
- * Minimal `ILogger` over `console.*`, used when pino isn't installed.
24
- *
25
- * Renders as `LEVEL message {fields}` — readable in a terminal, greppable, and
26
- * honest about not being machine-parseable. `child` fields are merged eagerly
27
- * so correlation still works without pino.
28
+ * Minimal `ILogger` over `console.*`, used when pino isn't installed. Renders
29
+ * as `LEVEL message {fields}` — greppable, and honest about not being
30
+ * machine-parseable. `child` fields merge eagerly so correlation still works.
28
31
  */
29
32
  export class ConsoleLogger {
30
33
  bound;
@@ -45,9 +48,8 @@ export class ConsoleLogger {
45
48
  const merged = { ...this.bound, ...fields };
46
49
  const suffix = Object.keys(merged).length > 0 ? ` ${safeStringify(merged)}` : '';
47
50
  const line = `${level.toUpperCase()} ${message}${suffix}`;
48
- // Route to the matching console method so stderr/stdout split as expected.
49
- // `info` (not `log`) carries debug too — both go to stdout, and the level
50
- // is already spelled out in the line's own prefix.
51
+ // Split stderr/stdout the way an operator expects. `info` carries debug
52
+ // too — both land on stdout, and the line already spells out its level.
51
53
  if (level === 'error')
52
54
  console.error(line);
53
55
  else if (level === 'warn')
@@ -85,8 +87,8 @@ class PinoLogger {
85
87
  constructor(pino) {
86
88
  this.pino = pino;
87
89
  }
88
- // pino takes (fields, message); ILogger takes (message, fields) — the flip is
89
- // deliberate, since the message is what a human scans for first.
90
+ // pino takes (fields, message); ILogger takes (message, fields). The flip is
91
+ // deliberate — the message is what a human scans for first.
90
92
  debug(message, fields) {
91
93
  try {
92
94
  this.pino.debug(fields ?? {}, message);
@@ -128,11 +130,7 @@ class PinoLogger {
128
130
  }
129
131
  }
130
132
  }
131
- /**
132
- * Build the app's root logger. Never throws and never returns `null`: a
133
- * deployment with no pino installed gets {@link ConsoleLogger}, because losing
134
- * warnings and errors is a worse outcome than losing JSON formatting.
135
- */
133
+ /** Build the app's root logger. Never throws, never returns `null`. */
136
134
  export async function createLogger(opts = {}) {
137
135
  const level = opts.level ?? 'info';
138
136
  try {
@@ -150,10 +148,9 @@ export async function createLogger(opts = {}) {
150
148
  return new PinoLogger(pino);
151
149
  }
152
150
  catch (err) {
153
- // pino resolved but could not be constructed — a real misconfiguration
154
- // (e.g. `pretty: true` with pino-pretty absent). Unlike a plain absent
155
- // package, this one is worth complaining about: the operator asked for
156
- // something they didn't get. Fall back rather than go quiet.
151
+ // pino resolved but wouldn't construct — a real misconfiguration (e.g.
152
+ // `pretty: true` with pino-pretty absent). Worth complaining about, unlike
153
+ // a merely absent package: the operator asked for something they didn't get.
157
154
  console.warn(`[Logger] pino was found but could not be initialized; falling back to console logging. ${String(err)}`);
158
155
  return new ConsoleLogger(opts.base ?? {}, level);
159
156
  }
@@ -161,16 +158,13 @@ export async function createLogger(opts = {}) {
161
158
  /**
162
159
  * Load pino's factory, or `null` when it isn't installed.
163
160
  *
164
- * The indirection through a variable specifier is load-bearing, not stylistic.
165
- * A literal `import('pino')` is statically analyzable, so bundlers rewrite it —
166
- * and Vite, applying browser-field mapping to this server build, resolved it to
167
- * pino's `server_false` browser shim: a stub whose factory throws. That turned
168
- * "use pino" into a silent fall back to the console. Computing the specifier
169
- * defeats that resolution and leaves the import to Node at runtime, which is
170
- * exactly what an optional peer dependency needs.
171
- *
172
- * `@vite-ignore` alone was NOT sufficient — it suppressed the warning while the
173
- * rewrite happened anyway.
161
+ * Don't inline the specifier. A literal `import('pino')` is statically
162
+ * analyzable, so Vite applied browser-field mapping to this server build and
163
+ * resolved it to pino's `server_false` browser shim — a stub whose factory
164
+ * throws, turning "use pino" into a silent fall back to the console. Computing
165
+ * the specifier defeats that and leaves the import to Node at runtime.
166
+ * `@vite-ignore` alone was NOT enough: it silenced the warning and the rewrite
167
+ * still happened.
174
168
  */
175
169
  async function loadPinoFactory() {
176
170
  const specifier = 'pino';
@@ -181,8 +175,8 @@ async function loadPinoFactory() {
181
175
  }
182
176
  catch {
183
177
  // Not installed — the expected path on a base install, where the console
184
- // fallback is the intended behavior. Staying quiet here on purpose: a
185
- // boot-time complaint about an optional package nobody asked for is noise.
178
+ // fallback is intended. Quiet on purpose: complaining at boot about an
179
+ // optional package nobody asked for is noise.
186
180
  return null;
187
181
  }
188
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PinoLogger.js","sourceRoot":"","sources":["../../src/logging/PinoLogger.ts"],"names":[],"mappings":"AAkCA;;;;;GAKG;AACH,MAAM,cAAc,GAAG;IACtB,OAAO;IACP,cAAc;IACd,cAAc;IACd,QAAQ;IACR,SAAS;IACT,UAAU;IACV,eAAe;IACf,QAAQ;IACR,SAAS;IACT,gBAAgB;IAChB,gBAAgB;IAChB,UAAU;IACV,YAAY;CACZ,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAEP;IACA;IAFlB,YACkB,QAAmB,EAAE,EACrB,WAAqB,MAAM;QAD3B,UAAK,GAAL,KAAK,CAAgB;QACrB,aAAQ,GAAR,QAAQ,CAAmB;IAC1C,CAAC;IAEI,MAAM,CAAU,KAAK,GAA6B;QACzD,KAAK,EAAE,EAAE;QACT,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;KACT,CAAC;IAEM,KAAK,CAAC,KAAe,EAAE,OAAe,EAAE,MAAkB;QACjE,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO;QAC5E,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,IAAI,OAAO,GAAG,MAAM,EAAE,CAAC;QAC1D,2EAA2E;QAC3E,0EAA0E;QAC1E,mDAAmD;QACnD,IAAI,KAAK,KAAK,OAAO;YAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;aACtC,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACzC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,MAAkB;QACxC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,OAAe,EAAE,MAAkB;QACvC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,CAAC,OAAe,EAAE,MAAkB;QACvC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,KAAK,CAAC,OAAe,EAAE,MAAkB;QACxC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,KAAK,CAAC,MAAiB;QACtB,OAAO,IAAI,aAAa,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,MAAM,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvE,CAAC;;AAGF,6EAA6E;AAC7E,SAAS,aAAa,CAAC,KAAc;IACpC,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,kBAAkB,CAAC;IAC3B,CAAC;AACF,CAAC;AAED,MAAM,UAAU;IACc;IAA7B,YAA6B,IAAc;QAAd,SAAI,GAAJ,IAAI,CAAU;IAAG,CAAC;IAE/C,8EAA8E;IAC9E,iEAAiE;IACjE,KAAK,CAAC,OAAe,EAAE,MAAkB;QACxC,IAAI,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACR,8BAA8B;QAC/B,CAAC;IACF,CAAC;IACD,IAAI,CAAC,OAAe,EAAE,MAAkB;QACvC,IAAI,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACR,8BAA8B;QAC/B,CAAC;IACF,CAAC;IACD,IAAI,CAAC,OAAe,EAAE,MAAkB;QACvC,IAAI,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACR,8BAA8B;QAC/B,CAAC;IACF,CAAC;IACD,KAAK,CAAC,OAAe,EAAE,MAAkB;QACxC,IAAI,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACR,8BAA8B;QAC/B,CAAC;IACF,CAAC;IACD,KAAK,CAAC,MAAiB;QACtB,IAAI,CAAC;YACJ,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;CACD;AAeD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA4B,EAAE;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;IACnC,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,OAAO,CAAC;YACpB,KAAK;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE;YACvD,SAAS,EAAE,IAAI,CAAC,MAAM;gBACrB,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE;gBAChF,CAAC,CAAC,SAAS;SACZ,CAAC,CAAC;QACH,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,uEAAuE;QACvE,uEAAuE;QACvE,uEAAuE;QACvE,6DAA6D;QAC7D,OAAO,CAAC,IAAI,CACX,0FAA0F,MAAM,CAAC,GAAG,CAAC,EAAE,CACvG,CAAC;QACF,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,KAAK,UAAU,eAAe;IAC7B,MAAM,SAAS,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,CAAE,GAA6B,CAAC,OAAO,IAAI,GAAG,CAAgB,CAAC;QAC/E,OAAO,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACR,yEAAyE;QACzE,sEAAsE;QACtE,2EAA2E;QAC3E,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"PinoLogger.js","sourceRoot":"","sources":["../../src/logging/PinoLogger.ts"],"names":[],"mappings":"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"}
@@ -1,45 +1,37 @@
1
1
  /**
2
- * Request-id resolution for log correlation.
2
+ * Request-id resolution for log correlation. Every record on a request path
3
+ * carries the same `requestId`, so an operator handed one id can reconstruct
4
+ * that request's whole story.
3
5
  *
4
- * Every log record on a request path carries the same `requestId`, so an
5
- * operator handed one id can reconstruct that request's whole story — which is
6
- * the thing 199 uncorrelated `console.*` calls could never offer.
6
+ * Inbound ids are TRUSTED when present. Selva runs behind a reverse proxy
7
+ * (Caddy/nginx) that mints a request id for its own access logs; minting a
8
+ * second one here would split one request across two ids and kill cross-tier
9
+ * correlation. The trade — a direct caller picks their own id — is fine, because
10
+ * the id is only ever a log field and an echoed response header, never an
11
+ * authorization or uniqueness input.
7
12
  *
8
- * Inbound ids are TRUSTED when present. Selva is documented as running behind a
9
- * reverse proxy (Caddy/nginx), and those proxies mint a request id for their own
10
- * access logs; generating a second one here would split a single request across
11
- * two ids and make cross-tier correlation impossible. The trade is that a direct
12
- * caller can choose their own id — acceptable, because a request id is a
13
- * correlation hint, never an authorization or uniqueness input. It is used only
14
- * as a log field and an echoed response header.
15
- *
16
- * That trust is exactly why {@link sanitizeRequestId} exists: an attacker-chosen
17
- * id is untrusted *content*. Unbounded or newline-carrying values would let a
18
- * caller forge log lines (log injection) or bloat every record on the request.
13
+ * That trust is why {@link sanitizeRequestId} exists: an attacker-chosen id is
14
+ * untrusted content. Unbounded or newline-carrying values would let a caller
15
+ * forge log lines (log injection) or bloat every record on the request.
19
16
  */
20
17
  /** Header carrying a caller/proxy-supplied id. The de-facto standard spelling. */
21
18
  export declare const REQUEST_ID_HEADER = "x-request-id";
22
- /**
23
- * Reduce an untrusted inbound id to something safe to stamp on every record.
24
- * Returns `null` when nothing usable survives, so the caller generates instead.
25
- */
19
+ /** Returns `null` when nothing usable survives, so the caller generates instead. */
26
20
  export declare function sanitizeRequestId(raw: string | null | undefined): string | null;
27
21
  /**
28
- * Resolve the id for a request: reuse the proxy's when it survives
29
- * sanitization, otherwise mint a fresh UUID.
22
+ * Reuse the proxy's id when it survives sanitization, otherwise mint a UUID.
30
23
  *
31
- * @param headers the inbound request headers
32
- * @param generate id source; injectable so tests get deterministic ids
24
+ * @param generate injectable so tests get deterministic ids
33
25
  */
34
26
  export declare function resolveRequestId(headers: Headers, generate?: () => string): string;
35
27
  /**
36
28
  * Render a thrown value into a log field.
37
29
  *
38
- * Not every rejection is an `Error` — provider adapters occasionally reject with
39
- * a plain `{ message, status }` object, which would otherwise stringify to a
40
- * useless `[object Object]` and destroy the only record of the failure. Errors
41
- * keep their stack (that's the diagnostic); anything else is JSON-serialized,
42
- * with `String()` as the last resort for cycles and exotic values.
30
+ * Not every rejection is an `Error` — provider adapters sometimes reject with a
31
+ * plain `{ message, status }`, which would stringify to a useless
32
+ * `[object Object]` and destroy the only record of the failure. Errors keep
33
+ * their stack; anything else is JSON-serialized, with `String()` as the last
34
+ * resort for cycles and exotic values.
43
35
  */
44
36
  export declare function renderThrown(error: unknown): string;
45
37
  //# sourceMappingURL=requestId.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"requestId.d.ts","sourceRoot":"","sources":["../../src/logging/requestId.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAahD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAI/E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAM,MAAiB,GAAG,MAAM,CAE5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOnD"}
1
+ {"version":3,"file":"requestId.d.ts","sourceRoot":"","sources":["../../src/logging/requestId.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAahD,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAI/E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAM,MAAiB,GAAG,MAAM,CAE5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOnD"}
@@ -1,37 +1,31 @@
1
1
  /**
2
- * Request-id resolution for log correlation.
2
+ * Request-id resolution for log correlation. Every record on a request path
3
+ * carries the same `requestId`, so an operator handed one id can reconstruct
4
+ * that request's whole story.
3
5
  *
4
- * Every log record on a request path carries the same `requestId`, so an
5
- * operator handed one id can reconstruct that request's whole story — which is
6
- * the thing 199 uncorrelated `console.*` calls could never offer.
6
+ * Inbound ids are TRUSTED when present. Selva runs behind a reverse proxy
7
+ * (Caddy/nginx) that mints a request id for its own access logs; minting a
8
+ * second one here would split one request across two ids and kill cross-tier
9
+ * correlation. The trade — a direct caller picks their own id — is fine, because
10
+ * the id is only ever a log field and an echoed response header, never an
11
+ * authorization or uniqueness input.
7
12
  *
8
- * Inbound ids are TRUSTED when present. Selva is documented as running behind a
9
- * reverse proxy (Caddy/nginx), and those proxies mint a request id for their own
10
- * access logs; generating a second one here would split a single request across
11
- * two ids and make cross-tier correlation impossible. The trade is that a direct
12
- * caller can choose their own id — acceptable, because a request id is a
13
- * correlation hint, never an authorization or uniqueness input. It is used only
14
- * as a log field and an echoed response header.
15
- *
16
- * That trust is exactly why {@link sanitizeRequestId} exists: an attacker-chosen
17
- * id is untrusted *content*. Unbounded or newline-carrying values would let a
18
- * caller forge log lines (log injection) or bloat every record on the request.
13
+ * That trust is why {@link sanitizeRequestId} exists: an attacker-chosen id is
14
+ * untrusted content. Unbounded or newline-carrying values would let a caller
15
+ * forge log lines (log injection) or bloat every record on the request.
19
16
  */
20
17
  /** Header carrying a caller/proxy-supplied id. The de-facto standard spelling. */
21
18
  export const REQUEST_ID_HEADER = 'x-request-id';
22
- /** Max retained length. Long enough for a UUID or a W3C trace-id, short enough to bound a record. */
19
+ /** Long enough for a UUID or a W3C trace-id, short enough to bound a record. */
23
20
  const MAX_REQUEST_ID_LENGTH = 128;
24
21
  /**
25
- * Characters allowed through verbatim. Deliberately strict: UUIDs, ULIDs, hex
26
- * trace-ids and nginx's `$request_id` all fit. Anything else (whitespace,
27
- * control chars, quotes, CR/LF) is dropped rather than escaped — there is no
28
- * legitimate id that needs them, and dropping can't be un-escaped downstream.
22
+ * Characters allowed through verbatim. Strict on purpose: UUIDs, ULIDs, hex
23
+ * trace-ids and nginx's `$request_id` all fit. Everything else — whitespace,
24
+ * control chars, quotes, CR/LF — is dropped rather than escaped, since no
25
+ * legitimate id needs them and dropping can't be un-escaped downstream.
29
26
  */
30
27
  const UNSAFE_REQUEST_ID_CHARS = /[^A-Za-z0-9._:-]/g;
31
- /**
32
- * Reduce an untrusted inbound id to something safe to stamp on every record.
33
- * Returns `null` when nothing usable survives, so the caller generates instead.
34
- */
28
+ /** Returns `null` when nothing usable survives, so the caller generates instead. */
35
29
  export function sanitizeRequestId(raw) {
36
30
  if (!raw)
37
31
  return null;
@@ -39,11 +33,9 @@ export function sanitizeRequestId(raw) {
39
33
  return cleaned.length > 0 ? cleaned : null;
40
34
  }
41
35
  /**
42
- * Resolve the id for a request: reuse the proxy's when it survives
43
- * sanitization, otherwise mint a fresh UUID.
36
+ * Reuse the proxy's id when it survives sanitization, otherwise mint a UUID.
44
37
  *
45
- * @param headers the inbound request headers
46
- * @param generate id source; injectable so tests get deterministic ids
38
+ * @param generate injectable so tests get deterministic ids
47
39
  */
48
40
  export function resolveRequestId(headers, generate = randomId) {
49
41
  return sanitizeRequestId(headers.get(REQUEST_ID_HEADER)) ?? generate();
@@ -51,11 +43,11 @@ export function resolveRequestId(headers, generate = randomId) {
51
43
  /**
52
44
  * Render a thrown value into a log field.
53
45
  *
54
- * Not every rejection is an `Error` — provider adapters occasionally reject with
55
- * a plain `{ message, status }` object, which would otherwise stringify to a
56
- * useless `[object Object]` and destroy the only record of the failure. Errors
57
- * keep their stack (that's the diagnostic); anything else is JSON-serialized,
58
- * with `String()` as the last resort for cycles and exotic values.
46
+ * Not every rejection is an `Error` — provider adapters sometimes reject with a
47
+ * plain `{ message, status }`, which would stringify to a useless
48
+ * `[object Object]` and destroy the only record of the failure. Errors keep
49
+ * their stack; anything else is JSON-serialized, with `String()` as the last
50
+ * resort for cycles and exotic values.
59
51
  */
60
52
  export function renderThrown(error) {
61
53
  if (error instanceof Error)
@@ -68,10 +60,10 @@ export function renderThrown(error) {
68
60
  }
69
61
  }
70
62
  /**
71
- * `crypto.randomUUID` is available in every runtime Selva targets (Node 22+,
72
- * workers, modern browsers). The fallback covers non-secure contexts where
73
- * `randomUUID` is missing — ids only need to be collision-free enough to
74
- * correlate logs, never unguessable.
63
+ * `crypto.randomUUID` exists in every runtime Selva targets (Node 22+, workers,
64
+ * modern browsers); the `Math.random` branch only covers non-secure contexts
65
+ * where it is missing. Not a weakness: these ids need to be collision-free
66
+ * enough to correlate logs, never unguessable.
75
67
  */
76
68
  function randomId() {
77
69
  const c = globalThis.crypto;