@warlock.js/core 5.25.0 → 5.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/esm/cli/commands/doctor/checks/index.mjs +2 -0
  3. package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
  4. package/esm/cli/commands/doctor/checks/locales-ts-deprecated.check.mjs +170 -0
  5. package/esm/cli/commands/doctor/checks/locales-ts-deprecated.check.mjs.map +1 -0
  6. package/esm/cli/commands/doctor/doctor-command.action.mjs +7 -2
  7. package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
  8. package/esm/cli/commands/doctor.command.mjs +5 -0
  9. package/esm/cli/commands/doctor.command.mjs.map +1 -1
  10. package/esm/cli/commands/generate/generators/module.generator.mjs +1 -12
  11. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  12. package/esm/cli/commands/typings-generator.command.mjs +4 -1
  13. package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
  14. package/esm/database/migrate-action.mjs +2 -2
  15. package/esm/database/migrate-action.mjs.map +1 -1
  16. package/esm/dev-server/legacy-locales-warning.mjs +16 -0
  17. package/esm/dev-server/legacy-locales-warning.mjs.map +1 -0
  18. package/esm/dev-server/module-loader.mjs +16 -0
  19. package/esm/dev-server/module-loader.mjs.map +1 -1
  20. package/esm/dev-server/special-file-patterns.mjs +2 -2
  21. package/esm/dev-server/special-file-patterns.mjs.map +1 -1
  22. package/esm/dev-server/translation-keys-sources.mjs +7 -3
  23. package/esm/dev-server/translation-keys-sources.mjs.map +1 -1
  24. package/esm/dev-server/type-generator.mjs +27 -4
  25. package/esm/dev-server/type-generator.mjs.map +1 -1
  26. package/esm/generations/features/access.feature.mjs +38 -18
  27. package/esm/generations/features/access.feature.mjs.map +1 -1
  28. package/esm/generations/features/bull-board.feature.mjs +2 -2
  29. package/esm/generations/features/bull-board.feature.mjs.map +1 -1
  30. package/esm/http/build-rate-limit-options.d.mts +8 -2
  31. package/esm/http/build-rate-limit-options.d.mts.map +1 -1
  32. package/esm/http/build-rate-limit-options.mjs +1 -0
  33. package/esm/http/build-rate-limit-options.mjs.map +1 -1
  34. package/esm/http/errors/errors.d.mts +1 -17
  35. package/esm/http/errors/errors.d.mts.map +1 -1
  36. package/esm/http/errors/errors.mjs +1 -20
  37. package/esm/http/errors/errors.mjs.map +1 -1
  38. package/esm/http/index.d.mts +2 -2
  39. package/esm/http/index.mjs +1 -1
  40. package/esm/http/middleware/inject-request-context.d.mts +1 -1
  41. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  42. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  43. package/esm/http/middleware/rate-limit.middleware.d.mts.map +1 -1
  44. package/esm/http/middleware/rate-limit.middleware.mjs +5 -1
  45. package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
  46. package/esm/http/request.d.mts +7 -24
  47. package/esm/http/request.d.mts.map +1 -1
  48. package/esm/http/request.mjs +1 -22
  49. package/esm/http/request.mjs.map +1 -1
  50. package/esm/http/response.d.mts.map +1 -1
  51. package/esm/http/response.mjs +4 -2
  52. package/esm/http/response.mjs.map +1 -1
  53. package/esm/index.d.mts +5 -3
  54. package/esm/index.mjs +5 -2
  55. package/esm/localization/index.mjs +4 -0
  56. package/esm/localization/parse-locale-dictionary.d.mts +23 -0
  57. package/esm/localization/parse-locale-dictionary.d.mts.map +1 -0
  58. package/esm/localization/parse-locale-dictionary.mjs +215 -0
  59. package/esm/localization/parse-locale-dictionary.mjs.map +1 -0
  60. package/esm/localization/register-locale-dictionary.d.mts +12 -0
  61. package/esm/localization/register-locale-dictionary.d.mts.map +1 -0
  62. package/esm/localization/register-locale-dictionary.mjs +58 -0
  63. package/esm/localization/register-locale-dictionary.mjs.map +1 -0
  64. package/esm/production/production-builder.mjs +11 -2
  65. package/esm/production/production-builder.mjs.map +1 -1
  66. package/esm/router/types.d.mts +8 -0
  67. package/esm/router/types.d.mts.map +1 -1
  68. package/esm/tests/start-http-development-server.d.mts +10 -0
  69. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  70. package/esm/tests/start-http-development-server.mjs +16 -2
  71. package/esm/tests/start-http-development-server.mjs.map +1 -1
  72. package/esm/validation/validateAll.mjs +48 -1
  73. package/esm/validation/validateAll.mjs.map +1 -1
  74. package/llms-full.txt +118 -75
  75. package/llms.txt +1 -1
  76. package/package.json +20 -20
  77. package/skills/README.md +1 -1
  78. package/skills/create-controller/SKILL.md +4 -0
  79. package/skills/test-http/SKILL.md +9 -1
  80. package/skills/use-localization/SKILL.md +39 -26
@@ -1 +1 @@
1
- {"version":3,"file":"build-rate-limit-options.mjs","names":[],"sources":["../../../../../../../core/src/http/build-rate-limit-options.ts"],"sourcesContent":["import type { RateLimitPluginOptions } from \"@fastify/rate-limit\";\n\n/**\n * `@fastify/rate-limit` options that `http.rateLimit` passes straight through.\n */\nconst passthroughKeys = [\n \"redis\",\n \"nameSpace\",\n \"keyGenerator\",\n \"skipOnError\",\n \"allowList\",\n \"continueExceeding\",\n \"enableDraftSpec\",\n \"global\",\n \"hook\",\n \"cache\",\n \"errorResponseBuilder\",\n \"addHeaders\",\n \"addHeadersOnExceeding\",\n] as const;\n\n/**\n * Shape of the `http.rateLimit` config.\n */\nexport type HttpRateLimitConfig = Pick<RateLimitPluginOptions, (typeof passthroughKeys)[number]> & {\n /**\n * Set `false` to skip registering the global rate limiter.\n *\n * @default true\n */\n enabled?: boolean;\n /**\n * Max requests per time window.\n *\n * @default 60\n */\n max?: number;\n /**\n * Time window in milliseconds. Alias of `timeWindow`.\n *\n * @default 60 * 1000\n */\n duration?: number;\n /**\n * Time window; takes precedence over `duration` when both are set.\n */\n timeWindow?: RateLimitPluginOptions[\"timeWindow\"];\n};\n\n/**\n * Build the `@fastify/rate-limit` plugin options from `http.rateLimit`.\n * Returns `null` when the limiter is disabled.\n */\nexport function buildRateLimitOptions(\n config: HttpRateLimitConfig | undefined,\n): RateLimitPluginOptions | null {\n const source = config ?? {};\n\n if (source.enabled === false) {\n return null;\n }\n\n const options: Record<string, unknown> = {\n max: source.max ?? 60,\n timeWindow: source.timeWindow ?? source.duration ?? 60 * 1000,\n };\n\n for (const key of passthroughKeys) {\n if (source[key] !== undefined) {\n options[key] = source[key];\n }\n }\n\n return options as RateLimitPluginOptions;\n}\n"],"mappings":";;;;AAKA,MAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;AAkCA,SAAgB,sBACd,QAC+B;CAC/B,MAAM,SAAS,UAAU,CAAC;CAE1B,IAAI,OAAO,YAAY,OACrB,OAAO;CAGT,MAAM,UAAmC;EACvC,KAAK,OAAO,OAAO;EACnB,YAAY,OAAO,cAAc,OAAO,YAAY,KAAK;CAC3D;CAEA,KAAK,MAAM,OAAO,iBAChB,IAAI,OAAO,SAAS,QAClB,QAAQ,OAAO,OAAO;CAI1B,OAAO;AACT"}
1
+ {"version":3,"file":"build-rate-limit-options.mjs","names":[],"sources":["../../../../../../../core/src/http/build-rate-limit-options.ts"],"sourcesContent":["import type { RateLimitPluginOptions } from \"@fastify/rate-limit\";\n\n/**\n * `@fastify/rate-limit` options that `http.rateLimit` passes straight through.\n */\nconst passthroughKeys = [\n \"redis\",\n \"nameSpace\",\n \"keyGenerator\",\n \"ipv6Subnet\",\n \"skipOnError\",\n \"allowList\",\n \"continueExceeding\",\n \"enableDraftSpec\",\n \"global\",\n \"hook\",\n \"cache\",\n \"errorResponseBuilder\",\n \"addHeaders\",\n \"addHeadersOnExceeding\",\n] as const;\n\n/**\n * Shape of the `http.rateLimit` config.\n */\nexport type HttpRateLimitConfig = Omit<\n Pick<RateLimitPluginOptions, (typeof passthroughKeys)[number]>,\n \"ipv6Subnet\"\n> & {\n /**\n * IPv6 subnet size used to group client addresses.\n *\n * @default 64\n */\n ipv6Subnet?: RateLimitPluginOptions[\"ipv6Subnet\"];\n /**\n * Set `false` to skip registering the global rate limiter.\n *\n * @default true\n */\n enabled?: boolean;\n /**\n * Max requests per time window.\n *\n * @default 60\n */\n max?: number;\n /**\n * Time window in milliseconds. Alias of `timeWindow`.\n *\n * @default 60 * 1000\n */\n duration?: number;\n /**\n * Time window; takes precedence over `duration` when both are set.\n */\n timeWindow?: RateLimitPluginOptions[\"timeWindow\"];\n};\n\n/**\n * Build the `@fastify/rate-limit` plugin options from `http.rateLimit`.\n * Returns `null` when the limiter is disabled.\n */\nexport function buildRateLimitOptions(\n config: HttpRateLimitConfig | undefined,\n): RateLimitPluginOptions | null {\n const source = config ?? {};\n\n if (source.enabled === false) {\n return null;\n }\n\n const options: Record<string, unknown> = {\n max: source.max ?? 60,\n timeWindow: source.timeWindow ?? source.duration ?? 60 * 1000,\n };\n\n for (const key of passthroughKeys) {\n if (source[key] !== undefined) {\n options[key] = source[key];\n }\n }\n\n return options as RateLimitPluginOptions;\n}\n"],"mappings":";;;;AAKA,MAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;AA2CA,SAAgB,sBACd,QAC+B;CAC/B,MAAM,SAAS,UAAU,CAAC;CAE1B,IAAI,OAAO,YAAY,OACrB,OAAO;CAGT,MAAM,UAAmC;EACvC,KAAK,OAAO,OAAO;EACnB,YAAY,OAAO,cAAc,OAAO,YAAY,KAAK;CAC3D;CAEA,KAAK,MAAM,OAAO,iBAChB,IAAI,OAAO,SAAS,QAClB,QAAQ,OAAO,OAAO;CAI1B,OAAO;AACT"}
@@ -52,22 +52,6 @@ declare class NotAllowedError extends HttpError {
52
52
  declare class CookieJarUnavailableError extends Error {
53
53
  constructor(cookieName: string);
54
54
  }
55
- /**
56
- * Thrown by `Request.prototype.user` in development to catch a call site
57
- * still reading the removed `request.user` getter/setter after the 5.12.0
58
- * move: the authenticated user now lives at `request.locals.user`, written
59
- * by `@warlock.js/auth`'s middleware.
60
- *
61
- * Development-only diagnostic, not a runtime contract other code should
62
- * catch — the getter itself is typed `never`, so a caller that still
63
- * compiles against `request.user` only does so via `any`/an outdated type.
64
- * Kept in core (not auth) because `request.user` is a core `Request`
65
- * accessor and core cannot depend on `@warlock.js/auth` to react to it.
66
- * Slated for removal one release after 5.12.0 — see the CHANGELOG.
67
- */
68
- declare class RequestUserMovedError extends Error {
69
- constructor();
70
- }
71
55
  //#endregion
72
- export { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, RequestUserMovedError, ResourceNotFoundError, ServerError, UnAuthorizedError };
56
+ export { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError };
73
57
  //# sourceMappingURL=errors.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../../core/src/http/errors/errors.ts"],"mappings":";cAAa,SAAA,SAAkB,KAAK;EAEzB,MAAA;EACA,OAAA;EACA,OAAA;cAFA,MAAA,UACA,OAAA,UACA,OAAA;AAAA;AAAA,cAOE,qBAAA,SAA8B,SAAS;EAGzC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,iBAAA,SAA0B,SAAS;EAGrC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,cAAA,SAAuB,SAAS;EAGlC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,eAAA,SAAwB,SAAS;EAGnC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,WAAA,SAAoB,SAAS;EAG/B,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,aAAA,SAAsB,SAAS;EAGjC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,kBAAA,SAA2B,SAAS;EAGtC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,eAAA,SAAwB,SAAS;EAGnC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;;;;;;;;;;AAlDa;AAOxB;;cA8Da,yBAAA,SAAkC,KAAK;cAC/B,UAAA;AAAA;;;;;;AA5DG;AAOxB;;;;;;;cA6Ea,qBAAA,SAA8B,KAAK;EAAL,WAAA;AAAA"}
1
+ {"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../../core/src/http/errors/errors.ts"],"mappings":";cAAa,SAAA,SAAkB,KAAK;EAEzB,MAAA;EACA,OAAA;EACA,OAAA;cAFA,MAAA,UACA,OAAA,UACA,OAAA;AAAA;AAAA,cAOE,qBAAA,SAA8B,SAAS;EAGzC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,iBAAA,SAA0B,SAAS;EAGrC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,cAAA,SAAuB,SAAS;EAGlC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,eAAA,SAAwB,SAAS;EAGnC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,WAAA,SAAoB,SAAS;EAG/B,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,aAAA,SAAsB,SAAS;EAGjC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,kBAAA,SAA2B,SAAS;EAGtC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;AAAA,cAOE,eAAA,SAAwB,SAAS;EAGnC,OAAA;cADP,OAAA,UACO,OAAA;AAAA;;;;;;;;;;AAlDa;AAOxB;;cA8Da,yBAAA,SAAkC,KAAK;cAC/B,UAAA;AAAA"}
@@ -82,26 +82,7 @@ var CookieJarUnavailableError = class extends Error {
82
82
  this.name = "CookieJarUnavailableError";
83
83
  }
