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

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 (61) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.155.0 → _@oxc-project_runtime@0.158.0}/helpers/esm/decorate.js +1 -1
  2. package/dist/_virtual/_@oxc-project_runtime@0.158.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 +3 -3
  9. package/dist/nodefony/controller/LoginPageController.js +5 -7
  10. package/dist/nodefony/controller/ProtectedResourceMetadataController.js +75 -10
  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 +103 -15
  14. package/dist/nodefony/grpc/GrpcController.js +138 -0
  15. package/dist/nodefony/grpc/GrpcService.js +75 -0
  16. package/dist/nodefony/grpc/declarations.js +33 -0
  17. package/dist/nodefony/grpc/index.js +3 -0
  18. package/dist/nodefony/service/AdminBroker.js +1 -1
  19. package/dist/nodefony/service/IdempotencyStore.js +1 -1
  20. package/dist/nodefony/service/router.js +15 -12
  21. package/dist/nodefony/src/Controller.js +330 -115
  22. package/dist/nodefony/src/PlaygroundAdminApi.js +1 -1
  23. package/dist/nodefony/src/Resolver.js +85 -51
  24. package/dist/nodefony/src/Route.js +45 -45
  25. package/dist/nodefony/src/SecurityApiController.js +30 -7
  26. package/dist/nodefony/src/conditionalRequest.js +103 -0
  27. package/dist/nodefony/src/docsReader.js +1 -1
  28. package/dist/nodefony/src/idempotency.js +1 -1
  29. package/dist/nodefony/src/loginPage.js +3 -2
  30. package/dist/nodefony/src/scopeCatalog.js +1 -1
  31. package/dist/nodefony/src/viewNonce.js +58 -0
  32. package/dist/types/index.d.ts +4 -2
  33. package/dist/types/nodefony/config/config.d.ts +20 -0
  34. package/dist/types/nodefony/controller/AdminApiController.d.ts +4 -2
  35. package/dist/types/nodefony/controller/CspReportController.d.ts +35 -0
  36. package/dist/types/nodefony/controller/LoginPageController.d.ts +3 -2
  37. package/dist/types/nodefony/controller/ProtectedResourceMetadataController.d.ts +21 -1
  38. package/dist/types/nodefony/controller/SessionAuthController.d.ts +5 -6
  39. package/dist/types/nodefony/controller/TokenAuthController.d.ts +3 -3
  40. package/dist/types/nodefony/decorators/routerDecorators.d.ts +2 -1
  41. package/dist/types/nodefony/grpc/GrpcController.d.ts +75 -0
  42. package/dist/types/nodefony/grpc/GrpcService.d.ts +51 -0
  43. package/dist/types/nodefony/grpc/declarations.d.ts +12 -0
  44. package/dist/types/nodefony/grpc/index.d.ts +13 -0
  45. package/dist/types/nodefony/interfaces/IController.d.ts +4 -4
  46. package/dist/types/nodefony/service/router.d.ts +8 -6
  47. package/dist/types/nodefony/src/Controller.d.ts +124 -14
  48. package/dist/types/nodefony/src/Resolver.d.ts +35 -8
  49. package/dist/types/nodefony/src/Route.d.ts +35 -0
  50. package/dist/types/nodefony/src/SecurityApiController.d.ts +17 -4
  51. package/dist/types/nodefony/src/conditionalRequest.d.ts +82 -0
  52. package/dist/types/nodefony/src/loginPage.d.ts +1 -1
  53. package/dist/types/nodefony/src/viewNonce.d.ts +33 -0
  54. package/docs/controller.md +234 -35
  55. package/docs/decorateurs.md +1 -1
  56. package/docs/grpc.md +690 -0
  57. package/docs/idempotence.md +13 -8
  58. package/docs/index.md +16 -10
  59. package/docs/routing.md +30 -2
  60. package/docs/templates.md +15 -0
  61. package/package.json +28 -11
