@nodefony/framework 10.0.0-beta.5 → 10.0.0-beta.6

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 (47) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.157.0}/helpers/esm/decorate.js +1 -1
  2. package/dist/_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js +7 -0
  3. package/dist/index.js +14 -10
  4. package/dist/nodefony/config/config.js +16 -2
  5. package/dist/nodefony/controller/AdminApiController.js +18 -5
  6. package/dist/nodefony/controller/BenchController.js +1 -1
  7. package/dist/nodefony/controller/CspReportController.js +70 -0
  8. package/dist/nodefony/controller/IssuerMetadataController.js +1 -1
  9. package/dist/nodefony/controller/LoginPageController.js +5 -7
  10. package/dist/nodefony/controller/ProtectedResourceMetadataController.js +1 -1
  11. package/dist/nodefony/controller/SessionAuthController.js +6 -7
  12. package/dist/nodefony/controller/TokenAuthController.js +3 -3
  13. package/dist/nodefony/decorators/routerDecorators.js +2 -2
  14. package/dist/nodefony/service/AdminBroker.js +1 -1
  15. package/dist/nodefony/service/IdempotencyStore.js +1 -1
  16. package/dist/nodefony/service/router.js +5 -4
  17. package/dist/nodefony/src/Controller.js +311 -114
  18. package/dist/nodefony/src/PlaygroundAdminApi.js +1 -1
  19. package/dist/nodefony/src/Resolver.js +3 -9
  20. package/dist/nodefony/src/SecurityApiController.js +30 -7
  21. package/dist/nodefony/src/conditionalRequest.js +103 -0
  22. package/dist/nodefony/src/docsReader.js +1 -1
  23. package/dist/nodefony/src/idempotency.js +1 -1
  24. package/dist/nodefony/src/loginPage.js +2 -1
  25. package/dist/nodefony/src/scopeCatalog.js +1 -1
  26. package/dist/nodefony/src/viewNonce.js +58 -0
  27. package/dist/types/index.d.ts +3 -2
  28. package/dist/types/nodefony/config/config.d.ts +20 -0
  29. package/dist/types/nodefony/controller/AdminApiController.d.ts +4 -2
  30. package/dist/types/nodefony/controller/CspReportController.d.ts +35 -0
  31. package/dist/types/nodefony/controller/LoginPageController.d.ts +3 -2
  32. package/dist/types/nodefony/controller/SessionAuthController.d.ts +5 -6
  33. package/dist/types/nodefony/controller/TokenAuthController.d.ts +3 -3
  34. package/dist/types/nodefony/interfaces/IController.d.ts +2 -2
  35. package/dist/types/nodefony/service/router.d.ts +3 -2
  36. package/dist/types/nodefony/src/Controller.d.ts +110 -12
  37. package/dist/types/nodefony/src/Resolver.d.ts +1 -1
  38. package/dist/types/nodefony/src/SecurityApiController.d.ts +17 -4
  39. package/dist/types/nodefony/src/conditionalRequest.d.ts +82 -0
  40. package/dist/types/nodefony/src/loginPage.d.ts +1 -1
  41. package/dist/types/nodefony/src/viewNonce.d.ts +33 -0
  42. package/docs/controller.md +145 -26
  43. package/docs/decorateurs.md +1 -1
  44. package/docs/idempotence.md +2 -2
  45. package/docs/index.md +4 -2
  46. package/docs/templates.md +15 -0
  47. package/package.json +10 -9