84
84
  };
85
- /**
86
- * Thrown by `Request.prototype.user` in development to catch a call site
87
- * still reading the removed `request.user` getter/setter after the 5.12.0
88
- * move: the authenticated user now lives at `request.locals.user`, written
89
- * by `@warlock.js/auth`'s middleware.
90
- *
91
- * Development-only diagnostic, not a runtime contract other code should
92
- * catch — the getter itself is typed `never`, so a caller that still
93
- * compiles against `request.user` only does so via `any`/an outdated type.
94
- * Kept in core (not auth) because `request.user` is a core `Request`
95
- * accessor and core cannot depend on `@warlock.js/auth` to react to it.
96
- * Slated for removal one release after 5.12.0 — see the CHANGELOG.
97
- */
98
- var RequestUserMovedError = class extends Error {
99
- constructor() {
100
- super("request.user has been removed. The authenticated user now lives at request.locals.user, set by @warlock.js/auth's middleware.");
101
- this.name = "RequestUserMovedError";
102
- }
103
- };
104
85
 
105
86
  //#endregion
106
- export { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, RequestUserMovedError, ResourceNotFoundError, ServerError, UnAuthorizedError };
87
+ export { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError };
107
88
  //# sourceMappingURL=errors.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../../../core/src/http/errors/errors.ts"],"sourcesContent":["export class HttpError extends Error {\n public constructor(\n public status: number,\n public message: string,\n public payload?: any,\n ) {\n super(message);\n this.name = \"HttpError\";\n }\n}\n\nexport class ResourceNotFoundError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(404, message, payload);\n this.name = \"ResourceNotFoundError\";\n }\n}\n\nexport class UnAuthorizedError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(401, message, payload);\n this.name = \"UnAuthorizedError\";\n }\n}\n\nexport class ForbiddenError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(403, message, payload);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport class BadRequestError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(400, message, payload);\n this.name = \"BadRequestError\";\n }\n}\n\nexport class ServerError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(500, message, payload);\n this.name = \"ServerError\";\n }\n}\n\nexport class ConflictError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(409, message, payload);\n this.name = \"ConflictError\";\n }\n}\n\nexport class NotAcceptableError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(406, message, payload);\n this.name = \"NotAcceptableError\";\n }\n}\n\nexport class NotAllowedError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(405, message, payload);\n this.name = \"NotAllowedError\";\n }\n}\n\n/**\n * Thrown by `Request.prototype.cookie` / `Request.prototype.hasCookie` when\n * `@fastify/cookie` was never registered on this Fastify instance, so\n * `baseRequest.cookies` is `undefined` rather than an (possibly empty) object.\n *\n * A by-name cookie read is a deliberate assertion by the caller — \"this\n * cookie should be readable here\" — so an unavailable jar is a configuration\n * fault, not an absent cookie, and must not be swallowed into a default\n * value or a silent `false`. Contrast `Request.prototype.cookies`, which\n * stays lenient because the framework's own opportunistic reads (e.g.\n * locale resolution) must not throw on a request that simply has no jar.\n */\nexport class CookieJarUnavailableError extends Error {\n public constructor(cookieName: string) {\n super(\n `Cannot read cookie \"${cookieName}\": the cookie jar is unavailable because ` +\n \"@fastify/cookie is not registered on this Fastify instance. \" +\n \"Register the plugin (see core's http/plugins.ts) before reading cookies by name.\",\n );\n\n this.name = \"CookieJarUnavailableError\";\n }\n}\n\n/**\n * Thrown by `Request.prototype.user` in development to catch a call site\n * still reading the removed `request.user` getter/setter after the 5.12.0\n * move: the authenticated user now lives at `request.locals.user`, written\n * by `@warlock.js/auth`'s middleware.\n *\n * Development-only diagnostic, not a runtime contract other code should\n * catch — the getter itself is typed `never`, so a caller that still\n * compiles against `request.user` only does so via `any`/an outdated type.\n * Kept in core (not auth) because `request.user` is a core `Request`\n * accessor and core cannot depend on `@warlock.js/auth` to react to it.\n * Slated for removal one release after 5.12.0 — see the CHANGELOG.\n */\nexport class RequestUserMovedError extends Error {\n public constructor() {\n super(\n \"request.user has been removed. The authenticated user now lives at \" +\n \"request.locals.user, set by @warlock.js/auth's middleware.\",\n );\n this.name = \"RequestUserMovedError\";\n }\n}\n"],"mappings":";AAAA,IAAa,YAAb,cAA+B,MAAM;CACnC,AAAO,YACL,AAAO,QACP,AAAO,SACP,AAAO,SACP;EACA,MAAM,OAAO;EAJN;EACA;EACA;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,UAAU;CACnD,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,UAAU;CAC/C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,iBAAb,cAAoC,UAAU;CAC5C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,kBAAb,cAAqC,UAAU;CAC7C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,cAAb,cAAiC,UAAU;CACzC,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,gBAAb,cAAmC,UAAU;CAC3C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,qBAAb,cAAwC,UAAU;CAChD,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,kBAAb,cAAqC,UAAU;CAC7C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;AAcA,IAAa,4BAAb,cAA+C,MAAM;CACnD,AAAO,YAAY,YAAoB;EACrC,MACE,uBAAuB,WAAW,sLAGpC;EAEA,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;;AAeA,IAAa,wBAAb,cAA2C,MAAM;CAC/C,AAAO,cAAc;EACnB,MACE,+HAEF;EACA,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../../../core/src/http/errors/errors.ts"],"sourcesContent":["export class HttpError extends Error {\n public constructor(\n public status: number,\n public message: string,\n public payload?: any,\n ) {\n super(message);\n this.name = \"HttpError\";\n }\n}\n\nexport class ResourceNotFoundError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(404, message, payload);\n this.name = \"ResourceNotFoundError\";\n }\n}\n\nexport class UnAuthorizedError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(401, message, payload);\n this.name = \"UnAuthorizedError\";\n }\n}\n\nexport class ForbiddenError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(403, message, payload);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport class BadRequestError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(400, message, payload);\n this.name = \"BadRequestError\";\n }\n}\n\nexport class ServerError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(500, message, payload);\n this.name = \"ServerError\";\n }\n}\n\nexport class ConflictError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(409, message, payload);\n this.name = \"ConflictError\";\n }\n}\n\nexport class NotAcceptableError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(406, message, payload);\n this.name = \"NotAcceptableError\";\n }\n}\n\nexport class NotAllowedError extends HttpError {\n public constructor(\n message: string,\n public payload?: any,\n ) {\n super(405, message, payload);\n this.name = \"NotAllowedError\";\n }\n}\n\n/**\n * Thrown by `Request.prototype.cookie` / `Request.prototype.hasCookie` when\n * `@fastify/cookie` was never registered on this Fastify instance, so\n * `baseRequest.cookies` is `undefined` rather than an (possibly empty) object.\n *\n * A by-name cookie read is a deliberate assertion by the caller — \"this\n * cookie should be readable here\" — so an unavailable jar is a configuration\n * fault, not an absent cookie, and must not be swallowed into a default\n * value or a silent `false`. Contrast `Request.prototype.cookies`, which\n * stays lenient because the framework's own opportunistic reads (e.g.\n * locale resolution) must not throw on a request that simply has no jar.\n */\nexport class CookieJarUnavailableError extends Error {\n public constructor(cookieName: string) {\n super(\n `Cannot read cookie \"${cookieName}\": the cookie jar is unavailable because ` +\n \"@fastify/cookie is not registered on this Fastify instance. \" +\n \"Register the plugin (see core's http/plugins.ts) before reading cookies by name.\",\n );\n\n this.name = \"CookieJarUnavailableError\";\n }\n}\n"],"mappings":";AAAA,IAAa,YAAb,cAA+B,MAAM;CACnC,AAAO,YACL,AAAO,QACP,AAAO,SACP,AAAO,SACP;EACA,MAAM,OAAO;EAJN;EACA;EACA;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,wBAAb,cAA2C,UAAU;CACnD,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,UAAU;CAC/C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,iBAAb,cAAoC,UAAU;CAC5C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,kBAAb,cAAqC,UAAU;CAC7C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,cAAb,cAAiC,UAAU;CACzC,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,gBAAb,cAAmC,UAAU;CAC3C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,qBAAb,cAAwC,UAAU;CAChD,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;AAEA,IAAa,kBAAb,cAAqC,UAAU;CAC7C,AAAO,YACL,SACA,AAAO,SACP;EACA,MAAM,KAAK,SAAS,OAAO;EAFpB;EAGP,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;AAcA,IAAa,4BAAb,cAA+C,MAAM;CACnD,AAAO,YAAY,YAAoB;EACrC,MACE,uBAAuB,WAAW,sLAGpC;EAEA,KAAK,OAAO;CACd;AACF"}
