@warlock.js/core 5.15.0 → 5.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/esm/cli/commands/migrate.command.mjs +5 -1
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/seed.command.mjs +2 -1
- package/esm/cli/commands/seed.command.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs +16 -23
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/config/locale-configuration.mjs +7 -1
- package/esm/config/locale-configuration.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +1 -18
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/read-bound-port.mjs +32 -0
- package/esm/connectors/read-bound-port.mjs.map +1 -0
- package/esm/dev-server/development-server.mjs +5 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/extract-translation-keys.mjs +68 -0
- package/esm/dev-server/extract-translation-keys.mjs.map +1 -0
- package/esm/dev-server/translation-keys-sources.mjs +19 -0
- package/esm/dev-server/translation-keys-sources.mjs.map +1 -0
- package/esm/dev-server/type-generator.mjs +64 -11
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/generations/features/shared/merge-web-sitemap-config.mjs +118 -0
- package/esm/generations/features/shared/merge-web-sitemap-config.mjs.map +1 -0
- package/esm/generations/features/sitemap.feature.mjs +50 -47
- package/esm/generations/features/sitemap.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +7 -6
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/csrf-default-guard.d.mts +32 -0
- package/esm/http/csrf-default-guard.d.mts.map +1 -0
- package/esm/http/csrf-default-guard.mjs +156 -0
- package/esm/http/csrf-default-guard.mjs.map +1 -0
- package/esm/http/csrf-origin-policy.d.mts +53 -0
- package/esm/http/csrf-origin-policy.d.mts.map +1 -0
- package/esm/http/csrf-origin-policy.mjs +104 -0
- package/esm/http/csrf-origin-policy.mjs.map +1 -0
- package/esm/http/error-codes.d.mts +14 -1
- package/esm/http/error-codes.d.mts.map +1 -1
- package/esm/http/error-codes.mjs +13 -0
- package/esm/http/error-codes.mjs.map +1 -1
- package/esm/http/index.d.mts +8 -1
- package/esm/http/index.mjs +7 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs +1 -1
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +6 -0
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/ip-filter.middleware.mjs +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs +1 -1
- package/esm/http/parse-urlencoded-body.mjs +27 -0
- package/esm/http/parse-urlencoded-body.mjs.map +1 -0
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +8 -0
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/request.d.mts +2 -2
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +7 -3
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +9 -2
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +18 -4
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/uploads/detect-image-format.mjs +59 -0
- package/esm/http/uploads/detect-image-format.mjs.map +1 -0
- package/esm/http/uploads/generate-image-variant.mjs +46 -0
- package/esm/http/uploads/generate-image-variant.mjs.map +1 -0
- package/esm/http/uploads/generate-image-variants.d.mts +49 -0
- package/esm/http/uploads/generate-image-variants.d.mts.map +1 -0
- package/esm/http/uploads/generate-image-variants.mjs +141 -0
- package/esm/http/uploads/generate-image-variants.mjs.map +1 -0
- package/esm/http/uploads/image-variant-types.d.mts +93 -0
- package/esm/http/uploads/image-variant-types.d.mts.map +1 -0
- package/esm/http/uploads/image-variants-config-error.d.mts +13 -0
- package/esm/http/uploads/image-variants-config-error.d.mts.map +1 -0
- package/esm/http/uploads/image-variants-config-error.mjs +17 -0
- package/esm/http/uploads/image-variants-config-error.mjs.map +1 -0
- package/esm/http/uploads/index.d.mts +4 -0
- package/esm/http/uploads/index.mjs +5 -0
- package/esm/http/uploads/is-path-inside.mjs +26 -0
- package/esm/http/uploads/is-path-inside.mjs.map +1 -0
- package/esm/http/uploads/load-variant-source.mjs +42 -0
- package/esm/http/uploads/load-variant-source.mjs.map +1 -0
- package/esm/http/uploads/matches-if-none-match.mjs +13 -0
- package/esm/http/uploads/matches-if-none-match.mjs.map +1 -0
- package/esm/http/uploads/parse-uploaded-file-query.mjs +51 -0
- package/esm/http/uploads/parse-uploaded-file-query.mjs.map +1 -0
- package/esm/http/uploads/read-file-head.mjs +20 -0
- package/esm/http/uploads/read-file-head.mjs.map +1 -0
- package/esm/http/uploads/resolve-image-variants-config.mjs +91 -0
- package/esm/http/uploads/resolve-image-variants-config.mjs.map +1 -0
- package/esm/http/uploads/resolve-inline-image-content-type.mjs +53 -0
- package/esm/http/uploads/resolve-inline-image-content-type.mjs.map +1 -0
- package/esm/http/uploads/resolve-original-content-type.mjs +31 -0
- package/esm/http/uploads/resolve-original-content-type.mjs.map +1 -0
- package/esm/http/uploads/resolve-upload-path.mjs +56 -0
- package/esm/http/uploads/resolve-upload-path.mjs.map +1 -0
- package/esm/http/uploads/resolve-variant-candidate.mjs +39 -0
- package/esm/http/uploads/resolve-variant-candidate.mjs.map +1 -0
- package/esm/http/uploads/single-flight.mjs +40 -0
- package/esm/http/uploads/single-flight.mjs.map +1 -0
- package/esm/http/uploads/uploaded-file.controller.d.mts +24 -0
- package/esm/http/uploads/uploaded-file.controller.d.mts.map +1 -0
- package/esm/http/uploads/uploaded-file.controller.mjs +174 -0
- package/esm/http/uploads/uploaded-file.controller.mjs.map +1 -0
- package/esm/http/uploads/variant-cache-key.mjs +45 -0
- package/esm/http/uploads/variant-cache-key.mjs.map +1 -0
- package/esm/http/uploads/variant-generations.mjs +16 -0
- package/esm/http/uploads/variant-generations.mjs.map +1 -0
- package/esm/http/uploads-types.d.mts +102 -1
- package/esm/http/uploads-types.d.mts.map +1 -1
- package/esm/http/xmlable.d.mts +14 -0
- package/esm/http/xmlable.d.mts.map +1 -0
- package/esm/index.d.mts +10 -2
- package/esm/index.mjs +8 -2
- package/esm/router/types.d.mts +23 -0
- package/esm/router/types.d.mts.map +1 -1
- package/esm/storage/index.d.mts +1 -0
- package/esm/storage/index.mjs +1 -0
- package/esm/storage/scoped-storage.d.mts +1 -0
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs +3 -0
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage.d.mts +2 -0
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +4 -0
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/utils/storage-not-initialized-error.d.mts +22 -0
- package/esm/storage/utils/storage-not-initialized-error.d.mts.map +1 -0
- package/esm/storage/utils/storage-not-initialized-error.mjs +24 -0
- package/esm/storage/utils/storage-not-initialized-error.mjs.map +1 -0
- package/llms-full.txt +47 -7
- package/llms.txt +1 -1
- package/package.json +22 -21
- package/skills/configure-app/SKILL.md +6 -6
- package/skills/create-controller/SKILL.md +14 -0
- package/skills/health-checks/SKILL.md +8 -0
- package/skills/send-response/SKILL.md +2 -0
- package/skills/use-localization/SKILL.md +16 -0
- package/skills/use-middleware/SKILL.md +1 -1
- package/esm/dev-server/translation-type-generator.mjs +0 -28
- package/esm/dev-server/translation-type-generator.mjs.map +0 -1
|
@@ -37,7 +37,20 @@ declare enum HttpErrorCodes {
|
|
|
37
37
|
/**
|
|
38
38
|
* Application is in maintenance mode and the request did not match the allowlist.
|
|
39
39
|
*/
|
|
40
|
-
Maintenance = "EC106"
|
|
40
|
+
Maintenance = "EC106",
|
|
41
|
+
/**
|
|
42
|
+
* Default core CSRF-Origin guard (SECURITY, card 8a752ab2, `./csrf-default-guard.ts`)
|
|
43
|
+
* refused an unsafe-method, cookie-carrying request whose `Origin`/`Referer`
|
|
44
|
+
* did not name the request's own origin or an entry in
|
|
45
|
+
* `auth.csrf.allowedOrigins`.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately reuses `@warlock.js/auth`'s `AuthErrorCodes.CsrfOriginMismatch`
|
|
48
|
+
* value (`EC006`), NOT a fresh `EC1xx` core code: this is the exact same
|
|
49
|
+
* failure class `authMiddleware("cookie:*")`'s own CSRF-Origin check
|
|
50
|
+
* reports — one client-visible identifier for "CSRF Origin check failed",
|
|
51
|
+
* regardless of which seam caught it.
|
|
52
|
+
*/
|
|
53
|
+
CsrfOriginMismatch = "EC006"
|
|
41
54
|
}
|
|
42
55
|
//#endregion
|
|
43
56
|
export { HttpErrorCodes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes.d.mts","names":[],"sources":["../../../../../../../core/src/http/error-codes.ts"],"mappings":";;AASA;;;;;;;;aAAY,cAAA;EA8BV
|
|
1
|
+
{"version":3,"file":"error-codes.d.mts","names":[],"sources":["../../../../../../../core/src/http/error-codes.ts"],"mappings":";;AASA;;;;;;;;aAAY,cAAA;EA8BV;;;;EAzBA,sBAAA;;;;EAKA,qBAAA;;;;EAKA,iBAAA;;;;EAKA,uBAAA;;;;EAKA,YAAA;;;;EAKA,WAAA;;;;EAKA,WAAA;;;;;;;;;;;;;EAcA,kBAAA;AAAA"}
|
package/esm/http/error-codes.mjs
CHANGED
|
@@ -38,6 +38,19 @@ let HttpErrorCodes = /* @__PURE__ */ function(HttpErrorCodes) {
|
|
|
38
38
|
* Application is in maintenance mode and the request did not match the allowlist.
|
|
39
39
|
*/
|
|
40
40
|
HttpErrorCodes["Maintenance"] = "EC106";
|
|
41
|
+
/**
|
|
42
|
+
* Default core CSRF-Origin guard (SECURITY, card 8a752ab2, `./csrf-default-guard.ts`)
|
|
43
|
+
* refused an unsafe-method, cookie-carrying request whose `Origin`/`Referer`
|
|
44
|
+
* did not name the request's own origin or an entry in
|
|
45
|
+
* `auth.csrf.allowedOrigins`.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately reuses `@warlock.js/auth`'s `AuthErrorCodes.CsrfOriginMismatch`
|
|
48
|
+
* value (`EC006`), NOT a fresh `EC1xx` core code: this is the exact same
|
|
49
|
+
* failure class `authMiddleware("cookie:*")`'s own CSRF-Origin check
|
|
50
|
+
* reports — one client-visible identifier for "CSRF Origin check failed",
|
|
51
|
+
* regardless of which seam caught it.
|
|
52
|
+
*/
|
|
53
|
+
HttpErrorCodes["CsrfOriginMismatch"] = "EC006";
|
|
41
54
|
return HttpErrorCodes;
|
|
42
55
|
}({});
|
|
43
56
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-codes.mjs","names":[],"sources":["../../../../../../../core/src/http/error-codes.ts"],"sourcesContent":["/**\n * HTTP middleware error codes.\n *\n * Mirrors the convention used by `@warlock.js/auth`'s `AuthErrorCodes` —\n * stable string identifiers clients can branch on without parsing message text.\n *\n * Range `EC100..EC199` is reserved for `@warlock.js/core` HTTP middleware.\n * `EC001..EC099` belongs to `@warlock.js/auth`.\n */\nexport enum HttpErrorCodes {\n /**\n * Same idempotency key reused with a different request body.\n * Client likely has a bug — same intent must reuse the same body.\n */\n IdempotencyKeyConflict = \"EC100\",\n\n /**\n * Idempotency key header is malformed (wrong length / non-printable chars).\n */\n IdempotencyKeyInvalid = \"EC101\",\n\n /**\n * Per-route rate limit exceeded. Different from the global `@fastify/rate-limit` 429.\n */\n RateLimitExceeded = \"EC102\",\n\n /**\n * Per-route concurrency cap reached — too many in-flight requests against this endpoint.\n */\n ConcurrencyLimitReached = \"EC103\",\n\n /**\n * Request `Content-Length` exceeds the per-route body cap.\n */\n BodyTooLarge = \"EC104\",\n\n /**\n * Client IP failed the `ipFilter()` allow/deny check.\n */\n IpForbidden = \"EC105\",\n\n /**\n * Application is in maintenance mode and the request did not match the allowlist.\n */\n Maintenance = \"EC106\",\n}\n"],"mappings":";;;;;;;;;;AASA,IAAY,iBAAL;;;;;CAKL;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;AACF"}
|
|
1
|
+
{"version":3,"file":"error-codes.mjs","names":[],"sources":["../../../../../../../core/src/http/error-codes.ts"],"sourcesContent":["/**\n * HTTP middleware error codes.\n *\n * Mirrors the convention used by `@warlock.js/auth`'s `AuthErrorCodes` —\n * stable string identifiers clients can branch on without parsing message text.\n *\n * Range `EC100..EC199` is reserved for `@warlock.js/core` HTTP middleware.\n * `EC001..EC099` belongs to `@warlock.js/auth`.\n */\nexport enum HttpErrorCodes {\n /**\n * Same idempotency key reused with a different request body.\n * Client likely has a bug — same intent must reuse the same body.\n */\n IdempotencyKeyConflict = \"EC100\",\n\n /**\n * Idempotency key header is malformed (wrong length / non-printable chars).\n */\n IdempotencyKeyInvalid = \"EC101\",\n\n /**\n * Per-route rate limit exceeded. Different from the global `@fastify/rate-limit` 429.\n */\n RateLimitExceeded = \"EC102\",\n\n /**\n * Per-route concurrency cap reached — too many in-flight requests against this endpoint.\n */\n ConcurrencyLimitReached = \"EC103\",\n\n /**\n * Request `Content-Length` exceeds the per-route body cap.\n */\n BodyTooLarge = \"EC104\",\n\n /**\n * Client IP failed the `ipFilter()` allow/deny check.\n */\n IpForbidden = \"EC105\",\n\n /**\n * Application is in maintenance mode and the request did not match the allowlist.\n */\n Maintenance = \"EC106\",\n\n /**\n * Default core CSRF-Origin guard (SECURITY, card 8a752ab2, `./csrf-default-guard.ts`)\n * refused an unsafe-method, cookie-carrying request whose `Origin`/`Referer`\n * did not name the request's own origin or an entry in\n * `auth.csrf.allowedOrigins`.\n *\n * Deliberately reuses `@warlock.js/auth`'s `AuthErrorCodes.CsrfOriginMismatch`\n * value (`EC006`), NOT a fresh `EC1xx` core code: this is the exact same\n * failure class `authMiddleware(\"cookie:*\")`'s own CSRF-Origin check\n * reports — one client-visible identifier for \"CSRF Origin check failed\",\n * regardless of which seam caught it.\n */\n CsrfOriginMismatch = \"EC006\",\n}\n"],"mappings":";;;;;;;;;;AASA,IAAY,iBAAL;;;;;CAKL;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;;;;;;;;;;;;CAcA;;AACF"}
|
package/esm/http/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PipeableReactStream, StreamReactResponseOptions, streamReactResponse } from "./stream-react-response.mjs";
|
|
2
|
+
import { XMLable } from "./xmlable.mjs";
|
|
2
3
|
import { CookieOptions, Response, ResponseStatus, SendBufferOptions, SendFileOptions } from "./response.mjs";
|
|
3
|
-
import { FileNamingStrategy, ImageTransformCallback, ImageTransformConfig, PrefixConfig, PrefixOptions, SaveAsOptions, SaveOptions, UploadedFileImageOptions, UploadsConfigurations } from "./uploads-types.mjs";
|
|
4
|
+
import { FileNamingStrategy, ImageTransformCallback, ImageTransformConfig, ImageVariantDefinition, ImageVariantFit, ImageVariantOutputFormat, PrefixConfig, PrefixOptions, SaveAsOptions, SaveOptions, UploadedFileImageOptions, UploadsConfigurations, UploadsImagesConfigurations } from "./uploads-types.mjs";
|
|
4
5
|
import { FileValidationOptions, UploadedFile, UploadedFileJson } from "./uploaded-file.mjs";
|
|
5
6
|
import { Request } from "./request.mjs";
|
|
6
7
|
import { CspConfig, DEFAULT_CSP_DIRECTIVES, InvalidCspDirectiveError, applyCspHeader, buildCspHeaderValue, mergeCspDirectives, resolveCspConfig, serializeCspDirectives, validateCspConfigAtBoot, validateCspDirectives } from "./csp.mjs";
|
|
@@ -8,12 +9,18 @@ import { HttpTracingConfig, TracingContext, TracingHooks, TracingPhaseInfo, Trac
|
|
|
8
9
|
import { DecodedAccessToken, HttpConfigurations, PartialMiddleware, RequestEvent, RequestLocals, ResponseEvent, ResponseSSEController, ResponseStreamController, ReturnedResponse } from "./types.mjs";
|
|
9
10
|
import { defaultHttpConfigurations, httpConfig } from "./config.mjs";
|
|
10
11
|
import { createHttpApplication, stopHttpApplication } from "./createHttpApplication.mjs";
|
|
12
|
+
import { requiresDefaultCsrfGuard, runDefaultCsrfGuard } from "./csrf-default-guard.mjs";
|
|
13
|
+
import { CsrfOriginCheckReason, CsrfOriginVerdict, isAllowedCsrfOrigin, normalizeOrigin, originOf, ownOrigin, resolveCsrfOriginVerdict } from "./csrf-origin-policy.mjs";
|
|
11
14
|
import { RequestLog } from "./database/RequestLog.mjs";
|
|
12
15
|
import { HttpErrorCodes } from "./error-codes.mjs";
|
|
13
16
|
import { logResponse, wrapResponseInDataKey } from "./events.mjs";
|
|
14
17
|
import { HealthCheck, HealthStatus, health } from "./health.mjs";
|
|
15
18
|
import { RequestController } from "./request-controller.mjs";
|
|
16
19
|
import { UPLOADS_DEFAULTS, uploadsConfig } from "./uploads-config.mjs";
|
|
20
|
+
import { DetectedImageFormat, GeneratedImageDescriptor, GeneratedImageVariant, ResolvedImageVariantsConfig, UploadedFileQuery, VariantOutputFormat, VariantSourceFormat } from "./uploads/image-variant-types.mjs";
|
|
21
|
+
import { GenerateImageVariantsOptions, generateImageVariants } from "./uploads/generate-image-variants.mjs";
|
|
22
|
+
import { ImageVariantsConfigError } from "./uploads/image-variants-config-error.mjs";
|
|
23
|
+
import { uploadedFileController } from "./uploads/uploaded-file.controller.mjs";
|
|
17
24
|
import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, RequestUserMovedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./errors/errors.mjs";
|
|
18
25
|
import { CacheMiddlewareOptions } from "./middleware/cache-response-middleware.mjs";
|
|
19
26
|
import { ConcurrencyLimitOptions } from "./middleware/concurrency-limit.middleware.mjs";
|
package/esm/http/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { requestContext, useCurrentUser, useRequest, useRequestStore } from "./context/request-context.mjs";
|
|
2
2
|
import { DEFAULT_CSP_DIRECTIVES, InvalidCspDirectiveError, applyCspHeader, buildCspHeaderValue, mergeCspDirectives, resolveCspConfig, serializeCspDirectives, validateCspConfigAtBoot, validateCspDirectives } from "./csp.mjs";
|
|
3
|
+
import { HttpErrorCodes } from "./error-codes.mjs";
|
|
4
|
+
import { isAllowedCsrfOrigin, normalizeOrigin, originOf, ownOrigin, resolveCsrfOriginVerdict } from "./csrf-origin-policy.mjs";
|
|
5
|
+
import { requiresDefaultCsrfGuard, runDefaultCsrfGuard } from "./csrf-default-guard.mjs";
|
|
3
6
|
import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, RequestUserMovedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./errors/errors.mjs";
|
|
4
7
|
import { deriveTraceId, parseTraceparentTraceId } from "./tracing/trace-id.mjs";
|
|
5
8
|
import { buildTracingContext, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, isTracingEnabled, resetTracingConfigForTests, resolveTracingConfig } from "./tracing/tracing-dispatcher.mjs";
|
|
@@ -15,10 +18,13 @@ import { registerHttpPlugins } from "./plugins.mjs";
|
|
|
15
18
|
import { closeServerWithTimeout, getHttpServer, startHttpServer } from "./server.mjs";
|
|
16
19
|
import { createHttpApplication, stopHttpApplication } from "./createHttpApplication.mjs";
|
|
17
20
|
import { RequestLog } from "./database/RequestLog.mjs";
|
|
18
|
-
import { HttpErrorCodes } from "./error-codes.mjs";
|
|
19
21
|
import { logResponse, wrapResponseInDataKey } from "./events.mjs";
|
|
20
22
|
import { health } from "./health.mjs";
|
|
21
23
|
import { RequestController } from "./request-controller.mjs";
|
|
24
|
+
import { ImageVariantsConfigError } from "./uploads/image-variants-config-error.mjs";
|
|
25
|
+
import { generateImageVariants } from "./uploads/generate-image-variants.mjs";
|
|
26
|
+
import { uploadedFileController } from "./uploads/uploaded-file.controller.mjs";
|
|
27
|
+
import "./uploads/index.mjs";
|
|
22
28
|
import { buildIdempotencyCacheKey, hashBody, isValidIdempotencyKey } from "./middleware/utils/idempotency-key.mjs";
|
|
23
29
|
import { anyMatch, ipMatches } from "./middleware/utils/cidr-match.mjs";
|
|
24
30
|
import { parseSize } from "./middleware/utils/parse-size.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t } from "./inject-request-context.mjs";
|
|
2
1
|
import { HttpErrorCodes } from "../error-codes.mjs";
|
|
2
|
+
import { t } from "./inject-request-context.mjs";
|
|
3
3
|
|
|
4
4
|
//#region ../core/src/http/middleware/concurrency-limit.middleware.ts
|
|
5
5
|
const counters = /* @__PURE__ */ new Map();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t } from "./inject-request-context.mjs";
|
|
2
1
|
import { HttpErrorCodes } from "../error-codes.mjs";
|
|
2
|
+
import { t } from "./inject-request-context.mjs";
|
|
3
3
|
import { buildIdempotencyCacheKey, hashBody, isValidIdempotencyKey } from "./utils/idempotency-key.mjs";
|
|
4
4
|
import config from "@mongez/config";
|
|
5
5
|
import { log } from "@warlock.js/logger";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inject-request-context.d.mts","names":[],"sources":["../../../../../../../../core/src/http/middleware/inject-request-context.ts"],"mappings":";;;;;;;
|
|
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;;;;iBAoIK,CAAA,CAAE,OAAA,UAAiB,YAAkB"}
|
|
@@ -2,6 +2,7 @@ import { environment } from "../../utils/environment.mjs";
|
|
|
2
2
|
import { requestContext } from "../context/request-context.mjs";
|
|
3
3
|
import "../../utils/index.mjs";
|
|
4
4
|
import { applyCspHeader } from "../csp.mjs";
|
|
5
|
+
import { runDefaultCsrfGuard } from "../csrf-default-guard.mjs";
|
|
5
6
|
import { HttpError } from "../errors/errors.mjs";
|
|
6
7
|
import { buildTracingContext, dispatchPhase, isTracingEnabled } from "../tracing/tracing-dispatcher.mjs";
|
|
7
8
|
import "../tracing/index.mjs";
|
|
@@ -9,6 +10,7 @@ import config from "@mongez/config";
|
|
|
9
10
|
import { trans } from "@mongez/localization";
|
|
10
11
|
import { DatabaseWriterValidationError } from "@warlock.js/cascade";
|
|
11
12
|
import { contextManager } from "@warlock.js/context";
|
|
13
|
+
import { log } from "@warlock.js/logger";
|
|
12
14
|
|
|
13
15
|
//#region ../core/src/http/middleware/inject-request-context.ts
|
|
14
16
|
/**
|
|
@@ -44,6 +46,10 @@ function createRequestStore(request, response) {
|
|
|
44
46
|
});
|
|
45
47
|
return contextManager.runAll(httpContextStore, async () => {
|
|
46
48
|
try {
|
|
49
|
+
const csrfGuardResponse = await runDefaultCsrfGuard(request, response, t, (reason) => {
|
|
50
|
+
log.error("http", "csrf-default-guard", /* @__PURE__ */ new Error(`Default CSRF Origin guard refused request: ${reason}`));
|
|
51
|
+
});
|
|
52
|
+
if (csrfGuardResponse) return csrfGuardResponse;
|
|
47
53
|
const result = await request.runMiddleware();
|
|
48
54
|
if (result) return result;
|
|
49
55
|
request.trigger("executingAction", request.route);
|
|
@@ -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 config from \"@mongez/config\";\nimport { environment } from \"../../utils\";\nimport { requestContext as requestContextInstance } from \"../context/request-context\";\nimport { applyCspHeader } from \"../csp\";\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 // 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);\n }\n });\n}\n\n/**\n * Handle request errors\n * @internal\n */\nfunction handleRequestError(error: unknown, response: Response): 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 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 return response.badRequest({\n errors: error.errors,\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 console.error(\"[warlock] unhandled request error:\", error);\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":"
|
|
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);\n }\n });\n}\n\n/**\n * Handle request errors\n * @internal\n */\nfunction handleRequestError(error: unknown, response: Response): 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 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 return response.badRequest({\n errors: error.errors,\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 console.error(\"[warlock] unhandled request error:\", error);\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,QAAQ;EAC3C;CACF,CAAC;AACH;;;;;AAMA,SAAS,mBAAmB,OAAgB,UAAsC;CAiBhF,SAAS,OAAO,iBAAiB,mBAAmB;CAEpD,IAAI,iBAAiB,WAAW;EAC9B,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,+BACnB,OAAO,SAAS,WAAW,EACzB,QAAQ,MAAM,OAChB,CAAC;CAQH,QAAQ,MAAM,sCAAsC,KAAK;CAEzD,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,5 +1,5 @@
|
|
|
1
|
-
import { t } from "./inject-request-context.mjs";
|
|
2
1
|
import { HttpErrorCodes } from "../error-codes.mjs";
|
|
2
|
+
import { t } from "./inject-request-context.mjs";
|
|
3
3
|
import { anyMatch } from "./utils/cidr-match.mjs";
|
|
4
4
|
|
|
5
5
|
//#region ../core/src/http/middleware/ip-filter.middleware.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t } from "./inject-request-context.mjs";
|
|
2
1
|
import { HttpErrorCodes } from "../error-codes.mjs";
|
|
2
|
+
import { t } from "./inject-request-context.mjs";
|
|
3
3
|
import { parseSize } from "./utils/parse-size.mjs";
|
|
4
4
|
|
|
5
5
|
//#region ../core/src/http/middleware/max-body-size.middleware.ts
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region ../core/src/http/parse-urlencoded-body.ts
|
|
2
|
+
/**
|
|
3
|
+
* Decode an `application/x-www-form-urlencoded` request body into a plain
|
|
4
|
+
* object, the shape `request.ts`'s `parseBody` already expects from JSON and
|
|
5
|
+
* multipart bodies.
|
|
6
|
+
*
|
|
7
|
+
* Repeated keys become arrays (`code=a&code=b` -> `{ code: ["a", "b"] }`);
|
|
8
|
+
* every other key stays a single string. Bracket-notation keys (`a[b]=1`) are
|
|
9
|
+
* NOT expanded here — they are handed through as literal keys and, like any
|
|
10
|
+
* JSON or query-string key of that shape, are expanded later by the shared
|
|
11
|
+
* `parseBody` nesting logic in `request.ts`. This function's only job is the
|
|
12
|
+
* urlencoded -> flat-object step; it does not duplicate that logic.
|
|
13
|
+
*/
|
|
14
|
+
function parseUrlencodedBody(raw) {
|
|
15
|
+
const params = new URLSearchParams(raw);
|
|
16
|
+
const body = {};
|
|
17
|
+
for (const key of params.keys()) {
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(body, key)) continue;
|
|
19
|
+
const values = params.getAll(key);
|
|
20
|
+
body[key] = values.length > 1 ? values : values[0] ?? "";
|
|
21
|
+
}
|
|
22
|
+
return body;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { parseUrlencodedBody };
|
|
27
|
+
//# sourceMappingURL=parse-urlencoded-body.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-urlencoded-body.mjs","names":[],"sources":["../../../../../../../core/src/http/parse-urlencoded-body.ts"],"sourcesContent":["/**\n * Decode an `application/x-www-form-urlencoded` request body into a plain\n * object, the shape `request.ts`'s `parseBody` already expects from JSON and\n * multipart bodies.\n *\n * Repeated keys become arrays (`code=a&code=b` -> `{ code: [\"a\", \"b\"] }`);\n * every other key stays a single string. Bracket-notation keys (`a[b]=1`) are\n * NOT expanded here — they are handed through as literal keys and, like any\n * JSON or query-string key of that shape, are expanded later by the shared\n * `parseBody` nesting logic in `request.ts`. This function's only job is the\n * urlencoded -> flat-object step; it does not duplicate that logic.\n */\nexport function parseUrlencodedBody(raw: string): Record<string, string | string[]> {\n const params = new URLSearchParams(raw);\n const body: Record<string, string | string[]> = {};\n\n for (const key of params.keys()) {\n if (Object.prototype.hasOwnProperty.call(body, key)) continue;\n\n const values = params.getAll(key);\n\n body[key] = values.length > 1 ? values : (values[0] ?? \"\");\n }\n\n return body;\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,SAAgB,oBAAoB,KAAgD;CAClF,MAAM,SAAS,IAAI,gBAAgB,GAAG;CACtC,MAAM,OAA0C,CAAC;CAEjD,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG;EAC/B,IAAI,OAAO,UAAU,eAAe,KAAK,MAAM,GAAG,GAAG;EAErD,MAAM,SAAS,OAAO,OAAO,GAAG;EAEhC,KAAK,OAAO,OAAO,SAAS,IAAI,SAAU,OAAO,MAAM;CACzD;CAEA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.d.mts","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"plugins.d.mts","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"mappings":";;;iBAQsB,mBAAA,CAAoB,MAAA,EAAQ,eAAA,GAAe,OAAA"}
|
package/esm/http/plugins.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { rootPath } from "../utils/paths.mjs";
|
|
2
2
|
import "../utils/index.mjs";
|
|
3
3
|
import { buildCorsOptions } from "./build-cors-options.mjs";
|
|
4
|
+
import { parseUrlencodedBody } from "./parse-urlencoded-body.mjs";
|
|
4
5
|
import config from "@mongez/config";
|
|
5
6
|
import fastifyMultipart from "@fastify/multipart";
|
|
6
7
|
|
|
@@ -15,6 +16,13 @@ async function registerHttpPlugins(server) {
|
|
|
15
16
|
attachFieldsToBody: true,
|
|
16
17
|
limits: { fileSize: config.get("http.fileUploadLimit", 10 * 1024 * 1024) }
|
|
17
18
|
});
|
|
19
|
+
server.addContentTypeParser("application/x-www-form-urlencoded", { parseAs: "string" }, (_request, body, done) => {
|
|
20
|
+
try {
|
|
21
|
+
done(null, parseUrlencodedBody(body));
|
|
22
|
+
} catch (error) {
|
|
23
|
+
done(error, void 0);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
18
26
|
server.register(import("@fastify/static"), {
|
|
19
27
|
root: config.get("storage.publicRoot", rootPath("public")),
|
|
20
28
|
prefix: config.get("storage.publicPrefix", "/public/")
|
package/esm/http/plugins.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.mjs","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"sourcesContent":["import fastifyMultipart from \"@fastify/multipart\";\nimport config from \"@mongez/config\";\nimport { rootPath } from \"../utils\";\nimport { buildCorsOptions } from \"./build-cors-options\";\nimport type { FastifyInstance } from \"./server\";\n\nexport async function registerHttpPlugins(server: FastifyInstance) {\n // 👇🏻 register rate-limit plugin\n server.register(import(\"@fastify/rate-limit\"), {\n // max requests per time window\n max: config.get(\"http.rateLimit.max\", 60),\n // maximum time that is will allow max requests\n timeWindow: config.get(\"http.rateLimit.duration\", 60 * 1000),\n });\n\n // 👇🏻 register cors plugin\n server.register(import(\"@fastify/cors\"), buildCorsOptions());\n\n // 👇🏻 import multipart plugin\n server.register(fastifyMultipart, {\n attachFieldsToBody: true,\n limits: {\n // file size could be up to 10MB\n fileSize: config.get(\"http.fileUploadLimit\", 10 * 1024 * 1024),\n },\n });\n\n server.register(import(\"@fastify/static\"), {\n root: config.get(\"storage.publicRoot\", rootPath(\"public\")),\n prefix: config.get(\"storage.publicPrefix\", \"/public/\"),\n });\n\n // 👇🏻 register cookie plugin\n server.register(import(\"@fastify/cookie\"), {\n secret: config.get(\"http.cookies.secret\"), // Optional: allow signed cookies\n parseOptions: config.get(\"http.cookies.options\", {}),\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugins.mjs","names":[],"sources":["../../../../../../../core/src/http/plugins.ts"],"sourcesContent":["import fastifyMultipart from \"@fastify/multipart\";\nimport config from \"@mongez/config\";\nimport type { FastifyRequest } from \"fastify\";\nimport { rootPath } from \"../utils\";\nimport { buildCorsOptions } from \"./build-cors-options\";\nimport { parseUrlencodedBody } from \"./parse-urlencoded-body\";\nimport type { FastifyInstance } from \"./server\";\n\nexport async function registerHttpPlugins(server: FastifyInstance) {\n // 👇🏻 register rate-limit plugin\n server.register(import(\"@fastify/rate-limit\"), {\n // max requests per time window\n max: config.get(\"http.rateLimit.max\", 60),\n // maximum time that is will allow max requests\n timeWindow: config.get(\"http.rateLimit.duration\", 60 * 1000),\n });\n\n // 👇🏻 register cors plugin\n server.register(import(\"@fastify/cors\"), buildCorsOptions());\n\n // 👇🏻 import multipart plugin\n server.register(fastifyMultipart, {\n attachFieldsToBody: true,\n limits: {\n // file size could be up to 10MB\n fileSize: config.get(\"http.fileUploadLimit\", 10 * 1024 * 1024),\n },\n });\n\n // 👇🏻 parse application/x-www-form-urlencoded bodies (Apple's OAuth\n // `form_post` callback, plain HTML forms). Fastify only parses JSON and\n // text natively, so without this every urlencoded POST is rejected with\n // FST_ERR_CTP_INVALID_MEDIA_TYPE before it reaches a route.\n //\n // No `bodyLimit` option here: Fastify falls back to the server's own\n // `bodyLimit` (`server.ts`, from `http.bodyLimit`) whenever a content-type\n // parser doesn't set one — the exact limit JSON bodies are already held to.\n // Reading `http.bodyLimit` again here would duplicate that lookup and risk\n // the two silently drifting.\n server.addContentTypeParser(\n \"application/x-www-form-urlencoded\",\n { parseAs: \"string\" },\n (_request: FastifyRequest, body: string, done: (err: Error | null, body?: any) => void) => {\n try {\n done(null, parseUrlencodedBody(body));\n } catch (error) {\n done(error as Error, undefined);\n }\n },\n );\n\n server.register(import(\"@fastify/static\"), {\n root: config.get(\"storage.publicRoot\", rootPath(\"public\")),\n prefix: config.get(\"storage.publicPrefix\", \"/public/\"),\n });\n\n // 👇🏻 register cookie plugin\n server.register(import(\"@fastify/cookie\"), {\n secret: config.get(\"http.cookies.secret\"), // Optional: allow signed cookies\n parseOptions: config.get(\"http.cookies.options\", {}),\n });\n}\n"],"mappings":";;;;;;;;AAQA,eAAsB,oBAAoB,QAAyB;CAEjE,OAAO,SAAS,OAAO,wBAAwB;EAE7C,KAAK,OAAO,IAAI,sBAAsB,EAAE;EAExC,YAAY,OAAO,IAAI,2BAA2B,KAAK,GAAI;CAC7D,CAAC;CAGD,OAAO,SAAS,OAAO,kBAAkB,iBAAiB,CAAC;CAG3D,OAAO,SAAS,kBAAkB;EAChC,oBAAoB;EACpB,QAAQ,EAEN,UAAU,OAAO,IAAI,wBAAwB,KAAK,OAAO,IAAI,EAC/D;CACF,CAAC;CAYD,OAAO,qBACL,qCACA,EAAE,SAAS,SAAS,IACnB,UAA0B,MAAc,SAAkD;EACzF,IAAI;GACF,KAAK,MAAM,oBAAoB,IAAI,CAAC;EACtC,SAAS,OAAO;GACd,KAAK,OAAgB,MAAS;EAChC;CACF,CACF;CAEA,OAAO,SAAS,OAAO,oBAAoB;EACzC,MAAM,OAAO,IAAI,sBAAsB,SAAS,QAAQ,CAAC;EACzD,QAAQ,OAAO,IAAI,wBAAwB,UAAU;CACvD,CAAC;CAGD,OAAO,SAAS,OAAO,oBAAoB;EACzC,QAAQ,OAAO,IAAI,qBAAqB;EACxC,cAAc,OAAO,IAAI,wBAAwB,CAAC,CAAC;CACrD,CAAC;AACH"}
|
package/esm/http/request.d.mts
CHANGED
|
@@ -210,8 +210,8 @@ declare class Request<RequestValidation = any> {
|
|
|
210
210
|
*/
|
|
211
211
|
protected cacheLocale(candidate: unknown): string;
|
|
212
212
|
/**
|
|
213
|
-
* Resolve the first present Mode B source
|
|
214
|
-
*
|
|
213
|
+
* Resolve the first present Mode B source, skipping unsupported browser
|
|
214
|
+
* preferences. Other unsupported values resolve to the configured default.
|
|
215
215
|
*/
|
|
216
216
|
protected resolveLocale(): string;
|
|
217
217
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.d.mts","names":[],"sources":["../../../../../../../core/src/http/request.ts"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"request.d.mts","names":[],"sources":["../../../../../../../core/src/http/request.ts"],"mappings":";;;;;;;;;;;KA2BK,eAAA,iBAIU,mBAAA,mBAAsC,CAAA,0BAA2B,CAAA,WAAY,CAAA,GACvF,mBAAA,CAAoB,CAAA;AAAA,KAGpB,UAAA,SAAmB,eAAe;AAAA,cAE1B,OAAA;;;;;;;;;;;;;;;;;EAiBJ,WAAA,EAAc,cAAA;EAtBG;AAAA;;EA2BjB,QAAA,EAAW,QAAA;EAxBmB;AAAA;AAEvC;EA2BS,KAAA,EAAQ,KAAA;EA3BG;;;EAAA,UAgCR,OAAA;EAcuB;;;EAAA,QATzB,mBAAA;EAsHwB;;;;;;;EAAA,IA7GrB,kBAAA,IAAsB,kBAAA;EAAA,IAItB,kBAAA,CAAmB,KAAA,EAAO,kBAAA;EAuV7B;;;;;;;;;;;;;;;;;EAAA,IAjUG,IAAA;EAi0BS;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA7xBb,MAAA,EAAQ,aAAA;EA8BJ;;;;EAAA,UAxBD,MAAA;EAyCsB;;;;;;;;;;;;;;;;;;;;;;EAAA,IAjBrB,KAAA;EAgKD;;;EAAA,OArJI,OAAA,EAAS,OAAA;EA4MZ;;;;EAtMJ,KAAA,EAAO,UAAA,QAAkB,KAAA;EAwNX;;;EAnNd,CAAA,EAAG,UAAA,QAAkB,KAAA;EAiON;;;EAAA,UAlNZ,OAAA;EAyNH;;;EAAA,UApNG,aAAA,GAAgB,iBAAA;EAqNF;;;EAhNjB,EAAA;EAyNe;;;;;EAlNf,OAAA;EAqPU;;;EAhPV,SAAA;EA8QI;;;EAzQJ,OAAA;EA6TI;;;EAxTJ,UAAA,CAAW,OAAA,EAAS,cAAA;EAmVH;;;;;;;;;EAAA,UAtTd,gBAAA;EA0gBa;;;;;;;EAAA,UAhfb,cAAA;EAmgBA;;;;;EAAA,iBAvfO,gBAAA,CAAiB,KAAA,YAAiB,KAAA;EA8f1B;;;EAlflB,SAAA,CAAU,UAAA,UAAoB,OAAA,UAAiB,YAAA;EA6hBzC;;;;;;;;EAAA,UAjhBH,WAAA,CAAY,SAAA;EAgkBI;;;;EAAA,UA5iBhB,aAAA;EAyjBgB;;;EAAA,IA/hBf,MAAA;EAsiBa;;;EAAA,IA7hBb,MAAA,CAAO,UAAA;EAwiBE;;;EAjiBb,aAAA,CAAc,UAAA;EAqjBY;;;;;EA1iB1B,aAAA,CAAc,wBAAA;EA2nBd;;;EAAA,IApnBI,QAAA;EA2nBA;;;EApnBE,QAAA,CAAS,UAAA,EAAY,aAAA,EAAe,cAAA,cAAyB,OAAA,4BAAA,gBAAA;EAkoBnE;;;EA3nBA,MAAA,gCAAsC,UAAA,EAC3C,IAAA,EAAM,aAAA,GAAgB,UAAA,EACtB,YAAA;EAkoBgB;;;EAAA,IA1nBP,OAAA,IAAW,MAAA;EA8oBX;;;;;;;EAAA,QAnoBH,wBAAA;EAgrBD;;;EAvqBA,MAAA,CAAO,IAAA,UAAc,YAAA;EAqrBrB;;;EAtqBA,SAAA,CAAU,IAAA;EAsrBV;;;EAAA,IA7qBI,MAAA;EA6rBJ;;;EAAA,IAtrBI,QAAA;EAsuBC;;;EAAA,IA/tBD,MAAA;EAivBG;;;EAAA,IA1uBH,YAAA;EAswBJ;;;EAAA,IAzvBI,kBAAA;EA2wBJ;;;;;EAAA,IA1vBI,WAAA;EA4wBJ;;;EAAA,IA7vBI,aAAA;EAizBJ;;;EAAA,IA1yBI,MAAA;EAm2BA;;;EAOmC;;;;;;AAOC;EAPD,UA51BpC,gBAAA,CAAiB,GAAA;;;;;;;;;;YAajB,aAAA,CAAc,KAAA,OAAY,UAAA,WAAqB,KAAA,GAAQ,KAAA;EAAA,UAMvD,YAAA;;;;YAeA,SAAA,CAAU,IAAA;;;;YAyKV,UAAA,CAAW,IAAA;;;;EAsBd,QAAA,CAAS,KAAA,EAAO,KAAA;;;;EAYhB,OAAA,CAAQ,SAAA,EAAW,YAAA,KAAiB,IAAA;;;;EAOpC,EAAA,CAAG,SAAA,EAAW,YAAA,EAAc,QAAA,iCAAa,iBAAA;;;;EAOzC,GAAA,CAAI,OAAA,OAAc,KAAA,GAAO,QAAA;;;;MAiBrB,IAAA;;;;MAOA,GAAA;;;;MAOA,OAAA;;;;;;;;;EAYE,aAAA,IAAa,OAAA,SAAA,MAAA,gBAAA,QAAA;;;;;;EAuCnB,UAAA,IAAU,cAAA,CAAA,OAAA;;;;;EAQV,SAAA,UAAmB,iBAAA,EAAmB,MAAA,UAAgB,MAAA,sBAA4B,MAAA;;;;EAalF,eAAA,IAAmB,MAAA,aAAmB,iBAAA;;;;EAOtC,gBAAA,CAAiB,IAAA,EAAM,iBAAA;;;;;;;;EAWjB,OAAA,IAAO,OAAA,SAAA,MAAA,gBAAA,QAAA;;;;;;;YAoBJ,iBAAA,IAAiB,OAAA,SAAA,MAAA,gBAAA,QAAA;;;;;;;YA4DvB,kBAAA,IAAsB,UAAA;;;;EAczB,KAAA,CAAM,GAAA,UAAa,YAAA;;;;EAOnB,KAAA,CAAM,GAAA,WAAuB,YAAA;;;;EAO7B,GAAA,CAAI,GAAA,UAAa,YAAA;;;;EAOjB,GAAA,CAAI,GAAA;;;;EAOJ,GAAA,CAAI,GAAA,UAAa,KAAA;;;;EASjB,UAAA,CAAW,GAAA,UAAa,KAAA;;;;EAWxB,KAAA,IAAS,IAAA;;;;MASL,IAAA;;;;EAOJ,OAAA,CAAQ,GAAA,UAAa,KAAA;;;;MASjB,UAAA;;;;EAmBJ,IAAA,CAAK,GAAA,WAAc,YAAA;;;;;EAUnB,KAAA,CAAM,IAAA,WAAe,YAAA;;;;MAOjB,MAAA;;;;EAOJ,QAAA,CAAS,GAAA,UAAa,KAAA;;;;MASlB,KAAA;;;;EAOJ,QAAA,CAAS,GAAA,UAAa,KAAA;;;;EAStB,GAAA;;;;EAOA,eAAA;;;;EAUA,iBAAA;;;;EAmBA,KAAA;;;;EAmBA,IAAA,CAAK,IAAA;;;;EAOL,KAAA,CAAM,IAAA;;;;EAWN,MAAA,CAAO,IAAA;;;;EAOP,IAAA,CAAK,GAAA,UAAa,YAAA;;;;EAqBlB,GAAA,CAAI,GAAA,UAAa,YAAA;;;;MAWb,OAAA;;;;EAOJ,MAAA,CAAO,GAAA,UAAa,YAAA;;;;EASpB,KAAA,CAAM,GAAA,UAAa,YAAA;;;;EASnB,MAAA,CAAO,GAAA,UAAa,YAAA;;;;;;;;;;MAehB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCJ,QAAA;;;;MAoCI,MAAA;;;;MAOA,GAAA;;;;MAOA,OAAA;;;;MAOA,SAAA;;;;MAOA,OAAA,gBAAuB,WAAA,CAAY,OAAA;;;;EAOvC,SAAA,CAAU,GAAA,EAAK,UAAA,EAAY,KAAA;AAAA"}
|
package/esm/http/request.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { config } from "../config/config-getter.mjs";
|
|
2
|
-
import { LOCALE_COOKIE_NAME, resolveLocaleConfiguration } from "../config/locale-configuration.mjs";
|
|
2
|
+
import { LOCALE_COOKIE_NAME, LOCALE_PREFERENCE_COOKIE_NAME, resolveLocaleConfiguration } from "../config/locale-configuration.mjs";
|
|
3
3
|
import { CookieJarUnavailableError, RequestUserMovedError } from "./errors/errors.mjs";
|
|
4
4
|
import { deriveTraceId } from "./tracing/trace-id.mjs";
|
|
5
5
|
import { buildTracingContext, dispatchPhase, isTracingEnabled } from "./tracing/tracing-dispatcher.mjs";
|
|
@@ -162,12 +162,16 @@ var Request = class Request {
|
|
|
162
162
|
return this._locale;
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
|
-
* Resolve the first present Mode B source
|
|
166
|
-
*
|
|
165
|
+
* Resolve the first present Mode B source, skipping unsupported browser
|
|
166
|
+
* preferences. Other unsupported values resolve to the configured default.
|
|
167
167
|
*/
|
|
168
168
|
resolveLocale() {
|
|
169
|
+
const { localeCodes } = resolveLocaleConfiguration(config.key("app.localeCode"), config.key("app.localeCodes"));
|
|
170
|
+
const preference = this.cookies[LOCALE_PREFERENCE_COOKIE_NAME];
|
|
171
|
+
const supportedPreference = typeof preference === "string" && (localeCodes === void 0 || localeCodes.includes(preference)) ? preference : void 0;
|
|
169
172
|
const candidate = [
|
|
170
173
|
this.query["locale"],
|
|
174
|
+
supportedPreference,
|
|
171
175
|
this.cookies[LOCALE_COOKIE_NAME],
|
|
172
176
|
this.header("locale")
|
|
173
177
|
].find((value) => typeof value === "string" && value.length > 0);
|