@@ -1,4 +1,4 @@
1
- // \0@oxc-project+runtime@0.155.0/helpers/esm/decorate.js
1
+ // \0@oxc-project+runtime@0.157.0/helpers/esm/decorate.js
2
2
  function __decorate(decorators, target, key, desc) {
3
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -0,0 +1,7 @@
1
+ // \0@oxc-project+runtime@0.157.0/helpers/esm/decorateParam.js
2
+ function __decorateParam(paramIndex, decorator) {
3
+ return function(target, key) {
4
+ decorator(target, key, paramIndex);
5
+ };
6
+ }
7
+ export { __decorateParam as default };
package/dist/index.js CHANGED
@@ -3,17 +3,14 @@ import { defineFrameworkConfig, frameworkConfigJsonSchema } from "./nodefony/con
3
3
  import { getIdempotencyStoreFactory, listIdempotencyBackends, listIdempotencyStores, registerIdempotencyStore } from "./nodefony/src/idempotencyStoreRegistry.js";
4
4
  import { RedisIdempotencyStore } from "./nodefony/src/RedisIdempotencyStore.js";
5
5
  import { scheduleIdempotencyGc } from "./nodefony/src/idempotencyGc.js";
6
- import Route from "./nodefony/src/Route.js";
7
- import { validateRouteInput } from "./nodefony/src/routeSchema.js";
8
- import Controller from "./nodefony/src/Controller.js";
9
- import { All, Anonymous, Body, BypassFirewall, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, Options, Param, Patch, Post, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Scope, Session, UploadedFile, UploadedFiles, UseSession, buildContextParamArgs, controller, controllers, getParamArgsMeta, route, routeExpectsBodyStream } from "./nodefony/decorators/routerDecorators.js";
10
- import Resolver from "./nodefony/src/Resolver.js";
11
- import __decorate from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
12
6
  import router_default, { FRAMEWORK_ROUTER, isFrameworkRouter } from "./nodefony/service/router.js";
7
+ import Route from "./nodefony/src/Route.js";
8
+ import Controller, { configureFileServing } from "./nodefony/src/Controller.js";
13
9
  import ResourceController from "./nodefony/src/ResourceController.js";
14
- import AdminApiController from "./nodefony/controller/AdminApiController.js";
10
+ import Resolver from "./nodefony/src/Resolver.js";
15
11
  import AdminBroker_default from "./nodefony/service/AdminBroker.js";
16
12
  import IdempotencyStore_default from "./nodefony/service/IdempotencyStore.js";
13
+ import AdminApiController from "./nodefony/controller/AdminApiController.js";
17
14
  import SessionAuthController, { mountSessionAuthRoutes } from "./nodefony/controller/SessionAuthController.js";
18
15
  import LoginPageController, { mountLoginPage } from "./nodefony/controller/LoginPageController.js";
19
16
  import TokenAuthController, { mountTokenAuthRoutes } from "./nodefony/controller/TokenAuthController.js";
@@ -21,14 +18,18 @@ import IssuerMetadataController, { mountIssuerMetadataRoutes } from "./nodefony/
21
18
  import ProtectedResourceMetadataController, { collectProtectedResources, mountProtectedResourceRoutes, protectedResourceRoutePaths } from "./nodefony/controller/ProtectedResourceMetadataController.js";
22
19
  import WebAuthnController, { mountWebAuthnRoutes } from "./nodefony/controller/WebAuthnController.js";
23
20
  import OAuth2Controller, { mountOAuth2Routes } from "./nodefony/controller/OAuth2Controller.js";
21
+ import CspReportController, { mountCspReportRoute } from "./nodefony/controller/CspReportController.js";
24
22
  import BenchController, { mountBenchRoutes } from "./nodefony/controller/BenchController.js";
25
23
  import ApiKeyController, { mountApiKeyRoutes } from "./nodefony/controller/ApiKeyController.js";
26
24
  import TotpController, { mountTotpRoutes } from "./nodefony/controller/TotpController.js";
27
25
  import { createKernelAdminApi } from "./nodefony/src/KernelAdminApi.js";
28
- import { buildPlaygroundSnapshot } from "./nodefony/src/PlaygroundAdminApi.js";
29
26
  import { createFrameworkAdminApi } from "./nodefony/src/FrameworkAdminApi.js";
27
+ import { buildPlaygroundSnapshot } from "./nodefony/src/PlaygroundAdminApi.js";
30
28
  import { createSyslogAdminApi } from "./nodefony/src/SyslogAdminApi.js";
31
29
  import Eta from "./nodefony/service/Eta.js";
30
+ import { All, Anonymous, Body, BypassFirewall, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, Options, Param, Patch, Post, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Scope, Session, UploadedFile, UploadedFiles, UseSession, buildContextParamArgs, controller, controllers, getParamArgsMeta, getRouteSchema, route, routeExpectsBodyStream } from "./nodefony/decorators/routerDecorators.js";
31
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
32
+ import { validateRouteInput } from "./nodefony/src/routeSchema.js";
32
33
  import path from "node:path";
33
34
  import { AUTO_STORE, EMPTY_INFRA, Module, durableStoreRemedy, readStoreConnector, readStoreLocation, resolveAutoStore, services, storeConnectivity } from "nodefony";
34
35
  // index.ts
@@ -63,7 +64,9 @@ let Framework = class Framework extends Module {
63
64
  * `this.options` (matérialise les défauts, préserve `router`/`adminBroker`).
64
65
  */
65
66
  async onKernelRegister() {
66
- this.options = defineFrameworkConfig(this.options ?? {});
67
+ const validated = defineFrameworkConfig(this.options ?? {});
68
+ this.options = validated;
69
+ configureFileServing(validated.fileServing);
67
70
  return this;
68
71
  }
69
72
  /**
@@ -191,6 +194,7 @@ let Framework = class Framework extends Module {
191
194
  }
192
195
  if (this.kernel?.container?.get("webauthn")) mountWebAuthnRoutes(this);
193
196
  if (this.kernel?.container?.get("oauth2")) mountOAuth2Routes(this);
197
+ if (this.kernel?.container?.get("cspReports")) mountCspReportRoute(this);
194
198
  if (process.env.NF_BENCH_ROUTE === "1") mountBenchRoutes(this);
195
199
  if (this.kernel?.container?.get("apiKeys")) mountApiKeyRoutes(this);
196
200
  if (this.kernel?.container?.get("totp")) mountTotpRoutes(this);
@@ -204,4 +208,4 @@ Framework = __decorate([services([
204
208
  IdempotencyStore_default
205
209
  ])], Framework);
206
210
  var framework_default = Framework;
207
- export { AdminApiController, AdminBroker_default as AdminBroker, All, Anonymous, ApiKeyController, BenchController, Body, BypassFirewall, Controller, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Eta, FRAMEWORK_ROUTER, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, IssuerMetadataController, LoginPageController, IdempotencyStore_default as MemoryIdempotencyStore, OAuth2Controller, Options, Param, Patch, Post, ProtectedResourceMetadataController, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Resolver, ResourceController, Route, router_default as Router, Scope, Session, SessionAuthController, TokenAuthController, TotpController, UploadedFile, UploadedFiles, UseSession, WebAuthnController, buildContextParamArgs, buildPlaygroundSnapshot, collectProtectedResources, controller, controllers, createFrameworkAdminApi, createKernelAdminApi, createSyslogAdminApi, framework_default as default, defineFrameworkConfig, frameworkConfigJsonSchema, frameworkConfigSchema, getIdempotencyStoreFactory, getParamArgsMeta, isFrameworkRouter, listIdempotencyStores, mountApiKeyRoutes, mountBenchRoutes, mountIssuerMetadataRoutes, mountLoginPage, mountOAuth2Routes, mountProtectedResourceRoutes, mountSessionAuthRoutes, mountTokenAuthRoutes, mountTotpRoutes, mountWebAuthnRoutes, protectedResourceRoutePaths, registerIdempotencyStore, route, routeExpectsBodyStream, validateRouteInput };
211
+ export { AdminApiController, AdminBroker_default as AdminBroker, All, Anonymous, ApiKeyController, BenchController, Body, BypassFirewall, Controller, Cookie, Csp, CspReportController, CsrfExempt, CsrfProtect, CurrentUser, Delete, Domain, Eta, FRAMEWORK_ROUTER, Get, Head, Header, Headers, HttpCode, Idempotent, IsGranted, IssuerMetadataController, LoginPageController, IdempotencyStore_default as MemoryIdempotencyStore, OAuth2Controller, Options, Param, Patch, Post, ProtectedResourceMetadataController, Put, Query, Redirect, Req, RequireAuthentication, RequireScope, Res, Resolver, ResourceController, Route, router_default as Router, Scope, Session, SessionAuthController, TokenAuthController, TotpController, UploadedFile, UploadedFiles, UseSession, WebAuthnController, buildContextParamArgs, buildPlaygroundSnapshot, collectProtectedResources, controller, controllers, createFrameworkAdminApi, createKernelAdminApi, createSyslogAdminApi, framework_default as default, defineFrameworkConfig, frameworkConfigJsonSchema, frameworkConfigSchema, getIdempotencyStoreFactory, getParamArgsMeta, getRouteSchema, isFrameworkRouter, listIdempotencyStores, mountApiKeyRoutes, mountBenchRoutes, mountCspReportRoute, mountIssuerMetadataRoutes, mountLoginPage, mountOAuth2Routes, mountProtectedResourceRoutes, mountSessionAuthRoutes, mountTokenAuthRoutes, mountTotpRoutes, mountWebAuthnRoutes, protectedResourceRoutePaths, registerIdempotencyStore, route, routeExpectsBodyStream, validateRouteInput };
@@ -25,6 +25,9 @@ import { z } from "zod";
25
25
  * tels quels aux Services `Router` / `AdminBroker` (4ᵉ arg du `super(...)`).
26
26
  * Aucune forme métier figée → `looseObject` + `optional` (ne RIEN stripper,
27
27
  * absent par défaut = `undefined`, comme avant validation).
28
+ * - `idempotency` — store du cache d'idempotence.
29
+ * - `fileServing` — défaut des fichiers servis par une action (validateurs,
30
+ * `Cache-Control`), posé au boot dans `Controller.ts#configureFileServing`.
28
31
  *
29
32
  * ## Pureté
30
33
  *
@@ -38,10 +41,21 @@ const idempotencySchema = z.strictObject({
38
41
  gcIntervalS: z.number().int().min(0).default(600).describe("Intervalle de purge des clés d'idempotence expirées (s), HORS hot-path. N'a d'effet QUE pour un store SANS expiration native (`drizzle` → `DELETE WHERE expiresAt<=now`) ; `redis` (TTL `PX`) et `memory` (purge passive) l'ignorent. 0 = timer désarmé (cron/k8s)."),
39
42
  gcJitter: z.boolean().default(true).describe("Étale le départ du gc d'idempotence par process — anti thundering-herd sur le store SQL partagé en cluster.")
40
43
  });
44
+ /**
45
+ * Requêtes conditionnelles des fichiers servis par une action (`streamFile`,
46
+ * `renderFileDownload`, `renderMediaStream`). Le défaut de TOUTE
47
+ * l'application ; un appel le remplace par ses options (`validators`) ou ses
48
+ * en-têtes (`Cache-Control`, `ETag`).
49
+ */
50
+ const fileServingSchema = z.strictObject({
51
+ validators: z.boolean().default(true).describe("ETag forte + Last-Modified tirés de la taille et de la date du fichier, et réponses 304/412 (RFC 9110 §13). `false` les coupe pour toute l'application — dates non fiables (archive décompressée, build reproductible : même taille et même date pour deux contenus). Un appel peut rétablir ou couper par `{ validators }`."),
52
+ cacheControl: z.string().trim().regex(/^[\t\x20-\x7e\x80-\xff]+$/, "valeur d'en-tête invalide (CR, LF ou caractère de contrôle)").nullable().default("private, no-cache").describe("`Cache-Control` posé quand l'action n'en fixe aucun. Défaut `private, no-cache` : `Last-Modified` rendrait sinon la réponse réutilisable par un cache PARTAGÉ sans revalidation (RFC 9111 §4.2.2), or un fichier servi par une action est souvent protégé. `null` = n'en poser aucun. Sans effet quand `validators` est coupé.")
53
+ });
41
54
  const frameworkConfigSchema = z.strictObject({
42
55
  router: serviceOptionsSchema.optional().describe("Options transmises au Service `Router` (bag d'options de Service de base : logger, timers…). Loose : non strippées. Absent (défaut) = aucune."),
43
56
  adminBroker: serviceOptionsSchema.optional().describe("Options transmises au Service `AdminBroker` (data plane admin `/nodefony/<ns>/api/*`). Loose : non strippées. Absent (défaut) = aucune."),
44
- idempotency: idempotencySchema.default(() => idempotencySchema.parse({})).describe("Idempotence des mutations (anti double-effet). Cf draft-ietf-httpapi-idempotency-key-header.")
57
+ idempotency: idempotencySchema.default(() => idempotencySchema.parse({})).describe("Idempotence des mutations (anti double-effet). Cf draft-ietf-httpapi-idempotency-key-header."),
58
+ fileServing: fileServingSchema.default(() => fileServingSchema.parse({})).describe("Fichiers servis par une action : validateurs (ETag, Last-Modified, 304/412) et Cache-Control par défaut.")
45
59
  }).describe("Configuration de @nodefony/framework.");
46
60
  var config_default = {
47
61
  ...frameworkConfigSchema.parse({}),
@@ -58,4 +72,4 @@ var config_default = {
58
72
  }
59
73
  } }
60
74
  };