@@ -3,7 +3,7 @@ import { XMLable } from "./xmlable.mjs";
3
3
  import { CookieOptions, Response, ResponseStatus, SendBufferOptions, SendFileOptions } from "./response.mjs";
4
4
  import { FileNamingStrategy, ImageTransformCallback, ImageTransformConfig, ImageVariantDefinition, ImageVariantFit, ImageVariantOutputFormat, PrefixConfig, PrefixOptions, SaveAsOptions, SaveOptions, UploadedFileImageOptions, UploadsConfigurations, UploadsImagesConfigurations } from "./uploads-types.mjs";
5
5
  import { FileValidationOptions, UploadedFile, UploadedFileJson } from "./uploaded-file.mjs";
6
- import { Request } from "./request.mjs";
6
+ import { Request, RequestTranslator } from "./request.mjs";
7
7
  import { CspConfig, DEFAULT_CSP_DIRECTIVES, InvalidCspDirectiveError, applyCspHeader, buildCspHeaderValue, mergeCspDirectives, resolveCspConfig, serializeCspDirectives, validateCspConfigAtBoot, validateCspDirectives } from "./csp.mjs";
8
8
  import { HttpTracingConfig, TracingContext, TracingHooks, TracingPhaseInfo, TracingRequestEndInfo } from "./tracing/tracing.type.mjs";
9
9
  import { DecodedAccessToken, HttpConfigurations, PartialMiddleware, RequestEvent, RequestLocals, ResponseEvent, ResponseSSEController, ResponseStreamController, ReturnedResponse } from "./types.mjs";
