@warlock.js/core 4.16.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/bin/warlock.js +154 -1
- package/esm/application/application.d.mts +65 -1
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs +62 -0
- package/esm/application/application.mjs.map +1 -1
- package/esm/application/index.d.mts +1 -1
- package/esm/cli/cli-commands.manager.mjs +60 -10
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/dev-server.command.mjs +1 -0
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +40 -0
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -0
- package/esm/cli/commands/doctor/checks/index.mjs +4 -2
- package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
- package/esm/commands/cli-command.d.mts +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -1
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/commands/types.d.mts +10 -2
- package/esm/commands/types.d.mts.map +1 -1
- package/esm/config/config-handlers.d.mts +1 -2
- package/esm/config/config-handlers.d.mts.map +1 -1
- package/esm/config/config-handlers.mjs +3 -2
- package/esm/config/config-handlers.mjs.map +1 -1
- package/esm/config/locale-configuration.mjs +29 -0
- package/esm/config/locale-configuration.mjs.map +1 -0
- package/esm/connectors/assert-no-reserved-connector-names.d.mts +7 -0
- package/esm/connectors/assert-no-reserved-connector-names.d.mts.map +1 -0
- package/esm/connectors/assert-no-reserved-connector-names.mjs +44 -0
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -0
- package/esm/connectors/assert-unique-connector-names.d.mts +7 -0
- package/esm/connectors/assert-unique-connector-names.d.mts.map +1 -0
- package/esm/connectors/assert-unique-connector-names.mjs +20 -0
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -0
- package/esm/connectors/connectors-manager.d.mts +38 -0
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +45 -12
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/describe-server-address.mjs +75 -0
- package/esm/connectors/describe-server-address.mjs.map +1 -0
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +6 -3
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/index.d.mts +4 -1
- package/esm/connectors/index.mjs +4 -1
- package/esm/connectors/register-configured-connectors.d.mts +14 -0
- package/esm/connectors/register-configured-connectors.d.mts.map +1 -0
- package/esm/connectors/register-configured-connectors.mjs +99 -0
- package/esm/connectors/register-configured-connectors.mjs.map +1 -0
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs +4 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/types.d.mts +76 -1
- package/esm/connectors/types.d.mts.map +1 -1
- package/esm/connectors/types.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.d.mts +50 -2
- package/esm/database/models/database-log/database-log.d.mts.map +1 -1
- package/esm/database/utils.d.mts +2 -2
- package/esm/database/utils.d.mts.map +1 -1
- package/esm/dev-server/development-server.mjs +1 -0
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs +2 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/read-config-ast.mjs +48 -0
- package/esm/dev-server/read-config-ast.mjs.map +1 -0
- package/esm/dev-server/run-typings-generation.mjs +18 -0
- package/esm/dev-server/run-typings-generation.mjs.map +1 -0
- package/esm/dev-server/type-generator.mjs +30 -39
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +169 -1
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/stubs.mjs +93 -1
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/context/request-context.d.mts +14 -3
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs +11 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts +21 -0
- package/esm/http/context/request-memo.d.mts.map +1 -0
- package/esm/http/context/request-memo.mjs +48 -0
- package/esm/http/context/request-memo.mjs.map +1 -0
- package/esm/http/database/RequestLog.d.mts +164 -2
- package/esm/http/database/RequestLog.d.mts.map +1 -1
- package/esm/http/index.d.mts +6 -5
- package/esm/http/index.mjs +2 -1
- package/esm/http/middleware/cache-response-middleware.d.mts +2 -2
- package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/index.d.mts +1 -1
- package/esm/http/middleware/index.mjs +1 -1
- package/esm/http/middleware/inject-request-context.d.mts +1 -8
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +9 -22
- 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/ip-filter.middleware.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.d.mts +0 -14
- package/esm/http/middleware/utils/idempotency-key.d.mts.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs +19 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/request.d.mts +119 -30
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +98 -25
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.mjs +1 -1
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/types.d.mts +36 -1
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploaded-file.d.mts +51 -19
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs +36 -8
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/index.d.mts +17 -9
- package/esm/index.mjs +10 -4
- package/esm/production/build-contributions.mjs +115 -0
- package/esm/production/build-contributions.mjs.map +1 -0
- package/esm/production/production-builder.mjs +97 -2
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.d.mts +25 -0
- package/esm/production/resolve-build-config.d.mts.map +1 -0
- package/esm/production/resolve-build-config.mjs +43 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs +20 -9
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/restful/restful.d.mts +29 -8
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs +31 -13
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/describe-route-for-log.mjs +29 -0
- package/esm/router/describe-route-for-log.mjs.map +1 -0
- package/esm/router/index.d.mts +3 -1
- package/esm/router/index.mjs +2 -0
- package/esm/router/log-request-lifecycle.mjs +38 -0
- package/esm/router/log-request-lifecycle.mjs.map +1 -0
- package/esm/router/positional-handler-diagnostics.d.mts +90 -0
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -0
- package/esm/router/positional-handler-diagnostics.mjs +247 -0
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -0
- package/esm/router/route-registry.d.mts +34 -0
- package/esm/router/route-registry.d.mts.map +1 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +87 -63
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +23 -5
- package/esm/router/types.d.mts.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +1 -1
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/utils/safe-fetch.d.mts +11 -1
- package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs +51 -4
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/vitest-setup.mjs +1 -1
- package/esm/utils/load-environment.mjs +33 -2
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/types.d.mts +4 -0
- package/esm/utils/types.d.mts.map +1 -1
- package/esm/validation/validateAll.mjs +4 -1
- package/esm/validation/validateAll.mjs.map +1 -1
- package/esm/validation/validators/file-validator.d.mts +25 -1
- package/esm/validation/validators/file-validator.d.mts.map +1 -1
- package/esm/warlock-config/index.mjs +1 -1
- package/esm/warlock-config/normalize-build-config.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +36 -2
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/package.json +15 -12
- package/skills/request-memo/SKILL.md +142 -0
- package/skills/use-request-locals/SKILL.md +144 -0
package/esm/http/request.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UploadedFile } from "./uploaded-file.mjs";
|
|
2
2
|
import { Response } from "./response.mjs";
|
|
3
|
-
import { RequestEvent } from "./types.mjs";
|
|
3
|
+
import { RequestEvent, RequestLocals, RequestUser } from "./types.mjs";
|
|
4
4
|
import { Middleware, RequestHandler, Route } from "../router/types.mjs";
|
|
5
5
|
import { BaseValidator } from "@warlock.js/seal";
|
|
6
6
|
import { trans } from "@mongez/localization";
|
|
@@ -44,6 +44,86 @@ declare class Request<RequestValidation = any> {
|
|
|
44
44
|
* Decoded access token payload (set by auth middleware)
|
|
45
45
|
*/
|
|
46
46
|
decodedAccessToken?: any;
|
|
47
|
+
/**
|
|
48
|
+
* The authenticated user attached to this request, if any.
|
|
49
|
+
*
|
|
50
|
+
* `RequestUser` is empty by default, so ANY shape is assignable here at the
|
|
51
|
+
* declaration site — the app or auth package declares its real fields via
|
|
52
|
+
* module augmentation:
|
|
53
|
+
*
|
|
54
|
+
* ```typescript
|
|
55
|
+
* declare module "@warlock.js/core" {
|
|
56
|
+
* interface RequestUser {
|
|
57
|
+
* id: string | number;
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* Replaces the v4 `GuardedRequest` convention
|
|
63
|
+
* (`create-warlock/.../guarded.request.ts` — `Request<T> & { user: User }`,
|
|
64
|
+
* an intersection type hand-declared per app) with a property core itself
|
|
65
|
+
* declares and types. `clearCurrentUser()` below is the one place core
|
|
66
|
+
* writes it directly; auth middleware is expected to do the same after a
|
|
67
|
+
* successful token resolution.
|
|
68
|
+
*/
|
|
69
|
+
user?: RequestUser;
|
|
70
|
+
/**
|
|
71
|
+
* Private, server-only, per-request data bag.
|
|
72
|
+
*
|
|
73
|
+
* Distinct from the input payload (`body` / `query` / `params` / `all()`):
|
|
74
|
+
* a write here never surfaces in `request.all()`, `request.validated()`, or
|
|
75
|
+
* `request.input()`. That is the trap `request.set()` sets for private data
|
|
76
|
+
* — it writes into the payload `all` bag, so anything stored there leaks
|
|
77
|
+
* into every input accessor and, from there, into the client-facing
|
|
78
|
+
* payload. `locals` is the correct home for private per-request app data
|
|
79
|
+
* (a resolved session, a fetched-once model) that must never be mistaken
|
|
80
|
+
* for client input.
|
|
81
|
+
*
|
|
82
|
+
* Augmentable via module augmentation, in the module that OWNS the key:
|
|
83
|
+
*
|
|
84
|
+
* ```typescript
|
|
85
|
+
* declare module "@warlock.js/core" {
|
|
86
|
+
* interface RequestLocals {
|
|
87
|
+
* session?: { token: string };
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* A plain class-field initializer is sufficient for "fresh per request":
|
|
93
|
+
* `router.ts:925` constructs `new Request()` for every incoming request —
|
|
94
|
+
* `Request` instances are not pooled or reused across requests — so this
|
|
95
|
+
* initializer runs exactly once per request and no value can leak in from
|
|
96
|
+
* a prior one.
|
|
97
|
+
*/
|
|
98
|
+
locals: RequestLocals;
|
|
99
|
+
/**
|
|
100
|
+
* Backing field for the lazily-generated CSP nonce. Left `undefined` until
|
|
101
|
+
* the first `request.nonce` read; see the `nonce` getter below.
|
|
102
|
+
*/
|
|
103
|
+
protected _nonce?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Per-request Content-Security-Policy nonce — a fresh, unguessable value
|
|
106
|
+
* the web layer hands to `<Scripts nonce={...} />` (the inline payload
|
|
107
|
+
* script) and to the `Content-Security-Policy` header, so a strict
|
|
108
|
+
* `script-src 'nonce-...'` allows only the script this request actually
|
|
109
|
+
* rendered.
|
|
110
|
+
*
|
|
111
|
+
* Generated LAZILY on first access, not eagerly in `setRequest()`: most
|
|
112
|
+
* requests (API routes, anything that isn't rendering HTML) never read it,
|
|
113
|
+
* and spending a `randomBytes` call on every single request for a value
|
|
114
|
+
* most of them discard is wasted entropy draw + CPU. Once generated it is
|
|
115
|
+
* cached in `_nonce`, so every subsequent read within the SAME request
|
|
116
|
+
* returns the identical value — required, since the header and the inline
|
|
117
|
+
* `<script>` tag must agree on one nonce. `_nonce` is a plain field on a
|
|
118
|
+
* per-request `Request` instance (see `locals` above — `router.ts:925`,
|
|
119
|
+
* no pooling), so the cache can never leak into the next request; a fresh
|
|
120
|
+
* `Request` means a fresh, unset `_nonce`.
|
|
121
|
+
*
|
|
122
|
+
* 16 random bytes, base64-encoded — the size the CSP Level 3 spec's own
|
|
123
|
+
* examples use, and far more entropy than an attacker could feasibly guess
|
|
124
|
+
* to defeat the policy.
|
|
125
|
+
*/
|
|
126
|
+
get nonce(): string;
|
|
47
127
|
/**
|
|
48
128
|
* Current request instance
|
|
49
129
|
*/
|
|
@@ -57,23 +137,6 @@ declare class Request<RequestValidation = any> {
|
|
|
57
137
|
* Alias to trans method
|
|
58
138
|
*/
|
|
59
139
|
t: ReturnType<typeof trans>;
|
|
60
|
-
/**
|
|
61
|
-
* Dynamic properties index signature
|
|
62
|
-
*
|
|
63
|
-
* This allows attaching custom properties to the request instance,
|
|
64
|
-
* commonly used during validation middleware to attach fetched models.
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* // In validation middleware:
|
|
68
|
-
* const post = await Post.find(request.int("id"));
|
|
69
|
-
* if (!post) return response.notFound();
|
|
70
|
-
* request.post = post; // Attach the model to the request
|
|
71
|
-
*
|
|
72
|
-
* // In route handler:
|
|
73
|
-
* const post = request.post;
|
|
74
|
-
* // Work with the pre-fetched model
|
|
75
|
-
*/
|
|
76
|
-
[key: string]: any;
|
|
77
140
|
/**
|
|
78
141
|
* Locale code
|
|
79
142
|
*/
|
|
@@ -118,6 +181,20 @@ declare class Request<RequestValidation = any> {
|
|
|
118
181
|
* Translate from the given locale code
|
|
119
182
|
*/
|
|
120
183
|
transFrom(localeCode: string, keyword: string, placeholders?: any): any;
|
|
184
|
+
/**
|
|
185
|
+
* Cache one supported locale without coercing request-controlled input.
|
|
186
|
+
*
|
|
187
|
+
* The default is the answer for a client that asked for NOTHING. A client
|
|
188
|
+
* that did ask is only overridden when its value fails a declared
|
|
189
|
+
* `app.localeCodes` allow-list; with no list declared there is nothing to
|
|
190
|
+
* fail, so the requested locale passes through unchanged.
|
|
191
|
+
*/
|
|
192
|
+
protected cacheLocale(candidate: unknown): string;
|
|
193
|
+
/**
|
|
194
|
+
* Resolve the first present Mode B source. Unsupported values fail closed to
|
|
195
|
+
* the configured default instead of widening the application's locale set.
|
|
196
|
+
*/
|
|
197
|
+
protected resolveLocale(): string;
|
|
121
198
|
/**
|
|
122
199
|
* Get current locale code
|
|
123
200
|
*/
|
|
@@ -126,18 +203,16 @@ declare class Request<RequestValidation = any> {
|
|
|
126
203
|
* Set locale code
|
|
127
204
|
*/
|
|
128
205
|
set locale(localeCode: string);
|
|
129
|
-
/**
|
|
130
|
-
* Get locale code that will be used for translation
|
|
131
|
-
*/
|
|
132
|
-
get localized(): any;
|
|
133
206
|
/**
|
|
134
207
|
* Set locale code
|
|
135
208
|
*/
|
|
136
209
|
setLocaleCode(localeCode: string): this;
|
|
137
210
|
/**
|
|
138
|
-
*
|
|
211
|
+
* @deprecated Use `request.locale`. This alias is removed after one version.
|
|
212
|
+
* The legacy default argument is accepted for source compatibility but the
|
|
213
|
+
* resolved default is owned exclusively by app configuration.
|
|
139
214
|
*/
|
|
140
|
-
getLocaleCode(
|
|
215
|
+
getLocaleCode(_legacyDefaultLocaleCode?: string): string;
|
|
141
216
|
/**
|
|
142
217
|
* Get http protocol
|
|
143
218
|
*/
|
|
@@ -145,11 +220,7 @@ declare class Request<RequestValidation = any> {
|
|
|
145
220
|
/**
|
|
146
221
|
* Validate the given validation schema
|
|
147
222
|
*/
|
|
148
|
-
validate(validation: BaseValidator, selectedInputs?: string[]): Promise<
|
|
149
|
-
/**
|
|
150
|
-
* Clear current user
|
|
151
|
-
*/
|
|
152
|
-
clearCurrentUser(): void;
|
|
223
|
+
validate(validation: BaseValidator, selectedInputs?: string[]): Promise<import("@warlock.js/seal").ValidationResult>;
|
|
153
224
|
/**
|
|
154
225
|
* Get value of the given header
|
|
155
226
|
*/
|
|
@@ -203,6 +274,24 @@ declare class Request<RequestValidation = any> {
|
|
|
203
274
|
/**
|
|
204
275
|
* Parse the payload and merge it from the request body, params and query string
|
|
205
276
|
*/
|
|
277
|
+
/**
|
|
278
|
+
* Turn a bracket-notation key into the dotted path `set()` expects.
|
|
279
|
+
*
|
|
280
|
+
* `a[b][c]` -> `a.b.c`. Used only for NON-numeric nesting; numeric indices
|
|
281
|
+
* keep the array-of-objects path in {@link parseBody}, which builds real
|
|
282
|
+
* arrays rather than objects with numeric keys.
|
|
283
|
+
*/
|
|
284
|
+
protected bracketKeyToPath(key: string): string;
|
|
285
|
+
/**
|
|
286
|
+
* Apply the `key[]` array marker to a parsed value.
|
|
287
|
+
*
|
|
288
|
+
* The subtlety this exists to remove: a key declared `[]` should ALWAYS be an
|
|
289
|
+
* array, but the underlying query/body parser only hands us one when the
|
|
290
|
+
* caller sent the key more than once. Deciding the TYPE from the number of
|
|
291
|
+
* occurrences means one selected filter is a string and two are an array —
|
|
292
|
+
* a shape that changes under the user's hands.
|
|
293
|
+
*/
|
|
294
|
+
protected arrayValueFor(value: any, isArrayKey: boolean, parse: (value: any) => any): any;
|
|
206
295
|
protected parsePayload(): void;
|
|
207
296
|
/**
|
|
208
297
|
* Parse body payload
|
|
@@ -223,7 +312,7 @@ declare class Request<RequestValidation = any> {
|
|
|
223
312
|
/**
|
|
224
313
|
* Listen to the given event
|
|
225
314
|
*/
|
|
226
|
-
on(eventName: RequestEvent, callback: any):
|
|
315
|
+
on(eventName: RequestEvent, callback: any): import("@mongez/events").EventSubscription;
|
|
227
316
|
/**
|
|
228
317
|
* Make a log message
|
|
229
318
|
*/
|
|
@@ -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":"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"}
|
package/esm/http/request.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { config } from "../config/config-getter.mjs";
|
|
2
|
+
import { resolveLocaleConfiguration } from "../config/locale-configuration.mjs";
|
|
2
3
|
import { createRequestStore } from "./middleware/inject-request-context.mjs";
|
|
3
4
|
import { validateAll } from "../validation/validateAll.mjs";
|
|
4
5
|
import { Response } from "./response.mjs";
|
|
@@ -10,11 +11,13 @@ import { isEmpty } from "@mongez/supportive-is";
|
|
|
10
11
|
import { Random, except, get, only, rtrim, set, unset } from "@mongez/reinforcements";
|
|
11
12
|
import { colors } from "@mongez/copper";
|
|
12
13
|
import events from "@mongez/events";
|
|
14
|
+
import { randomBytes } from "node:crypto";
|
|
13
15
|
|
|
14
16
|
//#region ../core/src/http/request.ts
|
|
15
17
|
var Request = class Request {
|
|
16
18
|
constructor() {
|
|
17
19
|
this.payload = {};
|
|
20
|
+
this.locals = {};
|
|
18
21
|
this.trans = trans;
|
|
19
22
|
this.t = trans;
|
|
20
23
|
this._locale = "";
|
|
@@ -22,14 +25,39 @@ var Request = class Request {
|
|
|
22
25
|
this.startTime = Date.now();
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
28
|
+
* Per-request Content-Security-Policy nonce — a fresh, unguessable value
|
|
29
|
+
* the web layer hands to `<Scripts nonce={...} />` (the inline payload
|
|
30
|
+
* script) and to the `Content-Security-Policy` header, so a strict
|
|
31
|
+
* `script-src 'nonce-...'` allows only the script this request actually
|
|
32
|
+
* rendered.
|
|
33
|
+
*
|
|
34
|
+
* Generated LAZILY on first access, not eagerly in `setRequest()`: most
|
|
35
|
+
* requests (API routes, anything that isn't rendering HTML) never read it,
|
|
36
|
+
* and spending a `randomBytes` call on every single request for a value
|
|
37
|
+
* most of them discard is wasted entropy draw + CPU. Once generated it is
|
|
38
|
+
* cached in `_nonce`, so every subsequent read within the SAME request
|
|
39
|
+
* returns the identical value — required, since the header and the inline
|
|
40
|
+
* `<script>` tag must agree on one nonce. `_nonce` is a plain field on a
|
|
41
|
+
* per-request `Request` instance (see `locals` above — `router.ts:925`,
|
|
42
|
+
* no pooling), so the cache can never leak into the next request; a fresh
|
|
43
|
+
* `Request` means a fresh, unset `_nonce`.
|
|
44
|
+
*
|
|
45
|
+
* 16 random bytes, base64-encoded — the size the CSP Level 3 spec's own
|
|
46
|
+
* examples use, and far more entropy than an attacker could feasibly guess
|
|
47
|
+
* to defeat the policy.
|
|
48
|
+
*/
|
|
49
|
+
get nonce() {
|
|
50
|
+
if (!this._nonce) this._nonce = randomBytes(16).toString("base64");
|
|
51
|
+
return this._nonce;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
25
54
|
* Set request handler
|
|
26
55
|
*/
|
|
27
56
|
setRequest(request) {
|
|
28
57
|
this.baseRequest = request;
|
|
29
58
|
this.resolveRequestId();
|
|
30
59
|
this.parsePayload();
|
|
31
|
-
|
|
32
|
-
this.trans = this.t = transFrom.bind(null, localeCode);
|
|
60
|
+
this.trans = this.t = (keyword, placeholders) => transFrom(this.getLocaleCode(), keyword, placeholders);
|
|
33
61
|
return this;
|
|
34
62
|
}
|
|
35
63
|
/**
|
|
@@ -67,37 +95,58 @@ var Request = class Request {
|
|
|
67
95
|
return transFrom(localeCode, keyword, placeholders);
|
|
68
96
|
}
|
|
69
97
|
/**
|
|
98
|
+
* Cache one supported locale without coercing request-controlled input.
|
|
99
|
+
*
|
|
100
|
+
* The default is the answer for a client that asked for NOTHING. A client
|
|
101
|
+
* that did ask is only overridden when its value fails a declared
|
|
102
|
+
* `app.localeCodes` allow-list; with no list declared there is nothing to
|
|
103
|
+
* fail, so the requested locale passes through unchanged.
|
|
104
|
+
*/
|
|
105
|
+
cacheLocale(candidate) {
|
|
106
|
+
const { defaultLocaleCode, localeCodes } = resolveLocaleConfiguration(config.key("app.localeCode"), config.key("app.localeCodes"));
|
|
107
|
+
const requested = typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
108
|
+
this._locale = requested !== void 0 && (localeCodes === void 0 || localeCodes.includes(requested)) ? requested : defaultLocaleCode;
|
|
109
|
+
return this._locale;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolve the first present Mode B source. Unsupported values fail closed to
|
|
113
|
+
* the configured default instead of widening the application's locale set.
|
|
114
|
+
*/
|
|
115
|
+
resolveLocale() {
|
|
116
|
+
const candidate = [
|
|
117
|
+
this.query["locale"],
|
|
118
|
+
this.cookies["locale"],
|
|
119
|
+
this.header("locale")
|
|
120
|
+
].find((value) => typeof value === "string" && value.length > 0);
|
|
121
|
+
return this.cacheLocale(candidate);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
70
124
|
* Get current locale code
|
|
71
125
|
*/
|
|
72
126
|
get locale() {
|
|
73
127
|
if (this._locale) return this._locale;
|
|
74
|
-
return this.
|
|
128
|
+
return this.resolveLocale();
|
|
75
129
|
}
|
|
76
130
|
/**
|
|
77
131
|
* Set locale code
|
|
78
132
|
*/
|
|
79
133
|
set locale(localeCode) {
|
|
80
|
-
this.
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Get locale code that will be used for translation
|
|
84
|
-
*/
|
|
85
|
-
get localized() {
|
|
86
|
-
if (this._locale) return this._locale;
|
|
87
|
-
return this._locale = this.header("locale") || this.query["locale"];
|
|
134
|
+
this.cacheLocale(localeCode);
|
|
88
135
|
}
|
|
89
136
|
/**
|
|
90
137
|
* Set locale code
|
|
91
138
|
*/
|
|
92
139
|
setLocaleCode(localeCode) {
|
|
93
|
-
this.
|
|
140
|
+
this.locale = localeCode;
|
|
94
141
|
return this;
|
|
95
142
|
}
|
|
96
143
|
/**
|
|
97
|
-
*
|
|
144
|
+
* @deprecated Use `request.locale`. This alias is removed after one version.
|
|
145
|
+
* The legacy default argument is accepted for source compatibility but the
|
|
146
|
+
* resolved default is owned exclusively by app configuration.
|
|
98
147
|
*/
|
|
99
|
-
getLocaleCode(
|
|
100
|
-
return this.locale
|
|
148
|
+
getLocaleCode(_legacyDefaultLocaleCode) {
|
|
149
|
+
return this.locale;
|
|
101
150
|
}
|
|
102
151
|
/**
|
|
103
152
|
* Get http protocol
|
|
@@ -112,12 +161,6 @@ var Request = class Request {
|
|
|
112
161
|
return await v.validate(validation, selectedInputs ? this.only(selectedInputs) : this.all());
|
|
113
162
|
}
|
|
114
163
|
/**
|
|
115
|
-
* Clear current user
|
|
116
|
-
*/
|
|
117
|
-
clearCurrentUser() {
|
|
118
|
-
this.user = void 0;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
164
|
* Get value of the given header
|
|
122
165
|
*/
|
|
123
166
|
header(name, defaultValue = null) {
|
|
@@ -209,6 +252,29 @@ var Request = class Request {
|
|
|
209
252
|
/**
|
|
210
253
|
* Parse the payload and merge it from the request body, params and query string
|
|
211
254
|
*/
|
|
255
|
+
/**
|
|
256
|
+
* Turn a bracket-notation key into the dotted path `set()` expects.
|
|
257
|
+
*
|
|
258
|
+
* `a[b][c]` -> `a.b.c`. Used only for NON-numeric nesting; numeric indices
|
|
259
|
+
* keep the array-of-objects path in {@link parseBody}, which builds real
|
|
260
|
+
* arrays rather than objects with numeric keys.
|
|
261
|
+
*/
|
|
262
|
+
bracketKeyToPath(key) {
|
|
263
|
+
return key.replace(/\]\[/g, ".").replace(/\[/g, ".").replace(/\]/g, "");
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Apply the `key[]` array marker to a parsed value.
|
|
267
|
+
*
|
|
268
|
+
* The subtlety this exists to remove: a key declared `[]` should ALWAYS be an
|
|
269
|
+
* array, but the underlying query/body parser only hands us one when the
|
|
270
|
+
* caller sent the key more than once. Deciding the TYPE from the number of
|
|
271
|
+
* occurrences means one selected filter is a string and two are an array —
|
|
272
|
+
* a shape that changes under the user's hands.
|
|
273
|
+
*/
|
|
274
|
+
arrayValueFor(value, isArrayKey, parse) {
|
|
275
|
+
if (Array.isArray(value)) return value.map(parse);
|
|
276
|
+
return isArrayKey ? [parse(value)] : parse(value);
|
|
277
|
+
}
|
|
212
278
|
parsePayload() {
|
|
213
279
|
this.payload.body = this.parseBody(this.baseRequest.body);
|
|
214
280
|
this.payload.query = this.parseBody(this.baseRequest.query);
|
|
@@ -236,9 +302,13 @@ var Request = class Request {
|
|
|
236
302
|
if (key.includes("][")) {
|
|
237
303
|
const keyParts = key.split("[");
|
|
238
304
|
const keyName = keyParts[0];
|
|
239
|
-
if (!arrayOfObjectValues[keyName]) arrayOfObjectValues[keyName] = [];
|
|
240
305
|
const keyNameParts = keyParts[1].split("]");
|
|
241
306
|
const index = Number(keyNameParts[0]);
|
|
307
|
+
if (Number.isNaN(index)) {
|
|
308
|
+
set(body, this.bracketKeyToPath(key), this.arrayValueFor(value, isArrayKey, this.parseValue.bind(this)));
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (!arrayOfObjectValues[keyName]) arrayOfObjectValues[keyName] = [];
|
|
242
312
|
if (!arrayOfObjectValues[keyName][index]) arrayOfObjectValues[keyName][index] = {};
|
|
243
313
|
const keyName2 = keyParts[2].split("]")[0];
|
|
244
314
|
arrayOfObjectValues[keyName][index][keyName2] = this.parseValue(value);
|
|
@@ -247,7 +317,7 @@ var Request = class Request {
|
|
|
247
317
|
const keyParts = key.split("[");
|
|
248
318
|
const keyName = keyParts[0];
|
|
249
319
|
const keyNameParts = keyParts[1].split("]");
|
|
250
|
-
set(body, keyName + "." + keyNameParts[0],
|
|
320
|
+
set(body, keyName + "." + keyNameParts[0], this.arrayValueFor(value, isArrayKey, this.parseValue.bind(this)));
|
|
251
321
|
continue;
|
|
252
322
|
}
|
|
253
323
|
if (Array.isArray(value)) set(body, key, value.map(this.parseValue.bind(this)));
|
|
@@ -295,7 +365,7 @@ var Request = class Request {
|
|
|
295
365
|
* Listen to the given event
|
|
296
366
|
*/
|
|
297
367
|
on(eventName, callback) {
|
|
298
|
-
return
|
|
368
|
+
return events.subscribe(`request.${eventName}`, callback);
|
|
299
369
|
}
|
|
300
370
|
/**
|
|
301
371
|
* Make a log message
|
|
@@ -403,7 +473,10 @@ var Request = class Request {
|
|
|
403
473
|
this.trigger("executingMiddleware", middlewares, this.route);
|
|
404
474
|
for (const middleware of middlewares) {
|
|
405
475
|
this.log("Executing middleware " + colors.yellowBright(middleware.name));
|
|
406
|
-
const output = await middleware(
|
|
476
|
+
const output = await middleware({
|
|
477
|
+
request: this,
|
|
478
|
+
response: this.response
|
|
479
|
+
});
|
|
407
480
|
this.log("Executed middleware " + colors.yellowBright(middleware.name), "success");
|
|
408
481
|
if (output !== void 0) {
|
|
409
482
|
this.log(colors.yellow("request intercepted by middleware ") + colors.cyanBright(middleware.name), "warn");
|