61
- export { config_default as default, frameworkConfigSchema };
75
+ export { config_default as default, fileServingSchema, frameworkConfigSchema };
@@ -1,6 +1,7 @@
1
1
  import Controller from "../src/Controller.js";
2
+ import { withAuthChallenge } from "../src/SecurityApiController.js";
2
3
  import { computeFingerprint, evaluateIdempotency, resolveIdempotencyKey, resolveIdentity } from "../src/idempotency.js";
3
- import { JsonRpcServerError, RequestContext, RpcError, executeAdminEndpoint } from "nodefony";
4
+ import { BYTES_MARKER_HEADER, JsonRpcServerError, RequestContext, RpcError, executeAdminEndpoint } from "nodefony";
4
5
  // nodefony/controller/AdminApiController.ts
5
6
  /** Vrai pour une chaîne ou un tableau de chaînes — ce que porte {@link IAdminRequest.query}. */
6
7
  function isAdminQueryValue(value) {
@@ -62,8 +63,10 @@ var AdminApiController = class AdminApiController extends Controller {
62
63
  * (une action, deux transports) : même exécution sur les deux transports, seul
63
64
  * l'emballage diffère.
64
65
  *
65
- * - **HTTP** : rendu historique inchangé (`renderJson` + status + header
66
- * `x-nodefony-instance`).
66
+ * - **HTTP** : `renderJson` + status + header `x-nodefony-instance`. Quand
67
+ * l'application active un codec binaire, le corps passe par le codec texte
68
+ * de la socket (convention `$bytes`, annoncée par `x-nodefony-bytes`) : des
69
+ * octets se relisent alors à l'identique sur les deux transports.
67
70
  * - **Pont WS-RPC `api.request`** : valeur **nue** (le pont l'enveloppe
68
71
  * `{id, result}` — snapshot ≡ GET par construction) ; statut ≥ 400 →
69
72
  * {@link RpcError} (`data.status` + `data.body`), symétrie d'un `fetch`
@@ -85,9 +88,19 @@ var AdminApiController = class AdminApiController extends Controller {
85
88
  }
86
89
  return body;
87
90
  }
88
- return this.renderJson(body, status, {
89
- ...headers,
91
+ const stamped = {
92
+ ...status === 401 ? withAuthChallenge(headers) : headers,
90
93
  "x-nodefony-instance": AdminApiController.instanceId
94
+ };
95
+ const codec = this.get("realtimeService")?.textFrameCodec;
96
+ const marker = codec?.bytesMarker;
97
+ if (codec === void 0 || marker === void 0) return this.renderJson(body, status, stamped);
98
+ const text = codec.encode(body);
99
+ if (typeof text !== "string") return this.renderJson(body, status, stamped);
100
+ this.setContextJson();
101
+ return this.renderResponse(text, "utf8", status, {
102
+ ...stamped,
103
+ [BYTES_MARKER_HEADER]: marker
91
104
  });
92
105
  }
93
106
  /**
@@ -1,5 +1,5 @@
1
- import Controller from "../src/Controller.js";
2
1
  import router_default from "../service/router.js";
2
+ import Controller from "../src/Controller.js";
3
3
  // nodefony/controller/BenchController.ts
4
4
  /**
5
5
  * Corps servi par la cible de banc — **gelé et partagé** : ce qu'on chronomètre
@@ -0,0 +1,70 @@
1
+ import router_default from "../service/router.js";
2
+ import Controller from "../src/Controller.js";
3
+ import { Body, CsrfExempt } from "../decorators/routerDecorators.js";
4
+ import __decorateParam from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorateParam.js";
5
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
6
+ import { CSP_REPORT_MAX_BYTES, CSP_REPORT_PATH } from "nodefony";
7
+ import { HttpError } from "@nodefony/http";
8
+ // nodefony/controller/CspReportController.ts
9
+ const NO_STORE = { "cache-control": "no-store" };
10
+ let mounted = false;
11
+ /**
12
+ * Réception PUBLIQUE des rapports de violation CSP (ADR-0024 §C7) —
13
+ * `POST /nodefony/security/api/csp-report`, adaptateur MINCE au-dessus du
14
+ * service `cspReports`.
15
+ *
16
+ * Le navigateur poste sans identifiant : la route contourne le pare-feu, et la
17
+ * défense CSRF (`@CsrfExempt`) — elle ne change l'état de personne, et un
18
+ * rapport n'a aucune session à détourner. Le corps arrive en FLUX
19
+ * (`@Body({ stream: true })`) pour être borné PENDANT la lecture : au-delà de
20
+ * {@link CSP_REPORT_MAX_BYTES}, 413 sans rien garder. Tout le reste — quota,
21
+ * type de contenu, forme, expurgation — appartient au collecteur, et la
22
+ * réponse est 204 quoi qu'il en pense : l'émetteur n'apprend rien.
23
+ */
24
+ var CspReportController = class extends Controller {
25
+ constructor(context) {
26
+ super("CspReportController", context);
27
+ }
28
+ /**
29
+ * Reçoit un rapport, ou un lot de rapports.
30
+ *
31
+ * @param stream - le corps brut, jamais lu par le pipeline.
32
+ * @throws HttpError 413 quand le corps dépasse le plafond.
33
+ */
34
+ async report(stream) {
35
+ const headers = this.context?.request?.headers;
36
+ const declared = Number(headers?.["content-length"]);
37
+ if (Number.isFinite(declared) && declared > CSP_REPORT_MAX_BYTES) throw new HttpError("CSP report too large", 413, this.context);
38
+ const chunks = [];
39
+ let seen = 0;
40
+ for await (const chunk of stream) {
41
+ seen += chunk.byteLength;
42
+ if (seen > CSP_REPORT_MAX_BYTES) throw new HttpError("CSP report too large", 413, this.context);
43
+ chunks.push(chunk);
44
+ }
45
+ const sink = this.get("cspReports");
46
+ if (sink) sink.receive(headers?.["content-type"], Buffer.concat(chunks, seen), this.context?.remoteAddress ?? null, headers?.["user-agent"] ?? null);
47
+ return this.renderResponse("", "utf8", 204, NO_STORE);
48
+ }
49
+ };
50
+ __decorate([CsrfExempt(), __decorateParam(0, Body({ stream: true }))], CspReportController.prototype, "report", null);
51
+ /**
52
+ * Monte la route de réception — appelé par le module framework à
53
+ * `onKernelReady`, seulement si le service `cspReports` est présent (rapports
54
+ * demandés vers la route du framework).
55
+ *
56
+ * @param frameworkModule - module qui porte le contrôleur.
57
+ */
58
+ function mountCspReportRoute(frameworkModule) {
59
+ if (mounted) return;
60
+ router_default.createRoute("security.cspReport", {
61
+ path: CSP_REPORT_PATH,
62
+ constructor: CspReportController,
63
+ classMethod: "report",
64
+ requirements: { methods: ["POST"] },
65
+ bypassFirewall: true
66
+ });
67
+ if (!Object.prototype.hasOwnProperty.call(CspReportController.prototype, "module")) router_default.setController(CspReportController, frameworkModule);
68
+ mounted = true;
69
+ }
70
+ export { CspReportController as default, mountCspReportRoute };
@@ -1,5 +1,5 @@
1
- import Controller from "../src/Controller.js";
2
1
  import router_default from "../service/router.js";
2
+ import Controller from "../src/Controller.js";
3
3
  import { askedAuthority, onDeclaredAuthority } from "./oauthAuthority.js";
4
4
  import { JWKS_PATH, authorizationServerMetadataPath, buildAuthorizationServerMetadata } from "nodefony";
5
5
  // nodefony/controller/IssuerMetadataController.ts
@@ -1,8 +1,6 @@
1
- import { isDevelopment } from "../src/singletonGuard.js";
2
- import Controller from "../src/Controller.js";
3
- import { Csp } from "../decorators/routerDecorators.js";
4
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
5
1
  import router_default from "../service/router.js";
2
+ import Controller from "../src/Controller.js";
3
+ import { isDevelopment } from "../src/singletonGuard.js";
6
4
  import { LOGIN_PAGE_ASSET_FILES, LOGIN_PAGE_TEMPLATE, buildLoginPageView, keepSuccess } from "../src/loginPage.js";
7
5
  import path from "node:path";
8
6
  import { LOGIN_PAGE_ASSETS_BASE, LOGIN_PAGE_SKINS, loginPageSkinFile } from "nodefony";
@@ -53,8 +51,9 @@ const version = keepSuccess(async () => {
53
51
  * feuille `nodefony/login.css`, script `nodefony/login.js`. Aucun front, aucun
54
52
  * build côté application.
55
53
  *
56
- * Joignable sans session ; jamais mise en cache (`no-store`) ni affichable
57
- * dans un cadre (`frame-ancestors 'none'`).
54
+ * Joignable sans session ; jamais mise en cache (`no-store`). L’encadrement
55
+ * suit `http.securityHeaders.frameOptions`, qui pose `X-Frame-Options` ET
56
+ * `frame-ancestors` sur toute la politique (ADR-0024 §C5).
58
57
  */
59
58
  var LoginPageController = class extends Controller {
60
59
  constructor(context) {
@@ -87,7 +86,6 @@ var LoginPageController = class extends Controller {
87
86
  });
88
87
  }
89
88
  };
90
- __decorate([Csp({ "frame-ancestors": ["'none'"] })], LoginPageController.prototype, "page", null);
91
89
  /**
92
90
  * Monte la page de connexion par défaut quand security la décrit : la route
93
91
  * sur `loginPage.path`, et ses fichiers sous `LOGIN_PAGE_ASSETS_BASE`.
@@ -1,5 +1,5 @@
1
- import Controller from "../src/Controller.js";
2
1
  import router_default from "../service/router.js";
2
+ import Controller from "../src/Controller.js";
3
3
  import { askedAuthority, onDeclaredAuthority } from "./oauthAuthority.js";
4
4
  import { buildProtectedResourceMetadata, canonicalResourceUri, protectedResourceMetadataPath } from "nodefony";
5
5
  // nodefony/controller/ProtectedResourceMetadataController.ts
@@ -1,7 +1,7 @@
1
1
  import { CsrfProtect } from "../decorators/routerDecorators.js";
2
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
3
2
  import router_default from "../service/router.js";
4
3
  import SecurityApiController from "../src/SecurityApiController.js";
4
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
5
5
  import { AUTH_API_BASE } from "nodefony";
6
6
  // nodefony/controller/SessionAuthController.ts
7
7
  let mounted = false;
@@ -29,12 +29,11 @@ let mounted = false;
29
29
  * (Auth.js, better-auth). Le `401` y écrivait une erreur réseau dans la console
30
30
  * de chaque visiteur anonyme, sans rien protéger : la réponse ne révèle rien.
31
31
  *
32
- * @remarks Dérogation RFC 9110 §15.5.2 DÉLIBÉRÉE : les 401 de `login` ne
33
- * portent PAS de `WWW-Authenticate` — aucun scheme HTTP ne décrit un cookie de
34
- * session, et un challenge `Basic` mensonger déclencherait le popup natif du
35
- * navigateur (l'anti-pattern que le BFF élimine). Les zones du firewall, elles,
36
- * restent strictement conformes (challenge du premier authenticator qui en
37
- * déclare un).
32
+ * @remarks Les 401 de `login` portent le défi `Session realm="nodefony"`
33
+ * (RFC 9110 §15.5.2, posé par `SecurityApiController`), celui du pare-feu :
34
+ * aucun schéma HTTP ne décrit un cookie de session, et un `Basic` ouvrirait la
35
+ * fenêtre de connexion native du navigateur — l'anti-pattern que le BFF
36
+ * élimine. Un schéma inconnu des navigateurs n'en ouvre aucune.
38
37
  */
39
38
  var SessionAuthController = class extends SecurityApiController {
40
39
  constructor(context) {
@@ -24,9 +24,9 @@ function parseScope(scope) {
24
24
  * framework ne peut pas importer les classes d'erreur de security ; un 429
25
25
  * reporte le `Retry-After` (RFC 6585) du throttler NIST.
26
26
  *
27
- * @remarks Dérogation RFC 9110 §15.5.2 (comme `SessionAuthController`) : ces 401 ne
28
- * portent pas de `WWW-Authenticate` — l'endpoint d'émission n'est pas une
29
- * ressource protégée par Bearer, il DÉLIVRE le Bearer.
27
+ * @remarks Ces 401 portent le défi `Session realm="nodefony"` (RFC 9110
28
+ * §15.5.2, posé par `SecurityApiController`) et non un `Bearer` : l'endpoint
29
+ * d'émission n'est pas une ressource protégée par Bearer, il DÉLIVRE le Bearer.
30
30
  */
31
31
  var TokenAuthController = class extends SecurityApiController {
32
32
  constructor(context) {
@@ -1,6 +1,6 @@
1
- import { assertRouteSchemaShape } from "../src/routeSchema.js";
2
- import Controller from "../src/Controller.js";
3
1
  import router_default from "../service/router.js";
2
+ import Controller from "../src/Controller.js";
3
+ import { assertRouteSchemaShape } from "../src/routeSchema.js";
4
4
  import { BootConfigurationError, RequestContext } from "nodefony";
5
5
  import "reflect-metadata";
6
6
  // nodefony/decorators/routerDecorators.ts
@@ -1,6 +1,6 @@
1
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
2
1
  import router_default from "./router.js";
3
2
  import AdminApiController from "../controller/AdminApiController.js";
3
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
4
4
  import { ADMIN_DEFAULT_ROLE, Service, injectable, resolveAdminRole } from "nodefony";
5
5
  // nodefony/service/AdminBroker.ts
6
6
  const serviceName = "adminBroker";
@@ -1,4 +1,4 @@
1
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
1
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
2
2
  import { Service, assertPageQuery, injectable } from "nodefony";
3
3
  // nodefony/service/IdempotencyStore.ts
4
4
  const serviceName = "idempotencyStore";
@@ -1,7 +1,7 @@
1
1
  import Route from "../src/Route.js";
2
- import { assertRouteSchemaDeclaration, routeExpectsBodyStream } from "../decorators/routerDecorators.js";
3
2
  import Resolver from "../src/Resolver.js";
4
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
3
+ import { assertRouteSchemaDeclaration, routeExpectsBodyStream } from "../decorators/routerDecorators.js";
4
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.157.0/helpers/esm/decorate.js";
5
5
  import { BootConfigurationError, Injector, Module, Service, injectable, isPromise, stripTrailingSlashes } from "nodefony";
6
6
  import { HttpError, isDomainAllowed } from "@nodefony/http";
7
7
  // nodefony/service/router.ts
@@ -349,11 +349,12 @@ Router = _Router = __decorate([injectable()], Router);
349
349
  * installées du framework feraient échouer `instanceof` en silence. Une
350
350
  * lecture de propriété.
351
351
  *
352
- * @param router - le routeur lu sur le contexte (`context.router`).
352
+ * @param router - le routeur lu sur le contexte (`context.router`) ou le
353
+ * service `router` du conteneur.
353
354
  * @returns `true` si c'est le Router du framework.
354
355
  */
355
356
  function isFrameworkRouter(router) {
356
- return router != null && FRAMEWORK_ROUTER in router;
357
+ return typeof router === "object" && router !== null && FRAMEWORK_ROUTER in router;
357
358
  }
358
359
  var router_default = Router;
359
360
  export { FRAMEWORK_ROUTER, router_default as default, isFrameworkRouter };