@@ -21,7 +21,7 @@ import { DetectedImageFormat, GeneratedImageDescriptor, GeneratedImageVariant, R
21
21
  import { GenerateImageVariantsOptions, generateImageVariants } from "./uploads/generate-image-variants.mjs";
22
22
  import { ImageVariantsConfigError } from "./uploads/image-variants-config-error.mjs";
23
23
  import { uploadedFileController } from "./uploads/uploaded-file.controller.mjs";
24
- import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, RequestUserMovedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./errors/errors.mjs";
24
+ import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./errors/errors.mjs";
25
25
  import { CacheMiddlewareOptions } from "./middleware/cache-response-middleware.mjs";
26
26
  import { ConcurrencyLimitOptions } from "./middleware/concurrency-limit.middleware.mjs";
27
27
  import { IdempotencyOptions } from "./middleware/idempotency.middleware.mjs";
@@ -3,7 +3,7 @@ import { DEFAULT_CSP_DIRECTIVES, InvalidCspDirectiveError, applyCspHeader, build
3
3
  import { HttpErrorCodes } from "./error-codes.mjs";
4
4
  import { isAllowedCsrfOrigin, normalizeOrigin, originOf, ownOrigin, resolveCsrfOriginVerdict } from "./csrf-origin-policy.mjs";
5
5
  import { requiresDefaultCsrfGuard, runDefaultCsrfGuard } from "./csrf-default-guard.mjs";
6
- import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, RequestUserMovedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./errors/errors.mjs";
6
+ import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./errors/errors.mjs";
7
7
  import { deriveTraceId, parseTraceparentTraceId } from "./tracing/trace-id.mjs";
8
8
  import { buildTracingContext, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, isTracingEnabled, registerTracingHooks, resetTracingConfigForTests, resolveTracingConfig } from "./tracing/tracing-dispatcher.mjs";
9
9
  import "./tracing/index.mjs";
@@ -12,7 +12,7 @@ declare function createRequestStore(request: Request<any>, response: Response):
12
12
  /**
13
13
  * Translate a keyword (uses request context for locale)
14
14
  */
15
- declare function t(keyword: string, placeholders?: any): any;
15
+ declare function t(keyword: string, placeholders?: Record<string, unknown>): string;
16
16
  //#endregion
17
17
  export { createRequestStore, t };
18
18
  //# sourceMappingURL=inject-request-context.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inject-request-context.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/inject-request-context.ts"],"mappings":";;;;;;;AA8CA;;;iBAAgB,kBAAA,CACd,OAAA,EAAS,OAAA,OACT,QAAA,EAAU,QAAA,GACT,OAAA,CAAQ,gBAAA;;;;iBA2KK,CAAA,CAAE,OAAA,UAAiB,YAAkB"}
1
+ {"version":3,"file":"inject-request-context.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/inject-request-context.ts"],"mappings":";;;;;;;AA8CA;;;iBAAgB,kBAAA,CACd,OAAA,EAAS,OAAA,OACT,QAAA,EAAU,QAAA,GACT,OAAA,CAAQ,gBAAA;;;;iBA2KK,CAAA,CAAE,OAAA,UAAiB,YAAA,GAAe,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"inject-request-context.mjs","names":["requestContextInstance"],"sources":["../../../../../../../../core/src/http/middleware/inject-request-context.ts"],"sourcesContent":["/**\n * Request Context Middleware\n *\n * Creates a unified context for each request using the ContextManager.\n * All framework contexts (request, storage, database) are available throughout the request lifecycle.\n */\nimport { trans } from \"@mongez/localization\";\nimport { type GenericObject } from \"@mongez/reinforcements\";\nimport { DatabaseWriterValidationError } from \"@warlock.js/cascade\";\nimport { contextManager } from \"@warlock.js/context\";\nimport { log } from \"@warlock.js/logger\";\nimport config from \"@mongez/config\";\nimport { environment } from \"../../utils\";\nimport { requestContext as requestContextInstance } from \"../context/request-context\";\nimport { applyCspHeader } from \"../csp\";\nimport { runDefaultCsrfGuard } from \"../csrf-default-guard\";\nimport { HttpError } from \"../errors\";\nimport { type Request } from \"../request\";\nimport { type Response } from \"../response\";\nimport { buildTracingContext, dispatchPhase, isTracingEnabled } from \"../tracing\";\nimport { type ReturnedResponse } from \"./../types\";\n\n// Contexts are now registered in core/context/init-contexts.ts via initializeContexts()\n\n/**\n * Echo `request.id` back as a response header so the FE / proxies / log\n * aggregators can correlate by the same value the server logs against.\n *\n * Reads the header name from `http.requestId.header` (default `X-Request-Id`).\n * Skip when `http.requestId.enabled` is explicitly false.\n */\nfunction stampRequestIdHeader(request: Request, response: Response) {\n const requestIdConfig = config.get(\"http.requestId\", {} as Record<string, any>);\n\n if (requestIdConfig.enabled === false) return;\n\n const headerName = requestIdConfig.header || \"X-Request-Id\";\n\n response.header(headerName, request.id);\n}\n\n/**\n * Create request store and execute middleware + handler\n *\n * Runs all registered contexts together using ContextManager.\n */\nexport function createRequestStore(\n request: Request<any>,\n response: Response,\n): Promise<ReturnedResponse> {\n stampRequestIdHeader(request, response);\n\n // Stamped this early — before any middleware or handler runs — so the\n // header carries the SAME nonce the web layer later reads off\n // `request.nonce` for its `<script>` tags: `request.nonce` caches on first\n // read, and this is deliberately the first read. A no-op when the app\n // hasn't opted into `http.csp` (see `csp.ts`).\n applyCspHeader(request, response);\n\n // Build all context stores using the immutable API\n // Each context defines its own store initialization via buildStore()\n const httpContextStore = contextManager.buildStores({ request, response });\n\n // Run all contexts together!\n return contextManager.runAll(httpContextStore, async () => {\n try {\n // Default CSRF-Origin guard (SECURITY, card 8a752ab2) — the earliest\n // seam common to every request, BEFORE the route's own middleware\n // (`request.runMiddleware()` below) and before any app handler, so it\n // applies even to a route that never attaches\n // `authMiddleware(\"cookie:*\")`. See `../csrf-default-guard.ts` for the\n // full scope rule and `{ csrf: false }` exemption.\n const csrfGuardResponse = await runDefaultCsrfGuard(request, response, t, (reason) => {\n log.error(\n \"http\",\n \"csrf-default-guard\",\n new Error(`Default CSRF Origin guard refused request: ${reason}`),\n );\n });\n\n if (csrfGuardResponse) {\n return csrfGuardResponse as ReturnedResponse;\n }\n\n // Run middleware chain\n const result = await request.runMiddleware();\n\n if (result) {\n return result as ReturnedResponse;\n }\n\n // Execute route handler\n request.trigger(\"executingAction\", request.route);\n\n const handler = request.getHandler();\n\n request.log(\"Executing Handler\", \"info\");\n\n const tracingEnabled = isTracingEnabled();\n const handlerStartedAt = tracingEnabled ? performance.now() : 0;\n\n const output = await handler({ request, response });\n\n if (tracingEnabled) {\n dispatchPhase(buildTracingContext(request), {\n name: \"handler\",\n durationMs: performance.now() - handlerStartedAt,\n });\n }\n\n request.log(\"Handler Executed Successfully\", \"success\");\n\n request.trigger(\"executedAction\", request.route);\n\n return output as ReturnedResponse;\n } catch (error) {\n request.log(error, \"error\");\n return handleRequestError(error, response, request);\n }\n });\n}\n\n/**\n * Unconditional floor for an unhandled request error (canon 8d3c13a8): route it\n * through the logger, and when no logger channel is configured (nothing would\n * record it) fall back to `console.error` so the only copy is never lost.\n * @internal\n */\nfunction reportUnhandledRequestError(error: unknown, request: Request<any>) {\n log.error(\"http\", \"unhandled-request-error\", error, { requestId: request.id });\n\n if (log.channels.length === 0) {\n console.error(`[http] unhandled-request-error (requestId: ${request.id})`, error);\n }\n}\n\n/**\n * Handle request errors\n * @internal\n */\nfunction handleRequestError(\n error: unknown,\n response: Response,\n request: Request<any>,\n): ReturnedResponse {\n // Availability floor, not a cache-policy nit: `handleRequestError` is the\n // single funnel every unhandled error in every Warlock app passes through\n // (`createRequestStore`'s catch above), and none of the branches below set\n // a `Cache-Control` header. Without this, an error response — a 500 as\n // much as a 401/403/404 carrying per-request/-user state — can be stored\n // by a shared cache or CDN and replayed to other requests/users long after\n // the condition that caused it is gone: a cached 500 becomes an outage\n // that outlives its cause; a cached 401/403 becomes a leak across users.\n // Set once, here, before any branch runs — not per branch — because the\n // branches below do not partition by status. `ResourceNotFoundError`,\n // `UnAuthorizedError`, `ForbiddenError`, `BadRequestError` and `ServerError`\n // all extend `HttpError`, so the `HttpError` branch answers for every one of\n // them, and a raw `HttpError` can carry any caller-chosen status, 4xx or 5xx.\n // Gating the floor on the eventual status would mean re-deriving that status\n // per branch — one rule meeting one form while others reach the same output.\n // Applying it once, unconditionally, is both simpler and safer.\n response.header(\"Cache-Control\", \"private, no-store\");\n\n if (error instanceof HttpError) {\n // 5xx HttpErrors (e.g. `ServerError`) are server faults: send them to the\n // logger channels too, tagged with the request id.\n if (error.status >= 500) {\n log.error(\"http\", \"request-error\", error, { requestId: request.id });\n }\n\n const payload: GenericObject = {\n error: error.message,\n };\n if (error.payload) {\n payload.payload = error.payload;\n }\n\n if (environment() === \"development\") {\n payload.stack = error.stack;\n }\n\n return response.setStatusCode(error.status).send(payload);\n }\n\n if (error instanceof DatabaseWriterValidationError) {\n log.error(\"http\", \"model-write-validation-error\", error, {\n requestId: request.id,\n errors: error.errors,\n });\n\n const status = config.get(\"http.modelValidationErrorStatus\", 500);\n\n // A 4xx status means the app treats this as the client's fault, so the\n // field errors are useful to it; a 5xx stays opaque.\n if (status < 500) {\n return response.setStatusCode(status).send({\n errors: error.errors,\n });\n }\n\n return response.setStatusCode(status).send({\n error: \"Internal server error.\",\n });\n }\n\n // Last resort: the error matched none of the known shapes above, so the\n // client gets a deliberately opaque message. Without this line the error\n // itself is discarded here — no stack, no message, nothing in any log — and\n // an unrecognised failure becomes indistinguishable from a working server\n // returning 500. Never swallow the only copy of an error (`65e476ee`).\n reportUnhandledRequestError(error, request);\n\n return response.serverError({\n error: \"Internal server error.\",\n });\n}\n\n/**\n * Translate a keyword (uses request context for locale)\n */\nexport function t(keyword: string, placeholders?: any) {\n return (\n requestContextInstance.getRequest()?.trans(keyword, placeholders) ||\n trans(keyword, placeholders)\n );\n}\n\n// `fromRequest` was removed in v5. It cached computed values as dynamic\n// properties on the Request instance, which only compiled because of the\n// `[key: string]: any` index signature that v5 deletes (eed20184). Use\n// `requestMemo(key, fn)` from `../context/request-memo` instead — same\n// per-request lifetime, single-flight, and it never touches the Request object.\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAS,qBAAqB,SAAkB,UAAoB;CAClE,MAAM,kBAAkB,OAAO,IAAI,kBAAkB,CAAC,CAAwB;CAE9E,IAAI,gBAAgB,YAAY,OAAO;CAEvC,MAAM,aAAa,gBAAgB,UAAU;CAE7C,SAAS,OAAO,YAAY,QAAQ,EAAE;AACxC;;;;;;AAOA,SAAgB,mBACd,SACA,UAC2B;CAC3B,qBAAqB,SAAS,QAAQ;CAOtC,eAAe,SAAS,QAAQ;CAIhC,MAAM,mBAAmB,eAAe,YAAY;EAAE;EAAS;CAAS,CAAC;CAGzE,OAAO,eAAe,OAAO,kBAAkB,YAAY;EACzD,IAAI;GAOF,MAAM,oBAAoB,MAAM,oBAAoB,SAAS,UAAU,IAAI,WAAW;IACpF,IAAI,MACF,QACA,sCACA,IAAI,MAAM,8CAA8C,QAAQ,CAClE;GACF,CAAC;GAED,IAAI,mBACF,OAAO;GAIT,MAAM,SAAS,MAAM,QAAQ,cAAc;GAE3C,IAAI,QACF,OAAO;GAIT,QAAQ,QAAQ,mBAAmB,QAAQ,KAAK;GAEhD,MAAM,UAAU,QAAQ,WAAW;GAEnC,QAAQ,IAAI,qBAAqB,MAAM;GAEvC,MAAM,iBAAiB,iBAAiB;GACxC,MAAM,mBAAmB,iBAAiB,YAAY,IAAI,IAAI;GAE9D,MAAM,SAAS,MAAM,QAAQ;IAAE;IAAS;GAAS,CAAC;GAElD,IAAI,gBACF,cAAc,oBAAoB,OAAO,GAAG;IAC1C,MAAM;IACN,YAAY,YAAY,IAAI,IAAI;GAClC,CAAC;GAGH,QAAQ,IAAI,iCAAiC,SAAS;GAEtD,QAAQ,QAAQ,kBAAkB,QAAQ,KAAK;GAE/C,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,IAAI,OAAO,OAAO;GAC1B,OAAO,mBAAmB,OAAO,UAAU,OAAO;EACpD;CACF,CAAC;AACH;;;;;;;AAQA,SAAS,4BAA4B,OAAgB,SAAuB;CAC1E,IAAI,MAAM,QAAQ,2BAA2B,OAAO,EAAE,WAAW,QAAQ,GAAG,CAAC;CAE7E,IAAI,IAAI,SAAS,WAAW,GAC1B,QAAQ,MAAM,8CAA8C,QAAQ,GAAG,IAAI,KAAK;AAEpF;;;;;AAMA,SAAS,mBACP,OACA,UACA,SACkB;CAiBlB,SAAS,OAAO,iBAAiB,mBAAmB;CAEpD,IAAI,iBAAiB,WAAW;EAG9B,IAAI,MAAM,UAAU,KAClB,IAAI,MAAM,QAAQ,iBAAiB,OAAO,EAAE,WAAW,QAAQ,GAAG,CAAC;EAGrE,MAAM,UAAyB,EAC7B,OAAO,MAAM,QACf;EACA,IAAI,MAAM,SACR,QAAQ,UAAU,MAAM;EAG1B,IAAI,YAAY,MAAM,eACpB,QAAQ,QAAQ,MAAM;EAGxB,OAAO,SAAS,cAAc,MAAM,MAAM,CAAC,CAAC,KAAK,OAAO;CAC1D;CAEA,IAAI,iBAAiB,+BAA+B;EAClD,IAAI,MAAM,QAAQ,gCAAgC,OAAO;GACvD,WAAW,QAAQ;GACnB,QAAQ,MAAM;EAChB,CAAC;EAED,MAAM,SAAS,OAAO,IAAI,mCAAmC,GAAG;EAIhE,IAAI,SAAS,KACX,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,KAAK,EACzC,QAAQ,MAAM,OAChB,CAAC;EAGH,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,KAAK,EACzC,OAAO,yBACT,CAAC;CACH;CAOA,4BAA4B,OAAO,OAAO;CAE1C,OAAO,SAAS,YAAY,EAC1B,OAAO,yBACT,CAAC;AACH;;;;AAKA,SAAgB,EAAE,SAAiB,cAAoB;CACrD,OACEA,eAAuB,WAAW,CAAC,EAAE,MAAM,SAAS,YAAY,KAChE,MAAM,SAAS,YAAY;AAE/B"}
1
+ {"version":3,"file":"inject-request-context.mjs","names":["requestContextInstance"],"sources":["../../../../../../../../core/src/http/middleware/inject-request-context.ts"],"sourcesContent":["/**\n * Request Context Middleware\n *\n * Creates a unified context for each request using the ContextManager.\n * All framework contexts (request, storage, database) are available throughout the request lifecycle.\n */\nimport { trans } from \"@mongez/localization\";\nimport { type GenericObject } from \"@mongez/reinforcements\";\nimport { DatabaseWriterValidationError } from \"@warlock.js/cascade\";\nimport { contextManager } from \"@warlock.js/context\";\nimport { log } from \"@warlock.js/logger\";\nimport config from \"@mongez/config\";\nimport { environment } from \"../../utils\";\nimport { requestContext as requestContextInstance } from \"../context/request-context\";\nimport { applyCspHeader } from \"../csp\";\nimport { runDefaultCsrfGuard } from \"../csrf-default-guard\";\nimport { HttpError } from \"../errors\";\nimport { type Request } from \"../request\";\nimport { type Response } from \"../response\";\nimport { buildTracingContext, dispatchPhase, isTracingEnabled } from \"../tracing\";\nimport { type ReturnedResponse } from \"./../types\";\n\n// Contexts are now registered in core/context/init-contexts.ts via initializeContexts()\n\n/**\n * Echo `request.id` back as a response header so the FE / proxies / log\n * aggregators can correlate by the same value the server logs against.\n *\n * Reads the header name from `http.requestId.header` (default `X-Request-Id`).\n * Skip when `http.requestId.enabled` is explicitly false.\n */\nfunction stampRequestIdHeader(request: Request, response: Response) {\n const requestIdConfig = config.get(\"http.requestId\", {} as Record<string, any>);\n\n if (requestIdConfig.enabled === false) return;\n\n const headerName = requestIdConfig.header || \"X-Request-Id\";\n\n response.header(headerName, request.id);\n}\n\n/**\n * Create request store and execute middleware + handler\n *\n * Runs all registered contexts together using ContextManager.\n */\nexport function createRequestStore(\n request: Request<any>,\n response: Response,\n): Promise<ReturnedResponse> {\n stampRequestIdHeader(request, response);\n\n // Stamped this early — before any middleware or handler runs — so the\n // header carries the SAME nonce the web layer later reads off\n // `request.nonce` for its `<script>` tags: `request.nonce` caches on first\n // read, and this is deliberately the first read. A no-op when the app\n // hasn't opted into `http.csp` (see `csp.ts`).\n applyCspHeader(request, response);\n\n // Build all context stores using the immutable API\n // Each context defines its own store initialization via buildStore()\n const httpContextStore = contextManager.buildStores({ request, response });\n\n // Run all contexts together!\n return contextManager.runAll(httpContextStore, async () => {\n try {\n // Default CSRF-Origin guard (SECURITY, card 8a752ab2) — the earliest\n // seam common to every request, BEFORE the route's own middleware\n // (`request.runMiddleware()` below) and before any app handler, so it\n // applies even to a route that never attaches\n // `authMiddleware(\"cookie:*\")`. See `../csrf-default-guard.ts` for the\n // full scope rule and `{ csrf: false }` exemption.\n const csrfGuardResponse = await runDefaultCsrfGuard(request, response, t, (reason) => {\n log.error(\n \"http\",\n \"csrf-default-guard\",\n new Error(`Default CSRF Origin guard refused request: ${reason}`),\n );\n });\n\n if (csrfGuardResponse) {\n return csrfGuardResponse as ReturnedResponse;\n }\n\n // Run middleware chain\n const result = await request.runMiddleware();\n\n if (result) {\n return result as ReturnedResponse;\n }\n\n // Execute route handler\n request.trigger(\"executingAction\", request.route);\n\n const handler = request.getHandler();\n\n request.log(\"Executing Handler\", \"info\");\n\n const tracingEnabled = isTracingEnabled();\n const handlerStartedAt = tracingEnabled ? performance.now() : 0;\n\n const output = await handler({ request, response });\n\n if (tracingEnabled) {\n dispatchPhase(buildTracingContext(request), {\n name: \"handler\",\n durationMs: performance.now() - handlerStartedAt,\n });\n }\n\n request.log(\"Handler Executed Successfully\", \"success\");\n\n request.trigger(\"executedAction\", request.route);\n\n return output as ReturnedResponse;\n } catch (error) {\n request.log(error, \"error\");\n return handleRequestError(error, response, request);\n }\n });\n}\n\n/**\n * Unconditional floor for an unhandled request error (canon 8d3c13a8): route it\n * through the logger, and when no logger channel is configured (nothing would\n * record it) fall back to `console.error` so the only copy is never lost.\n * @internal\n */\nfunction reportUnhandledRequestError(error: unknown, request: Request<any>) {\n log.error(\"http\", \"unhandled-request-error\", error, { requestId: request.id });\n\n if (log.channels.length === 0) {\n console.error(`[http] unhandled-request-error (requestId: ${request.id})`, error);\n }\n}\n\n/**\n * Handle request errors\n * @internal\n */\nfunction handleRequestError(\n error: unknown,\n response: Response,\n request: Request<any>,\n): ReturnedResponse {\n // Availability floor, not a cache-policy nit: `handleRequestError` is the\n // single funnel every unhandled error in every Warlock app passes through\n // (`createRequestStore`'s catch above), and none of the branches below set\n // a `Cache-Control` header. Without this, an error response — a 500 as\n // much as a 401/403/404 carrying per-request/-user state — can be stored\n // by a shared cache or CDN and replayed to other requests/users long after\n // the condition that caused it is gone: a cached 500 becomes an outage\n // that outlives its cause; a cached 401/403 becomes a leak across users.\n // Set once, here, before any branch runs — not per branch — because the\n // branches below do not partition by status. `ResourceNotFoundError`,\n // `UnAuthorizedError`, `ForbiddenError`, `BadRequestError` and `ServerError`\n // all extend `HttpError`, so the `HttpError` branch answers for every one of\n // them, and a raw `HttpError` can carry any caller-chosen status, 4xx or 5xx.\n // Gating the floor on the eventual status would mean re-deriving that status\n // per branch — one rule meeting one form while others reach the same output.\n // Applying it once, unconditionally, is both simpler and safer.\n response.header(\"Cache-Control\", \"private, no-store\");\n\n if (error instanceof HttpError) {\n // 5xx HttpErrors (e.g. `ServerError`) are server faults: send them to the\n // logger channels too, tagged with the request id.\n if (error.status >= 500) {\n log.error(\"http\", \"request-error\", error, { requestId: request.id });\n }\n\n const payload: GenericObject = {\n error: error.message,\n };\n if (error.payload) {\n payload.payload = error.payload;\n }\n\n if (environment() === \"development\") {\n payload.stack = error.stack;\n }\n\n return response.setStatusCode(error.status).send(payload);\n }\n\n if (error instanceof DatabaseWriterValidationError) {\n log.error(\"http\", \"model-write-validation-error\", error, {\n requestId: request.id,\n errors: error.errors,\n });\n\n const status = config.get(\"http.modelValidationErrorStatus\", 500);\n\n // A 4xx status means the app treats this as the client's fault, so the\n // field errors are useful to it; a 5xx stays opaque.\n if (status < 500) {\n return response.setStatusCode(status).send({\n errors: error.errors,\n });\n }\n\n return response.setStatusCode(status).send({\n error: \"Internal server error.\",\n });\n }\n\n // Last resort: the error matched none of the known shapes above, so the\n // client gets a deliberately opaque message. Without this line the error\n // itself is discarded here — no stack, no message, nothing in any log — and\n // an unrecognised failure becomes indistinguishable from a working server\n // returning 500. Never swallow the only copy of an error (`65e476ee`).\n reportUnhandledRequestError(error, request);\n\n return response.serverError({\n error: \"Internal server error.\",\n });\n}\n\n/**\n * Translate a keyword (uses request context for locale)\n */\nexport function t(keyword: string, placeholders?: Record<string, unknown>): string {\n return (\n requestContextInstance.getRequest()?.trans(keyword, placeholders) ||\n trans(keyword, placeholders)\n );\n}\n\n// `fromRequest` was removed in v5. It cached computed values as dynamic\n// properties on the Request instance, which only compiled because of the\n// `[key: string]: any` index signature that v5 deletes (eed20184). Use\n// `requestMemo(key, fn)` from `../context/request-memo` instead — same\n// per-request lifetime, single-flight, and it never touches the Request object.\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAS,qBAAqB,SAAkB,UAAoB;CAClE,MAAM,kBAAkB,OAAO,IAAI,kBAAkB,CAAC,CAAwB;CAE9E,IAAI,gBAAgB,YAAY,OAAO;CAEvC,MAAM,aAAa,gBAAgB,UAAU;CAE7C,SAAS,OAAO,YAAY,QAAQ,EAAE;AACxC;;;;;;AAOA,SAAgB,mBACd,SACA,UAC2B;CAC3B,qBAAqB,SAAS,QAAQ;CAOtC,eAAe,SAAS,QAAQ;CAIhC,MAAM,mBAAmB,eAAe,YAAY;EAAE;EAAS;CAAS,CAAC;CAGzE,OAAO,eAAe,OAAO,kBAAkB,YAAY;EACzD,IAAI;GAOF,MAAM,oBAAoB,MAAM,oBAAoB,SAAS,UAAU,IAAI,WAAW;IACpF,IAAI,MACF,QACA,sCACA,IAAI,MAAM,8CAA8C,QAAQ,CAClE;GACF,CAAC;GAED,IAAI,mBACF,OAAO;GAIT,MAAM,SAAS,MAAM,QAAQ,cAAc;GAE3C,IAAI,QACF,OAAO;GAIT,QAAQ,QAAQ,mBAAmB,QAAQ,KAAK;GAEhD,MAAM,UAAU,QAAQ,WAAW;GAEnC,QAAQ,IAAI,qBAAqB,MAAM;GAEvC,MAAM,iBAAiB,iBAAiB;GACxC,MAAM,mBAAmB,iBAAiB,YAAY,IAAI,IAAI;GAE9D,MAAM,SAAS,MAAM,QAAQ;IAAE;IAAS;GAAS,CAAC;GAElD,IAAI,gBACF,cAAc,oBAAoB,OAAO,GAAG;IAC1C,MAAM;IACN,YAAY,YAAY,IAAI,IAAI;GAClC,CAAC;GAGH,QAAQ,IAAI,iCAAiC,SAAS;GAEtD,QAAQ,QAAQ,kBAAkB,QAAQ,KAAK;GAE/C,OAAO;EACT,SAAS,OAAO;GACd,QAAQ,IAAI,OAAO,OAAO;GAC1B,OAAO,mBAAmB,OAAO,UAAU,OAAO;EACpD;CACF,CAAC;AACH;;;;;;;AAQA,SAAS,4BAA4B,OAAgB,SAAuB;CAC1E,IAAI,MAAM,QAAQ,2BAA2B,OAAO,EAAE,WAAW,QAAQ,GAAG,CAAC;CAE7E,IAAI,IAAI,SAAS,WAAW,GAC1B,QAAQ,MAAM,8CAA8C,QAAQ,GAAG,IAAI,KAAK;AAEpF;;;;;AAMA,SAAS,mBACP,OACA,UACA,SACkB;CAiBlB,SAAS,OAAO,iBAAiB,mBAAmB;CAEpD,IAAI,iBAAiB,WAAW;EAG9B,IAAI,MAAM,UAAU,KAClB,IAAI,MAAM,QAAQ,iBAAiB,OAAO,EAAE,WAAW,QAAQ,GAAG,CAAC;EAGrE,MAAM,UAAyB,EAC7B,OAAO,MAAM,QACf;EACA,IAAI,MAAM,SACR,QAAQ,UAAU,MAAM;EAG1B,IAAI,YAAY,MAAM,eACpB,QAAQ,QAAQ,MAAM;EAGxB,OAAO,SAAS,cAAc,MAAM,MAAM,CAAC,CAAC,KAAK,OAAO;CAC1D;CAEA,IAAI,iBAAiB,+BAA+B;EAClD,IAAI,MAAM,QAAQ,gCAAgC,OAAO;GACvD,WAAW,QAAQ;GACnB,QAAQ,MAAM;EAChB,CAAC;EAED,MAAM,SAAS,OAAO,IAAI,mCAAmC,GAAG;EAIhE,IAAI,SAAS,KACX,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,KAAK,EACzC,QAAQ,MAAM,OAChB,CAAC;EAGH,OAAO,SAAS,cAAc,MAAM,CAAC,CAAC,KAAK,EACzC,OAAO,yBACT,CAAC;CACH;CAOA,4BAA4B,OAAO,OAAO;CAE1C,OAAO,SAAS,YAAY,EAC1B,OAAO,yBACT,CAAC;AACH;;;;AAKA,SAAgB,EAAE,SAAiB,cAAgD;CACjF,OACEA,eAAuB,WAAW,CAAC,EAAE,MAAM,SAAS,YAAY,KAChE,MAAM,SAAS,YAAY;AAE/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.middleware.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/rate-limit.middleware.ts"],"mappings":";;;;;;KAQY,gBAAA;EAAgB;;;EAI1B,GAAA;EAIA;;;EAAA,QAAA;EAaA;;;;;;AAU0C;EAf1C,YAAA,IAAgB,OAAA,EAAS,OAAA;EAoEQ;;;;EA/DjC,GAAA;EA+D8D;;AAAU;;EA1DxE,MAAA;;;;;EAKA,YAAA,cAA0B,OAAA,EAAS,OAAO;AAAA;;;;;;;;;;;;;;;;;;;;;iBAqD5B,mBAAA,CAAoB,OAAA,EAAS,gBAAA,GAAmB,UAAU"}
1
+ {"version":3,"file":"rate-limit.middleware.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/rate-limit.middleware.ts"],"mappings":";;;;;;KASY,gBAAA;EAAgB;;;EAI1B,GAAA;EAIA;;;EAAA,QAAA;EAaA;;;;;;AAU0C;EAf1C,YAAA,IAAgB,OAAA,EAAS,OAAA;EAqEQ;;;;EAhEjC,GAAA;EAgE8D;;AAAU;;EA3DxE,MAAA;;;;;EAKA,YAAA,cAA0B,OAAA,EAAS,OAAO;AAAA;;;;;;;;;;;;;;;;;;;;;iBAsD5B,mBAAA,CAAoB,OAAA,EAAS,gBAAA,GAAmB,UAAU"}
@@ -1,8 +1,10 @@
1
1
  import { HttpErrorCodes } from "../error-codes.mjs";