@@ -1,4 +1,4 @@
1
- // \0@oxc-project+runtime@0.155.0/helpers/esm/decorate.js
1
+ // \0@oxc-project+runtime@0.158.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.158.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, contentDisposition } 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.158.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, contentDisposition, 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.158.0/helpers/esm/decorateParam.js";
5
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.158.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
@@ -92,7 +92,7 @@ var IssuerMetadataController = class extends Controller {
92
92
  try {
93
93
  document = buildAuthorizationServerMetadata({ issuer });
94
94
  } catch (error) {
95
- this.log(`métadonnées d'émetteur impubliables : ${error.message}`, "CRITIC");
95
+ this.log(`métadonnées d'émetteur impubliables : ${error instanceof Error ? error.message : String(error)}`, "CRITIC");
96
96
  return this.renderJson({ error: "server_error" }, 500);
97
97
  }
98
98
  return this.renderJson(document, 200, { "Cache-Control": "public, max-age=3600" });
@@ -110,7 +110,7 @@ var IssuerMetadataController = class extends Controller {
110
110
  try {
111
111
  keys = await publisher.getPublicJWKS();
112
112
  } catch (error) {
113
- this.log(`JWKS illisible : ${error.message}`, "CRITIC");
113
+ this.log(`JWKS illisible : ${error instanceof Error ? error.message : String(error)}`, "CRITIC");
114
114
  return this.renderJson({ error: "server_error" }, 500);
115
115
  }
116
116
  return this.renderJson(keys, 200, { "Cache-Control": "public, max-age=300" });
@@ -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
@@ -66,9 +66,10 @@ var ProtectedResourceMetadataController = class extends Controller {
66
66
  /** Chemin bien connu demandé, tel que le Router l'a matché. */
67
67
  #askedPath() {
68
68
  const req = this.request;
69
- const p = req?.pathname;
69
+ if (typeof req !== "object" || req === null) return null;
70
+ const p = "pathname" in req ? req.pathname : void 0;
70
71
  if (typeof p === "string") return p;
71
- const url = req?.url;
72
+ const url = "url" in req ? req.url : void 0;
72
73
  if (url instanceof URL) return url.pathname;
73
74
  if (typeof url === "string") try {
74
75
  return new URL(url, "http://localhost").pathname;
@@ -112,7 +113,7 @@ var ProtectedResourceMetadataController = class extends Controller {
112
113
  try {
113
114
  document = buildProtectedResourceMetadata(match);
114
115
  } catch (error) {
115
- this.log(`métadonnées de ressource protégée impubliables : ${error.message}`, "CRITIC");
116
+ this.log(`métadonnées de ressource protégée impubliables : ${error instanceof Error ? error.message : String(error)}`, "CRITIC");
116
117
  return this.renderJson({ error: "server_error" }, 500);
117
118
  }
118
119
  return this.renderJson(document, 200, { "Cache-Control": "public, max-age=3600" });
@@ -140,23 +141,87 @@ var ProtectedResourceMetadataController = class extends Controller {
140
141
  * interface à implémenter formellement — le même couplage que `tokenService` ou
141
142
  * `adminBroker`.
142
143
  *
144
+ * Deux sources peuvent déclarer la MÊME ressource — une zone du pare-feu et la
145
+ * porte MCP qu'elle couvre, typiquement — : elles sont fusionnées
146
+ * ({@link mergeProtectedResources}), jamais départagées par leur ordre.
147
+ *
143
148
  * @param container - conteneur de services de l'application
144
- * @returns les ressources déclarées, dans l'ordre des services
149
+ * @returns les ressources déclarées, une entrée par ressource, dans l'ordre des
150
+ * services
145
151
  */
146
152
  function collectProtectedResources(container) {
147
153
  const collected = [];
148
154
  for (const name of container.keys()) {
149
155
  const service = container.get(name);
150
- if (typeof service?.publishedProtectedResources !== "function") continue;
156
+ if (typeof service !== "object" || service === null || !("publishedProtectedResources" in service) || typeof service.publishedProtectedResources !== "function") continue;
151
157
  let declared;
152
158
  try {
153
- declared = service.publishedProtectedResources();
159
+ declared = Reflect.apply(service.publishedProtectedResources, service, []);
160
+ } catch {
161
+ continue;
162
+ }
163
+ if (!Array.isArray(declared)) continue;
164
+ for (const entry of declared) if (isProtectedResourceInput(entry)) collected.push(entry);
165
+ }
166
+ return mergeProtectedResources(collected);
167
+ }
168
+ /** Une liste de chaînes, ou rien. */
169
+ function isStringList(value) {
170
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
171
+ }
172
+ /**
173
+ * Une déclaration a-t-elle la forme d'une ressource publiable ? Le contrat est
174
+ * structurel ({@link IProtectedResourcePublisher}) : ce qu'un service rend se
175
+ * VÉRIFIE, il ne se suppose pas.
176
+ */
177
+ function isProtectedResourceInput(value) {
178
+ if (typeof value !== "object" || value === null) return false;
179
+ if (!("resource" in value) || typeof value.resource !== "string") return false;
180
+ if (!("authorizationServers" in value) || !isStringList(value.authorizationServers)) return false;
181
+ return !("scopesSupported" in value) || value.scopesSupported === void 0 || isStringList(value.scopesSupported);
182
+ }
183
+ /** L'union de deux listes, dans l'ordre d'apparition. */
184
+ function union(first, second) {
185
+ return [.../* @__PURE__ */ new Set([...first, ...second])];
186
+ }
187
+ /**
188
+ * Une ressource, UN document : fusionne les déclarations d'une même ressource
189
+ * (même URI canonique) — l'union de leurs serveurs d'autorisation et de leurs
190
+ * scopes ; le premier nom et la première documentation déclarés.
191
+ *
192
+ * Pourquoi fusionner plutôt que garder la première : une zone du pare-feu
193
+ * publie sa ressource avec les émetteurs de confiance, et la porte MCP qu'elle
194
+ * couvre publie la MÊME ressource avec les scopes de ses outils. Garder la
195
+ * première jetait les scopes sans un mot — et un agent qui suit le document
196
+ * demandait alors un jeton sans eux, qui n'ouvrait pas l'outil.
197
+ *
198
+ * @param entries - déclarations, dans l'ordre des services
199
+ * @returns une entrée par ressource, à la place de la première déclaration
200
+ */
201
+ function mergeProtectedResources(entries) {
202
+ const merged = [];
203
+ const byResource = /* @__PURE__ */ new Map();
204
+ for (const entry of entries) {
205
+ let key;
206
+ try {
207
+ key = canonicalResourceUri(entry.resource);
154
208
  } catch {
209
+ merged.push(entry);
210
+ continue;
211
+ }
212
+ const seen = byResource.get(key);
213
+ if (seen === void 0) {
214
+ const first = { ...entry };
215
+ byResource.set(key, first);
216
+ merged.push(first);
155
217
  continue;
156
218
  }
157
- for (const entry of declared) collected.push(entry);
219
+ seen.authorizationServers = union(seen.authorizationServers, entry.authorizationServers);
220
+ if (entry.scopesSupported !== void 0) seen.scopesSupported = union(seen.scopesSupported ?? [], entry.scopesSupported);
221
+ if (seen.resourceName === void 0 && entry.resourceName !== void 0) seen.resourceName = entry.resourceName;
222
+ if (seen.resourceDocumentation === void 0 && entry.resourceDocumentation !== void 0) seen.resourceDocumentation = entry.resourceDocumentation;
158
223
  }
159
- return collected;
224
+ return merged;
160
225
  }
161
226
  /**
162
227
  * Chemins bien connus à monter pour un jeu de ressources déclarées.
@@ -217,4 +282,4 @@ function mountProtectedResourceRoutes(frameworkModule, resources) {
217
282
  mounted = true;
218
283
  return paths.length;
219
284
  }
220
- export { collectProtectedResources, ProtectedResourceMetadataController as default, mountProtectedResourceRoutes, protectedResourceRoutePaths };
285
+ export { collectProtectedResources, ProtectedResourceMetadataController as default, mergeProtectedResources, mountProtectedResourceRoutes, protectedResourceRoutePaths };
@@ -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.158.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
@@ -97,6 +97,36 @@ function controllers(targets) {
97
97
  };
98
98
  }
99
99
  /**
100
+ * Applique à une route en cours de montage ce que la classe et la méthode
101
+ * déclarent par décorateur : `@Domain` (hôte) et `@BypassFirewall`. Seule
102
+ * implémentation de ces précédences — `@controller` et `@GrpcService` montent
103
+ * leurs routes par elle.
104
+ *
105
+ * Précédences : `@route({ host })` > `@Domain` méthode > `@Domain` classe ;
106
+ * option `bypassFirewall: true` > `@BypassFirewall` méthode > classe. Lecture
107
+ * au montage, fail-closed : un oubli laisse la route gardée.
108
+ *
109
+ * @param ctor - la classe contrôleur.
110
+ * @param options - les options de la route, complétées EN PLACE.
111
+ * @param method - la méthode dont les décorateurs s'appliquent (celle du
112
+ * contrat pour une route gRPC, pas l'action de base).
113
+ *
114
+ * @internal
115
+ */
116
+ function applyDeclaredRouteOptions(ctor, options, method) {
117
+ if (options.host === void 0) {
118
+ const methodDomain = method === void 0 ? void 0 : Reflect.getMetadata(DOMAIN_METHOD_METADATA, ctor, method);
119
+ const classDomain = Reflect.getMetadata(DOMAIN_CLASS_METADATA, ctor);
120
+ const domain = methodDomain ?? classDomain;
121
+ if (domain) options.host = domain;
122
+ }
123
+ if (options.bypassFirewall !== true) {
124
+ const methodBypass = method === void 0 ? void 0 : Reflect.getMetadata(BYPASS_FIREWALL_METHOD_METADATA, ctor, method);
125
+ const classBypass = Reflect.getMetadata(BYPASS_FIREWALL_CLASS_METADATA, ctor);
126
+ if (methodBypass === true || classBypass === true) options.bypassFirewall = true;
127
+ }
128
+ }
129
+ /**
100
130
  * Declaration Controller
101
131
  *
102
132
  * @param prefix - prefixage du router du controller.
@@ -124,17 +154,7 @@ function controller(prefix) {
124
154
  let hasMagic = false;
125
155
  for (const [name, options] of Object.entries(metadata)) {
126
156
  options.prefix = prefix;
127
- if (options.host === void 0) {
128
- const methodDomain = options.classMethod === void 0 ? void 0 : Reflect.getMetadata(DOMAIN_METHOD_METADATA, mycontroller, options.classMethod);
129
- const classDomain = Reflect.getMetadata(DOMAIN_CLASS_METADATA, mycontroller);
130
- const domain = methodDomain ?? classDomain;
131
- if (domain) options.host = domain;
132
- }
133
- if (options.bypassFirewall !== true) {
134
- const methodBypass = options.classMethod === void 0 ? void 0 : Reflect.getMetadata(BYPASS_FIREWALL_METHOD_METADATA, mycontroller, options.classMethod);
135
- const classBypass = Reflect.getMetadata(BYPASS_FIREWALL_CLASS_METADATA, mycontroller);
136
- if (methodBypass === true || classBypass === true) options.bypassFirewall = true;
137
- }
157
+ applyDeclaredRouteOptions(mycontroller, options, options.classMethod);
138
158
  if (options.path === "*") {
139
159
  hasMagic = {
140
160
  options,
@@ -153,6 +173,72 @@ function controller(prefix) {
153
173
  function route(name, options) {
154
174
  return defineRoute(name, options);
155
175
  }
176
+ /** Plus long délai qu'un minuteur Node honore : au-delà, il tombe à 1 ms. */
177
+ const MAX_TIMER_MS = 2147483647;
178
+ /**
179
+ * Valide l'option de route `responseTimeout` À LA DÉCLARATION : un entier de
180
+ * 0 à 2³¹−1. Hors bornes, Node ramènerait le minuteur à 1 ms — chaque requête
181
+ * de la route expirerait aussitôt, sans que rien ne dise pourquoi.
182
+ *
183
+ * @param value - l'option telle que déclarée (absente = celle du serveur).
184
+ * @param className - classe du contrôleur, nommée dans l'erreur.
185
+ * @param method - action, nommée dans l'erreur.
186
+ * @returns la valeur validée, ou `undefined` si l'option est absente.
187
+ * @throws BootConfigurationError qui nomme l'action et la borne.
188
+ */
189
+ function checkedResponseTimeout(value, className, method) {
190
+ if (value === void 0) return void 0;
191
+ if (!Number.isInteger(value) || value < 0 || value > MAX_TIMER_MS) throw new BootConfigurationError(`${className}.${method} : « responseTimeout » entier entre 0 et ${MAX_TIMER_MS} ms attendu (reçu ${String(value)}).`);
192
+ return value;
193
+ }
194
+ /** Les bornes d'envoi chiffrées qu'une route peut régler pour elle-même. */
195
+ const UPLOAD_LIMIT_KEYS = /* @__PURE__ */ new Set([
196
+ "maxFileSize",
197
+ "maxTotalFileSize",
198
+ "maxFiles",
199
+ "maxFields",
200
+ "maxFieldsSize"
201
+ ]);
202
+ function isUploadLimitKey(key) {
203
+ return UPLOAD_LIMIT_KEYS.has(key);
204
+ }
205
+ /** Les empreintes que le flux d'envoi sait calculer. */
206
+ function isUploadHash(value) {
207
+ return value === false || value === "sha1" || value === "md5" || value === "sha256";
208
+ }
209
+ /**
210
+ * Valide l'option de route `upload` À LA DÉCLARATION : des entiers ≥ 1 pour
211
+ * les bornes, une empreinte connue, aucune clé inconnue. Une faute de frappe
212
+ * (`maxFilesize`) laisserait sinon la route sur la borne de l'application —
213
+ * 500 Mo par défaut — sans que rien ne le dise.
214
+ *
215
+ * @param value - l'option telle que déclarée (absente = celles de la config).
216
+ * @param bodyStream - la route laisse son corps en flux : ses bornes ne
217
+ * s'appliqueraient jamais, la déclaration est contradictoire.
218
+ * @param className - classe du contrôleur, nommée dans l'erreur.
219
+ * @param method - action, nommée dans l'erreur.
220
+ * @returns les bornes validées, figées, sans clé `undefined` (la fusion sur
221
+ * la configuration ne doit rien écraser par du vide), ou `undefined`.
222
+ * @throws BootConfigurationError qui nomme l'action et la clé fautive.
223
+ */
224
+ function checkedUpload(value, bodyStream, className, method) {
225
+ if (value === void 0) return void 0;
226
+ const where = `${className}.${method} : « upload »`;
227
+ if (bodyStream) throw new BootConfigurationError(`${where} et « bodyStream: true » s'excluent — un corps laissé en flux n'est jamais lu par le pipeline, ses bornes ne s'appliqueraient pas.`);
228
+ const checked = {};
229
+ for (const [key, entry] of Object.entries(value)) {
230
+ if (entry === void 0) continue;
231
+ if (key === "hashAlgorithm") {
232
+ if (!isUploadHash(entry)) throw new BootConfigurationError(`${where} : « hashAlgorithm » vaut false, "sha1", "md5" ou "sha256" (reçu ${String(entry)}).`);
233
+ checked.hashAlgorithm = entry;
234
+ continue;
235
+ }
236
+ if (!isUploadLimitKey(key)) throw new BootConfigurationError(`${where} : clé inconnue « ${key} » — une route règle ${[...UPLOAD_LIMIT_KEYS].join(", ")} et hashAlgorithm ; le dossier et l'encodage restent ceux de la configuration.`);
237
+ if (typeof entry !== "number" || !Number.isSafeInteger(entry) || entry < 1) throw new BootConfigurationError(`${where} : « ${key} » entier ≥ 1 attendu (reçu ${String(entry)}).`);
238
+ checked[key] = entry;
239
+ }
240
+ return Object.freeze(checked);
241
+ }
156
242
  /** L'implémentation de {@link route}, sans les surcharges de typage. */
157
243
  function defineRoute(name, options) {
158
244
  return function(target, propertyKey, descriptor) {
@@ -190,7 +276,9 @@ function defineRoute(name, options) {
190
276
  defaults: options.defaults,
191
277
  requirements: options.requirements,
192
278
  bypassFirewall: options.bypassFirewall,
193
- fallback: options.fallback
279
+ fallback: options.fallback,
280
+ responseTimeout: checkedResponseTimeout(options.responseTimeout, className, propertyKey),
281
+ upload: checkedUpload(options.upload, options.bodyStream === true, className, propertyKey)
194
282
  };
195
283
  Reflect.defineMetadata(metadataKey, metadata, target.constructor);
196
284
  return descriptor;
@@ -1176,4 +1264,4 @@ function computeAuthenticationRequirement(ctor, method) {
1176
1264
  amr: Object.freeze([.../* @__PURE__ */ new Set([...onClass.amr, ...onMethod.amr])])
1177
1265
  });
1178
1266
  }
1179
- export { All, Anonymous, BYPASS_FIREWALL_CLASS_METADATA, BYPASS_FIREWALL_METHOD_METADATA, Body, BypassFirewall, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, DOMAIN_CLASS_METADATA, DOMAIN_METHOD_METADATA, Delete, Domain, Get, HEADERS_METADATA, HTTP_CODE_METADATA, Head, Header, Headers, HttpCode, Idempotent, IsGranted, Options, PARAM_ARGS_METADATA, Param, Patch, Post, Put, Query, REDIRECT_METADATA, Redirect, Req, RequireAuthentication, RequireScope, Res, Scope, Session, USE_SESSION_CLASS_METADATA, USE_SESSION_METHOD_METADATA, UploadedFile, UploadedFiles, UseSession, assertRouteSchemaDeclaration, buildContextParamArgs, buildParamArgs, computeActionMeta, controller, controllers, extractActionScopes, getParamArgsMeta, getRouteSchema, readInvocationBody, readRouteInput, resolveActionMeta, resolveParamArg, resolveSessionIntent, route, routeExpectsBodyStream };
1267
+ export { All, Anonymous, BYPASS_FIREWALL_CLASS_METADATA, BYPASS_FIREWALL_METHOD_METADATA, Body, BypassFirewall, Cookie, Csp, CsrfExempt, CsrfProtect, CurrentUser, DOMAIN_CLASS_METADATA, DOMAIN_METHOD_METADATA, Delete, Domain, Get, HEADERS_METADATA, HTTP_CODE_METADATA, Head, Header, Headers, HttpCode, Idempotent, IsGranted, Options, PARAM_ARGS_METADATA, Param, Patch, Post, Put, Query, REDIRECT_METADATA, Redirect, Req, RequireAuthentication, RequireScope, Res, Scope, Session, USE_SESSION_CLASS_METADATA, USE_SESSION_METHOD_METADATA, UploadedFile, UploadedFiles, UseSession, applyDeclaredRouteOptions, assertRouteSchemaDeclaration, buildContextParamArgs, buildParamArgs, computeActionMeta, controller, controllers, extractActionScopes, getParamArgsMeta, getReservedActionNames, getRouteSchema, readInvocationBody, readRouteInput, resolveActionMeta, resolveParamArg, resolveSessionIntent, route, routeExpectsBodyStream };