2
2
  import { t } from "./inject-request-context.mjs";
3
+ import { normalizeIP } from "@fastify/rate-limit";
3
4
 
4
5
  //#region ../core/src/http/middleware/rate-limit.middleware.ts
5
6
  const buckets = /* @__PURE__ */ new Map();
7
+ const defaultIpv6Subnet = 64;
6
8
  /**
7
9
  * Each `rateLimitMiddleware()` call is its own limiter. Page actions share one
8
10
  * route, so without this two limits on the same page (say `like` and
@@ -53,7 +55,9 @@ function rateLimitMiddleware(options) {
53
55
  if (id !== void 0 && id !== null && id !== "") userKey = `user:${String(id)}`;
54
56
  else if (options.guests === "skip") return;
55
57
  }
56
- const groupKey = options.keyGenerator?.(request) || userKey || request.detectIp() || "unknown";
58
+ const ip = request.detectIp();
59
+ const ipKey = ip ? normalizeIP(ip, defaultIpv6Subnet) : "unknown";
60
+ const groupKey = options.keyGenerator?.(request) || userKey || ipKey;
57
61
  const cacheKey = `${limiterId}:${request.route.path}:${groupKey}`;
58
62
  let bucket = buckets.get(cacheKey);
59
63
  if (!bucket || bucket.resetAt <= now) {
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.middleware.mjs","names":[],"sources":["../../../../../../../../core/src/http/middleware/rate-limit.middleware.ts"],"sourcesContent":["import type { Middleware } from \"../../router\";\nimport { HttpErrorCodes } from \"../error-codes\";\nimport type { Request } from \"../request\";\nimport { t } from \"./inject-request-context\";\n\n/**\n * Options for the per-route rate limit middleware.\n */\nexport type RateLimitOptions = {\n /**\n * Max requests allowed within the time window.\n */\n max: number;\n /**\n * Time window in milliseconds.\n */\n duration: number;\n /**\n * Group key generator. Defaults to the client IP. Override to scope per-user,\n * per-organization, per-tenant, etc.\n *\n * @example\n * keyGenerator: (request) => request.locals.user?.id ?? request.ip,\n */\n keyGenerator?: (request: Request) => string;\n /**\n * Key the bucket to the signed-in user (`request.locals.user.id`). Works on\n * routes and page actions alike. `keyGenerator` wins when both are set.\n */\n key?: \"user\";\n /**\n * With `key: \"user\"`, what to do for unauthenticated requests: bucket them\n * by IP (default) or skip limiting entirely.\n */\n guests?: \"ip\" | \"skip\";\n /**\n * Override the default error message. Pass a function to build it per\n * request, e.g. to translate it into the visitor's locale.\n */\n errorMessage?: string | ((request: Request) => string);\n};\n\ntype Bucket = {\n count: number;\n resetAt: number;\n};\n\nconst buckets = new Map<string, Bucket>();\n\n/**\n * Each `rateLimitMiddleware()` call is its own limiter. Page actions share one\n * route, so without this two limits on the same page (say `like` and\n * `comment`) would count into one bucket.\n */\nlet nextLimiterId = 0;\n\n/**\n * Sweep expired buckets so the Map doesn't grow unbounded with one-shot keys\n * (rare per-user keys, dynamic path parameters in `keyGenerator`, etc.).\n * Called opportunistically, at most once per minute, not on a timer — keeps\n * the module side-effect-free for testing.\n */\nlet lastPruneAt = 0;\n\nfunction pruneExpired(now: number) {\n for (const [key, bucket] of buckets) {\n if (bucket.resetAt <= now) {\n buckets.delete(key);\n }\n }\n}\n\n/**\n * Per-route / per-group rate limit. Layers on top of `@fastify/rate-limit`'s\n * global cap (configured via `http.rateLimit`) — both run, and either can\n * 429. Use this for endpoints that need a tighter cap than the global default:\n * login, OTP request, password reset, expensive search, AI completions.\n *\n * The counter of THIS middleware always lives in-process. With `N` replicas\n * its effective cap is `N × max`. The global limit can be shared across\n * replicas by setting `http.rateLimit.redis` (an ioredis instance); that\n * does not affect this per-route middleware.\n *\n * Sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` on\n * every response; adds `Retry-After` (seconds) on a 429.\n *\n * @example\n * import { middleware } from \"@warlock.js/core\";\n * router.post(\"/auth/login\", loginController, {\n * middleware: [middleware.rateLimit({ max: 5, duration: 60_000 })],\n * });\n */\nexport function rateLimitMiddleware(options: RateLimitOptions): Middleware {\n const limiterId = nextLimiterId++;\n\n return ({ request, response }) => {\n const now = Date.now();\n\n if (now - lastPruneAt > 60_000) {\n pruneExpired(now);\n lastPruneAt = now;\n }\n\n let userKey: string | undefined;\n\n if (options.key === \"user\" && !options.keyGenerator) {\n const id = (request.locals as { user?: { id?: unknown } } | undefined)?.user?.id;\n\n if (id !== undefined && id !== null && id !== \"\") {\n userKey = `user:${String(id)}`;\n } else if (options.guests === \"skip\") {\n return;\n }\n }\n\n const groupKey = options.keyGenerator?.(request) || userKey || request.detectIp() || \"unknown\";\n const cacheKey = `${limiterId}:${request.route.path}:${groupKey}`;\n\n let bucket = buckets.get(cacheKey);\n\n if (!bucket || bucket.resetAt <= now) {\n bucket = { count: 0, resetAt: now + options.duration };\n buckets.set(cacheKey, bucket);\n }\n\n bucket.count++;\n\n const remaining = Math.max(0, options.max - bucket.count);\n const retryAfter = Math.max(1, Math.ceil((bucket.resetAt - now) / 1000));\n\n response.header(\"X-RateLimit-Limit\", options.max);\n response.header(\"X-RateLimit-Remaining\", remaining);\n response.header(\"X-RateLimit-Reset\", Math.ceil(bucket.resetAt / 1000));\n\n if (bucket.count > options.max) {\n response.header(\"Retry-After\", retryAfter);\n\n return response.tooManyRequests({\n error:\n (typeof options.errorMessage === \"function\"\n ? options.errorMessage(request)\n : options.errorMessage) || t(\"http.rateLimitExceeded\"),\n errorCode: HttpErrorCodes.RateLimitExceeded,\n });\n }\n };\n}\n"],"mappings":";;;;AA+CA,MAAM,0BAAU,IAAI,IAAoB;;;;;;AAOxC,IAAI,gBAAgB;;;;;;;AAQpB,IAAI,cAAc;AAElB,SAAS,aAAa,KAAa;CACjC,KAAK,MAAM,CAAC,KAAK,WAAW,SAC1B,IAAI,OAAO,WAAW,KACpB,QAAQ,OAAO,GAAG;AAGxB;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,oBAAoB,SAAuC;CACzE,MAAM,YAAY;CAElB,QAAQ,EAAE,SAAS,eAAe;EAChC,MAAM,MAAM,KAAK,IAAI;EAErB,IAAI,MAAM,cAAc,KAAQ;GAC9B,aAAa,GAAG;GAChB,cAAc;EAChB;EAEA,IAAI;EAEJ,IAAI,QAAQ,QAAQ,UAAU,CAAC,QAAQ,cAAc;GACnD,MAAM,KAAM,QAAQ,QAAoD,MAAM;GAE9E,IAAI,OAAO,UAAa,OAAO,QAAQ,OAAO,IAC5C,UAAU,QAAQ,OAAO,EAAE;QACtB,IAAI,QAAQ,WAAW,QAC5B;EAEJ;EAEA,MAAM,WAAW,QAAQ,eAAe,OAAO,KAAK,WAAW,QAAQ,SAAS,KAAK;EACrF,MAAM,WAAW,GAAG,UAAU,GAAG,QAAQ,MAAM,KAAK,GAAG;EAEvD,IAAI,SAAS,QAAQ,IAAI,QAAQ;EAEjC,IAAI,CAAC,UAAU,OAAO,WAAW,KAAK;GACpC,SAAS;IAAE,OAAO;IAAG,SAAS,MAAM,QAAQ;GAAS;GACrD,QAAQ,IAAI,UAAU,MAAM;EAC9B;EAEA,OAAO;EAEP,MAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,MAAM,OAAO,KAAK;EACxD,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,UAAU,OAAO,GAAI,CAAC;EAEvE,SAAS,OAAO,qBAAqB,QAAQ,GAAG;EAChD,SAAS,OAAO,yBAAyB,SAAS;EAClD,SAAS,OAAO,qBAAqB,KAAK,KAAK,OAAO,UAAU,GAAI,CAAC;EAErE,IAAI,OAAO,QAAQ,QAAQ,KAAK;GAC9B,SAAS,OAAO,eAAe,UAAU;GAEzC,OAAO,SAAS,gBAAgB;IAC9B,QACG,OAAO,QAAQ,iBAAiB,aAC7B,QAAQ,aAAa,OAAO,IAC5B,QAAQ,iBAAiB,EAAE,wBAAwB;IACzD;GACF,CAAC;EACH;CACF;AACF"}
1
+ {"version":3,"file":"rate-limit.middleware.mjs","names":[],"sources":["../../../../../../../../core/src/http/middleware/rate-limit.middleware.ts"],"sourcesContent":["import { normalizeIP } from \"@fastify/rate-limit\";\nimport type { Middleware } from \"../../router\";\nimport { HttpErrorCodes } from \"../error-codes\";\nimport type { Request } from \"../request\";\nimport { t } from \"./inject-request-context\";\n\n/**\n * Options for the per-route rate limit middleware.\n */\nexport type RateLimitOptions = {\n /**\n * Max requests allowed within the time window.\n */\n max: number;\n /**\n * Time window in milliseconds.\n */\n duration: number;\n /**\n * Group key generator. Defaults to the client IP. Override to scope per-user,\n * per-organization, per-tenant, etc.\n *\n * @example\n * keyGenerator: (request) => request.locals.user?.id ?? request.ip,\n */\n keyGenerator?: (request: Request) => string;\n /**\n * Key the bucket to the signed-in user (`request.locals.user.id`). Works on\n * routes and page actions alike. `keyGenerator` wins when both are set.\n */\n key?: \"user\";\n /**\n * With `key: \"user\"`, what to do for unauthenticated requests: bucket them\n * by IP (default) or skip limiting entirely.\n */\n guests?: \"ip\" | \"skip\";\n /**\n * Override the default error message. Pass a function to build it per\n * request, e.g. to translate it into the visitor's locale.\n */\n errorMessage?: string | ((request: Request) => string);\n};\n\ntype Bucket = {\n count: number;\n resetAt: number;\n};\n\nconst buckets = new Map<string, Bucket>();\nconst defaultIpv6Subnet = 64;\n\n/**\n * Each `rateLimitMiddleware()` call is its own limiter. Page actions share one\n * route, so without this two limits on the same page (say `like` and\n * `comment`) would count into one bucket.\n */\nlet nextLimiterId = 0;\n\n/**\n * Sweep expired buckets so the Map doesn't grow unbounded with one-shot keys\n * (rare per-user keys, dynamic path parameters in `keyGenerator`, etc.).\n * Called opportunistically, at most once per minute, not on a timer — keeps\n * the module side-effect-free for testing.\n */\nlet lastPruneAt = 0;\n\nfunction pruneExpired(now: number) {\n for (const [key, bucket] of buckets) {\n if (bucket.resetAt <= now) {\n buckets.delete(key);\n }\n }\n}\n\n/**\n * Per-route / per-group rate limit. Layers on top of `@fastify/rate-limit`'s\n * global cap (configured via `http.rateLimit`) — both run, and either can\n * 429. Use this for endpoints that need a tighter cap than the global default:\n * login, OTP request, password reset, expensive search, AI completions.\n *\n * The counter of THIS middleware always lives in-process. With `N` replicas\n * its effective cap is `N × max`. The global limit can be shared across\n * replicas by setting `http.rateLimit.redis` (an ioredis instance); that\n * does not affect this per-route middleware.\n *\n * Sets `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` on\n * every response; adds `Retry-After` (seconds) on a 429.\n *\n * @example\n * import { middleware } from \"@warlock.js/core\";\n * router.post(\"/auth/login\", loginController, {\n * middleware: [middleware.rateLimit({ max: 5, duration: 60_000 })],\n * });\n */\nexport function rateLimitMiddleware(options: RateLimitOptions): Middleware {\n const limiterId = nextLimiterId++;\n\n return ({ request, response }) => {\n const now = Date.now();\n\n if (now - lastPruneAt > 60_000) {\n pruneExpired(now);\n lastPruneAt = now;\n }\n\n let userKey: string | undefined;\n\n if (options.key === \"user\" && !options.keyGenerator) {\n const id = (request.locals as { user?: { id?: unknown } } | undefined)?.user?.id;\n\n if (id !== undefined && id !== null && id !== \"\") {\n userKey = `user:${String(id)}`;\n } else if (options.guests === \"skip\") {\n return;\n }\n }\n\n const ip = request.detectIp();\n const ipKey = ip ? normalizeIP(ip, defaultIpv6Subnet) : \"unknown\";\n const groupKey = options.keyGenerator?.(request) || userKey || ipKey;\n const cacheKey = `${limiterId}:${request.route.path}:${groupKey}`;\n\n let bucket = buckets.get(cacheKey);\n\n if (!bucket || bucket.resetAt <= now) {\n bucket = { count: 0, resetAt: now + options.duration };\n buckets.set(cacheKey, bucket);\n }\n\n bucket.count++;\n\n const remaining = Math.max(0, options.max - bucket.count);\n const retryAfter = Math.max(1, Math.ceil((bucket.resetAt - now) / 1000));\n\n response.header(\"X-RateLimit-Limit\", options.max);\n response.header(\"X-RateLimit-Remaining\", remaining);\n response.header(\"X-RateLimit-Reset\", Math.ceil(bucket.resetAt / 1000));\n\n if (bucket.count > options.max) {\n response.header(\"Retry-After\", retryAfter);\n\n return response.tooManyRequests({\n error:\n (typeof options.errorMessage === \"function\"\n ? options.errorMessage(request)\n : options.errorMessage) || t(\"http.rateLimitExceeded\"),\n errorCode: HttpErrorCodes.RateLimitExceeded,\n });\n }\n };\n}\n"],"mappings":";;;;;AAgDA,MAAM,0BAAU,IAAI,IAAoB;AACxC,MAAM,oBAAoB;;;;;;AAO1B,IAAI,gBAAgB;;;;;;;AAQpB,IAAI,cAAc;AAElB,SAAS,aAAa,KAAa;CACjC,KAAK,MAAM,CAAC,KAAK,WAAW,SAC1B,IAAI,OAAO,WAAW,KACpB,QAAQ,OAAO,GAAG;AAGxB;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,oBAAoB,SAAuC;CACzE,MAAM,YAAY;CAElB,QAAQ,EAAE,SAAS,eAAe;EAChC,MAAM,MAAM,KAAK,IAAI;EAErB,IAAI,MAAM,cAAc,KAAQ;GAC9B,aAAa,GAAG;GAChB,cAAc;EAChB;EAEA,IAAI;EAEJ,IAAI,QAAQ,QAAQ,UAAU,CAAC,QAAQ,cAAc;GACnD,MAAM,KAAM,QAAQ,QAAoD,MAAM;GAE9E,IAAI,OAAO,UAAa,OAAO,QAAQ,OAAO,IAC5C,UAAU,QAAQ,OAAO,EAAE;QACtB,IAAI,QAAQ,WAAW,QAC5B;EAEJ;EAEA,MAAM,KAAK,QAAQ,SAAS;EAC5B,MAAM,QAAQ,KAAK,YAAY,IAAI,iBAAiB,IAAI;EACxD,MAAM,WAAW,QAAQ,eAAe,OAAO,KAAK,WAAW;EAC/D,MAAM,WAAW,GAAG,UAAU,GAAG,QAAQ,MAAM,KAAK,GAAG;EAEvD,IAAI,SAAS,QAAQ,IAAI,QAAQ;EAEjC,IAAI,CAAC,UAAU,OAAO,WAAW,KAAK;GACpC,SAAS;IAAE,OAAO;IAAG,SAAS,MAAM,QAAQ;GAAS;GACrD,QAAQ,IAAI,UAAU,MAAM;EAC9B;EAEA,OAAO;EAEP,MAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,MAAM,OAAO,KAAK;EACxD,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,UAAU,OAAO,GAAI,CAAC;EAEvE,SAAS,OAAO,qBAAqB,QAAQ,GAAG;EAChD,SAAS,OAAO,yBAAyB,SAAS;EAClD,SAAS,OAAO,qBAAqB,KAAK,KAAK,OAAO,UAAU,GAAI,CAAC;EAErE,IAAI,OAAO,QAAQ,QAAQ,KAAK;GAC9B,SAAS,OAAO,eAAe,UAAU;GAEzC,OAAO,SAAS,gBAAgB;IAC9B,QACG,OAAO,QAAQ,iBAAiB,aAC7B,QAAQ,aAAa,OAAO,IAC5B,QAAQ,iBAAiB,EAAE,wBAAwB;IACzD;GACF,CAAC;EACH;CACF;AACF"}
@@ -3,7 +3,6 @@ import { UploadedFile } from "./uploaded-file.mjs";
3
3
  import { DecodedAccessToken, RequestEvent, RequestLocals } from "./types.mjs";
4
4
  import { Middleware, RequestHandler, Route } from "../router/types.mjs";
5
5
  import { BaseValidator } from "@warlock.js/seal";
6
- import { trans } from "@mongez/localization";
7
6
  import { LogLevel } from "@warlock.js/logger";
8
7
  import { FastifyRequest } from "fastify";
9
8
  import { IncomingHttpHeaders } from "node:http2";
@@ -11,6 +10,8 @@ import { IncomingHttpHeaders } from "node:http2";
11
10
  //#region ../core/src/http/request.d.ts
12
11
  type StandardHeaders = { [K in keyof IncomingHttpHeaders as string extends K ? never : number extends K ? never : K]: IncomingHttpHeaders[K] };
13
12
  type HeaderKeys = keyof StandardHeaders;
13
+ /** A request-bound translator: keyword in, translated string out. */
14
+ type RequestTranslator = (keyword: string, placeholders?: Record<string, unknown>) => string;
14
15
  declare class Request<RequestValidation = any> {
15
16
  /**
16
17
  * Underlying Fastify request — a public escape hatch to capabilities the
@@ -54,24 +55,6 @@ declare class Request<RequestValidation = any> {
54
55
  */
55
56
  get decodedAccessToken(): DecodedAccessToken | undefined;
56
57
  set decodedAccessToken(value: DecodedAccessToken | undefined);
57
- /**
58
- * REMOVED in 5.12.0 — the authenticated user now lives at
59
- * `request.locals.user`, a key `@warlock.js/auth` declares via module
60
- * augmentation on `RequestLocals` and writes from its middleware after a
61
- * successful token resolution. `RequestUser` moved out of core to
62
- * `@warlock.js/auth` alongside it.
63
- *
64
- * This getter is a development-time diagnostic only, kept for one release
65
- * so a call site that still reads `request.user` fails loudly at runtime
66
- * instead of silently reading `undefined`. It is typed `never` so it
67
- * cannot reintroduce an auth-shaped type into core, and it throws
68
- * unconditionally outside production so the failure is impossible to miss
69
- * in local dev — see `RequestUserMovedError`.
70
- *
71
- * There is no setter: nothing in core or downstream packages should ever
72
- * assign to `request.user` again.
73
- */
74
- get user(): never;
75
58
  /**
76
59
  * Private, server-only, per-request data bag.
77
60
  *
@@ -134,14 +117,14 @@ declare class Request<RequestValidation = any> {
134
117
  */
135
118
  static current: Request;
136
119
  /**
137
- * Translation method
138
- * Type of it is the same as the type of trans function
120
+ * Translate a keyword in this request's locale. Returns the translated
121
+ * string, or the keyword itself when no translation exists.
139
122
  */
140
- trans: ReturnType<typeof trans>;
123
+ trans: RequestTranslator;
141
124
  /**
142
125
  * Alias to trans method
143
126
  */
144
- t: ReturnType<typeof trans>;
127
+ t: RequestTranslator;
145
128
  /**
146
129
  * Locale code
147
130
  */
@@ -611,5 +594,5 @@ declare class Request<RequestValidation = any> {
611
594
  setHeader(key: HeaderKeys, value: string): this;
612
595
  }
613
596
  //#endregion
614
- export { Request };
597
+ export { Request, RequestTranslator };
615
598
  //# sourceMappingURL=request.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"request.d.mts","names":[],"sources":["../../../../../../../core/src/http/request.ts"],"mappings":"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"}
1
+ {"version":3,"file":"request.d.mts","names":[],"sources":["../../../../../../../core/src/http/request.ts"],"mappings":"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"}
@@ -1,11 +1,10 @@
1
1
  import { config } from "../config/config-getter.mjs";
2
2
  import { LOCALE_COOKIE_NAME, LOCALE_PREFERENCE_COOKIE_NAME, resolveLocaleConfiguration } from "../config/locale-configuration.mjs";
3
- import { CookieJarUnavailableError, RequestUserMovedError } from "./errors/errors.mjs";
3
+ import { CookieJarUnavailableError } from "./errors/errors.mjs";
4
4
  import { deriveTraceId } from "./tracing/trace-id.mjs";
5
5
  import { buildTracingContext, dispatchPhase, isTracingEnabled } from "./tracing/tracing-dispatcher.mjs";
6
6
  import "./tracing/index.mjs";
7
7
  import { createRequestStore } from "./middleware/inject-request-context.mjs";
8
- import { Application } from "../application/application.mjs";
9
8
  import { Response } from "./response.mjs";
10
9
  import { validateAll } from "../validation/validateAll.mjs";
11
10
  import { UploadedFile } from "./uploaded-file.mjs";
@@ -45,26 +44,6 @@ var Request = class Request {
45
44
  this.locals.authDerived = true;
46
45
  }
47
46
  /**
48
- * REMOVED in 5.12.0 — the authenticated user now lives at
49
- * `request.locals.user`, a key `@warlock.js/auth` declares via module
50
- * augmentation on `RequestLocals` and writes from its middleware after a
51
- * successful token resolution. `RequestUser` moved out of core to
52
- * `@warlock.js/auth` alongside it.
53
- *
54
- * This getter is a development-time diagnostic only, kept for one release
55
- * so a call site that still reads `request.user` fails loudly at runtime
56
- * instead of silently reading `undefined`. It is typed `never` so it
57
- * cannot reintroduce an auth-shaped type into core, and it throws
58
- * unconditionally outside production so the failure is impossible to miss
59
- * in local dev — see `RequestUserMovedError`.
60
- *
61
- * There is no setter: nothing in core or downstream packages should ever
62
- * assign to `request.user` again.
63
- */
64
- get user() {
65
- if (Application.isDevelopment) throw new RequestUserMovedError();
66
- }
67
- /**
68
47
  * Per-request Content-Security-Policy nonce — a fresh, unguessable value
69
48
  * the web layer hands to `<Scripts nonce={...} />` (the inline payload
70
49
  * script) and to the `Content-Security-Policy` header, so a strict