@depup/nestjs__core 12.0.4-depup.0 → 12.1.1-depup.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 (43) hide show
  1. package/README.md +2 -2
  2. package/adapters/http-adapter.d.ts +53 -3
  3. package/adapters/http-adapter.js +77 -1
  4. package/application-config.d.ts +4 -0
  5. package/application-config.js +7 -0
  6. package/changes.json +1 -1
  7. package/errors/exceptions/invalid-provider.exception.d.ts +5 -0
  8. package/errors/exceptions/invalid-provider.exception.js +7 -0
  9. package/errors/messages.d.ts +2 -1
  10. package/errors/messages.js +6 -0
  11. package/helpers/cookies/cookie-signer.d.ts +32 -0
  12. package/helpers/cookies/cookie-signer.js +70 -0
  13. package/helpers/cookies/parse-cookie-header.d.ts +19 -0
  14. package/helpers/cookies/parse-cookie-header.js +47 -0
  15. package/helpers/cookies/request-cookies.d.ts +30 -0
  16. package/helpers/cookies/request-cookies.js +83 -0
  17. package/helpers/cookies/serialize-cookie.d.ts +14 -0
  18. package/helpers/cookies/serialize-cookie.js +106 -0
  19. package/helpers/handler-metadata-storage.d.ts +1 -1
  20. package/injector/container.js +7 -1
  21. package/injector/injector.d.ts +8 -0
  22. package/injector/injector.js +22 -2
  23. package/injector/instance-wrapper.d.ts +1 -0
  24. package/injector/instance-wrapper.js +9 -0
  25. package/injector/module.js +4 -0
  26. package/injector/settlement-signal.d.ts +15 -4
  27. package/injector/settlement-signal.js +22 -7
  28. package/middleware/middleware-module.js +1 -1
  29. package/nest-application.d.ts +26 -1
  30. package/nest-application.js +75 -0
  31. package/package.json +5 -5
  32. package/router/legacy-route-converter.js +1 -1
  33. package/router/route-params-factory.d.ts +3 -0
  34. package/router/route-params-factory.js +13 -0
  35. package/router/router-execution-context.d.ts +6 -0
  36. package/router/router-execution-context.js +67 -6
  37. package/router/router-explorer.js +9 -6
  38. package/security/cross-origin-protection.d.ts +98 -0
  39. package/security/cross-origin-protection.js +276 -0
  40. package/security/http-security-hook.d.ts +29 -0
  41. package/security/http-security-hook.js +42 -0
  42. package/security/security-headers.d.ts +34 -0
  43. package/security/security-headers.js +342 -0
package/README.md CHANGED
@@ -13,8 +13,8 @@ npm install @depup/nestjs__core
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [@nestjs/core](https://www.npmjs.com/package/@nestjs/core) @ 12.0.4 |
17
- | Processed | 2026-09-21 |
16
+ | Original | [@nestjs/core](https://www.npmjs.com/package/@nestjs/core) @ 12.1.1 |
17
+ | Processed | 2026-09-28 |
18
18
  | Smoke test | failed |
19
19
  | Deps updated | 0 |
20
20
 
@@ -1,6 +1,7 @@
1
- import type { HttpServer, RequestMethod, VersioningOptions } from '@nestjs/common';
2
- import type { RequestHandler, VersionValue } from '@nestjs/common/internal';
1
+ import type { CookieSerializeOptions, HttpServer, RequestMethod, VersioningOptions } from '@nestjs/common';
2
+ import type { RequestHandler, SecurityRequestHook, VersionValue } from '@nestjs/common/internal';
3
3
  import type { NestApplicationOptions } from '@nestjs/common';
4
+ import type { CookieSigner } from '../helpers/cookies/cookie-signer.js';
4
5
  /**
5
6
  * Base class for HTTP platform adapters (see `ExpressAdapter` and
6
7
  * `FastifyAdapter` for reference implementations).
@@ -13,9 +14,12 @@ import type { NestApplicationOptions } from '@nestjs/common';
13
14
  * - default implementations that delegate to the wrapped framework
14
15
  * `instance` (`use()`, the HTTP-verb methods, `listen()`) or are inert
15
16
  * (`init()`, `normalizePath()`, `mapException()`, `beforeClose()`, the
16
- * `setOn*Hook()` setters);
17
+ * `setOn*Hook()` setters), plus an Express-style, `use()`-based
18
+ * `registerSecurityHook()`;
17
19
  * - storage for the native server (`httpServer`) and the framework instance
18
20
  * (`instance`), with their accessors;
21
+ * - `setCookie()` and `clearCookie()`, implemented once on top of
22
+ * `appendHeader()`, so they behave the same on every platform;
19
23
  * - the introspection hooks used by instrumentation tooling.
20
24
  *
21
25
  * Every remaining {@link HttpServer} member is declared abstract here, even
@@ -52,6 +56,11 @@ export declare abstract class AbstractHttpAdapter<TServer = any, TRequest = any,
52
56
  * {@link AbstractHttpAdapter.setOnRouteTriggered}, if any.
53
57
  */
54
58
  protected onRouteTriggered: ((requestMethod: RequestMethod, path: string) => void) | undefined;
59
+ /**
60
+ * Signer built from the `cookies.secret` application option, used by
61
+ * {@link AbstractHttpAdapter.setCookie} for `signed` cookies.
62
+ */
63
+ protected cookieSigner: CookieSigner | undefined;
55
64
  /**
56
65
  * @param instance The framework application instance to delegate to (e.g.
57
66
  * an Express `Application`). Subclasses typically create a default one
@@ -298,6 +307,47 @@ export declare abstract class AbstractHttpAdapter<TServer = any, TRequest = any,
298
307
  * `BadRequestException`.
299
308
  */
300
309
  mapException(error: unknown): unknown;
310
+ /**
311
+ * Installs the request hook of the built-in HTTP security features as a
312
+ * global middleware (`use()`), which fits Express-like frameworks: the hook
313
+ * gets the request and the response, and a rejection is handed to
314
+ * `next(error)`, i.e. to the exception layer. Override when the framework
315
+ * offers an earlier request hook (the Fastify adapter uses `onRequest`).
316
+ *
317
+ * @see {@link HttpServer.registerSecurityHook}
318
+ */
319
+ registerSecurityHook(hook: SecurityRequestHook<TRequest>): any;
320
+ /**
321
+ * Appends a `Set-Cookie` header through {@link AbstractHttpAdapter.appendHeader},
322
+ * so every cookie set during a request is sent, on every platform. The
323
+ * value is percent-encoded, `path` defaults to `/` and `maxAge` is in
324
+ * seconds (not milliseconds, unlike Express' `res.cookie()`); with
325
+ * `signed: true`, the value is signed with the first `cookies.secret`.
326
+ *
327
+ * Throws a `TypeError` when the name, the value or an attribute is not
328
+ * valid per RFC 6265 (which rules out header injection through `;`, CR or
329
+ * LF) or when `sameSite: 'none'` or `partitioned` is set without `secure`,
330
+ * and an `Error` when `signed` is set but no secret is configured.
331
+ *
332
+ * @see {@link HttpServer.setCookie}
333
+ */
334
+ setCookie(response: TResponse, name: string, value: string, options?: CookieSerializeOptions): any;
335
+ /**
336
+ * Appends a `Set-Cookie` header that expires the cookie immediately.
337
+ * `path` and `domain` must match the ones the cookie was set with; `maxAge`,
338
+ * `expires` and `signed` are ignored.
339
+ *
340
+ * @see {@link HttpServer.clearCookie}
341
+ */
342
+ clearCookie(response: TResponse, name: string, options?: CookieSerializeOptions): any;
343
+ /**
344
+ * Sets the signer used for `signed` cookies. Called by `NestApplication`
345
+ * with the signer it builds from the `cookies.secret` application option;
346
+ * configure that option instead of calling this method.
347
+ *
348
+ * @internal
349
+ */
350
+ setCookieSigner(signer: CookieSigner | undefined): void;
301
351
  /**
302
352
  * Stops the server; called by `app.close()`. May return a promise.
303
353
  *
@@ -1,3 +1,4 @@
1
+ import { serializeCookie } from '../helpers/cookies/serialize-cookie.js';
1
2
  /**
2
3
  * Base class for HTTP platform adapters (see `ExpressAdapter` and
3
4
  * `FastifyAdapter` for reference implementations).
@@ -10,9 +11,12 @@
10
11
  * - default implementations that delegate to the wrapped framework
11
12
  * `instance` (`use()`, the HTTP-verb methods, `listen()`) or are inert
12
13
  * (`init()`, `normalizePath()`, `mapException()`, `beforeClose()`, the
13
- * `setOn*Hook()` setters);
14
+ * `setOn*Hook()` setters), plus an Express-style, `use()`-based
15
+ * `registerSecurityHook()`;
14
16
  * - storage for the native server (`httpServer`) and the framework instance
15
17
  * (`instance`), with their accessors;
18
+ * - `setCookie()` and `clearCookie()`, implemented once on top of
19
+ * `appendHeader()`, so they behave the same on every platform;
16
20
  * - the introspection hooks used by instrumentation tooling.
17
21
  *
18
22
  * Every remaining {@link HttpServer} member is declared abstract here, even
@@ -49,6 +53,11 @@ export class AbstractHttpAdapter {
49
53
  * {@link AbstractHttpAdapter.setOnRouteTriggered}, if any.
50
54
  */
51
55
  onRouteTriggered;
56
+ /**
57
+ * Signer built from the `cookies.secret` application option, used by
58
+ * {@link AbstractHttpAdapter.setCookie} for `signed` cookies.
59
+ */
60
+ cookieSigner;
52
61
  /**
53
62
  * @param instance The framework application instance to delegate to (e.g.
54
63
  * an Express `Application`). Subclasses typically create a default one
@@ -220,4 +229,71 @@ export class AbstractHttpAdapter {
220
229
  mapException(error) {
221
230
  return error;
222
231
  }
232
+ /**
233
+ * Installs the request hook of the built-in HTTP security features as a
234
+ * global middleware (`use()`), which fits Express-like frameworks: the hook
235
+ * gets the request and the response, and a rejection is handed to
236
+ * `next(error)`, i.e. to the exception layer. Override when the framework
237
+ * offers an earlier request hook (the Fastify adapter uses `onRequest`).
238
+ *
239
+ * @see {@link HttpServer.registerSecurityHook}
240
+ */
241
+ registerSecurityHook(hook) {
242
+ return this.use((request, response, next) => {
243
+ const error = hook(request, response);
244
+ return error ? next(error) : next();
245
+ });
246
+ }
247
+ /**
248
+ * Appends a `Set-Cookie` header through {@link AbstractHttpAdapter.appendHeader},
249
+ * so every cookie set during a request is sent, on every platform. The
250
+ * value is percent-encoded, `path` defaults to `/` and `maxAge` is in
251
+ * seconds (not milliseconds, unlike Express' `res.cookie()`); with
252
+ * `signed: true`, the value is signed with the first `cookies.secret`.
253
+ *
254
+ * Throws a `TypeError` when the name, the value or an attribute is not
255
+ * valid per RFC 6265 (which rules out header injection through `;`, CR or
256
+ * LF) or when `sameSite: 'none'` or `partitioned` is set without `secure`,
257
+ * and an `Error` when `signed` is set but no secret is configured.
258
+ *
259
+ * @see {@link HttpServer.setCookie}
260
+ */
261
+ setCookie(response, name, value, options = {}) {
262
+ let cookieValue = value;
263
+ if (options.signed) {
264
+ if (!this.cookieSigner) {
265
+ throw new Error(`Cannot sign cookie "${name}": no cookie secret is configured. ` +
266
+ 'Pass "cookies: { secret }" to NestFactory.create().');
267
+ }
268
+ if (typeof value !== 'string') {
269
+ throw new TypeError(`Invalid value for cookie "${name}": expected a string, received ${typeof value}`);
270
+ }
271
+ cookieValue = this.cookieSigner.sign(value);
272
+ }
273
+ return this.appendHeader(response, 'Set-Cookie', serializeCookie(name, cookieValue, options));
274
+ }
275
+ /**
276
+ * Appends a `Set-Cookie` header that expires the cookie immediately.
277
+ * `path` and `domain` must match the ones the cookie was set with; `maxAge`,
278
+ * `expires` and `signed` are ignored.
279
+ *
280
+ * @see {@link HttpServer.clearCookie}
281
+ */
282
+ clearCookie(response, name, options = {}) {
283
+ return this.appendHeader(response, 'Set-Cookie', serializeCookie(name, '', {
284
+ ...options,
285
+ maxAge: 0,
286
+ expires: new Date(0),
287
+ }));
288
+ }
289
+ /**
290
+ * Sets the signer used for `signed` cookies. Called by `NestApplication`
291
+ * with the signer it builds from the `cookies.secret` application option;
292
+ * configure that option instead of calling this method.
293
+ *
294
+ * @internal
295
+ */
296
+ setCookieSigner(signer) {
297
+ this.cookieSigner = signer;
298
+ }
223
299
  }
@@ -1,5 +1,6 @@
1
1
  import type { CanActivate, ExceptionFilter, NestInterceptor, PipeTransform, PreRequestHook, RouteConflictPolicy, RouteResolutionStrategy, VersioningOptions, WebSocketAdapter } from '@nestjs/common';
2
2
  import type { GlobalPrefixOptions } from '@nestjs/common/internal';
3
+ import type { CookieSigner } from './helpers/cookies/cookie-signer.js';
3
4
  import { InstanceWrapper } from './injector/instance-wrapper.js';
4
5
  import { ExcludeRouteMetadata } from './router/interfaces/exclude-route-metadata.interface.js';
5
6
  export declare class ApplicationConfig {
@@ -14,6 +15,7 @@ export declare class ApplicationConfig {
14
15
  private versioningOptions;
15
16
  private routeConflictPolicy;
16
17
  private routeResolutionStrategy;
18
+ private cookieSigner;
17
19
  private readonly globalRequestPipes;
18
20
  private readonly globalRequestFilters;
19
21
  private readonly globalRequestInterceptors;
@@ -53,4 +55,6 @@ export declare class ApplicationConfig {
53
55
  getRouteConflictPolicy(): RouteConflictPolicy | undefined;
54
56
  setRouteResolutionStrategy(strategy: RouteResolutionStrategy | undefined): void;
55
57
  getRouteResolutionStrategy(): RouteResolutionStrategy | undefined;
58
+ setCookieSigner(signer: CookieSigner | undefined): void;
59
+ getCookieSigner(): CookieSigner | undefined;
56
60
  }
@@ -10,6 +10,7 @@ export class ApplicationConfig {
10
10
  versioningOptions;
11
11
  routeConflictPolicy;
12
12
  routeResolutionStrategy;
13
+ cookieSigner;
13
14
  globalRequestPipes = [];
14
15
  globalRequestFilters = [];
15
16
  globalRequestInterceptors = [];
@@ -123,4 +124,10 @@ export class ApplicationConfig {
123
124
  getRouteResolutionStrategy() {
124
125
  return this.routeResolutionStrategy;
125
126
  }
127
+ setCookieSigner(signer) {
128
+ this.cookieSigner = signer;
129
+ }
130
+ getCookieSigner() {
131
+ return this.cookieSigner;
132
+ }
126
133
  }
package/changes.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "bumped": {},
3
- "timestamp": "2026-09-21T08:13:04.811Z",
3
+ "timestamp": "2026-09-28T16:11:01.983Z",
4
4
  "totalUpdated": 0
5
5
  }
@@ -0,0 +1,5 @@
1
+ import { InjectorDependency } from '../../injector/injector.js';
2
+ import { RuntimeException } from './runtime.exception.js';
3
+ export declare class InvalidProviderException extends RuntimeException {
4
+ constructor(token: InjectorDependency, moduleName: string);
5
+ }
@@ -0,0 +1,7 @@
1
+ import { INVALID_PROVIDER_MESSAGE } from '../messages.js';
2
+ import { RuntimeException } from './runtime.exception.js';
3
+ export class InvalidProviderException extends RuntimeException {
4
+ constructor(token, moduleName) {
5
+ super(INVALID_PROVIDER_MESSAGE(token, moduleName));
6
+ }
7
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ForwardReference, Type } from '@nestjs/common';
2
- import { InjectorDependencyContext } from '../injector/injector.js';
2
+ import { InjectorDependency, InjectorDependencyContext } from '../injector/injector.js';
3
3
  import { Module } from '../injector/module.js';
4
4
  export declare const UNKNOWN_DEPENDENCIES_MESSAGE: (type: string | symbol, unknownDependencyContext: InjectorDependencyContext, moduleRef: Module | undefined) => string;
5
5
  export declare const INVALID_MIDDLEWARE_MESSAGE: (text: TemplateStringsArray, name: string) => string;
@@ -7,6 +7,7 @@ export declare const UNDEFINED_FORWARDREF_MESSAGE: (scope: Type<any>[]) => strin
7
7
  export declare const INVALID_MODULE_MESSAGE: (parentModule: any, index: number, scope: any[], receivedValue: unknown) => string;
8
8
  export declare const USING_INVALID_CLASS_AS_A_MODULE_MESSAGE: (metatypeUsedAsAModule: Type | ForwardReference, scope: any[], classKind: "provider" | "controller" | "filter") => string;
9
9
  export declare const UNDEFINED_MODULE_MESSAGE: (parentModule: any, index: number, scope: any[]) => string;
10
+ export declare const INVALID_PROVIDER_MESSAGE: (token: InjectorDependency, moduleName: string) => string;
10
11
  export declare const UNKNOWN_EXPORT_MESSAGE: (token: string | symbol | undefined, module: string) => string;
11
12
  export declare const INVALID_CLASS_MESSAGE: (text: TemplateStringsArray, value: any) => string;
12
13
  export declare const INVALID_CLASS_SCOPE_MESSAGE: (text: TemplateStringsArray, name: string | undefined) => string;
@@ -163,6 +163,12 @@ Potential causes:
163
163
 
164
164
  Scope [${stringifyScope(scope)}]`;
165
165
  };
166
+ export const INVALID_PROVIDER_MESSAGE = (token, moduleName) => `Nest cannot create the ${moduleName} instance.
167
+ The provider ${getDependencyName(token, 'provider')} in the ${moduleName} "providers" array does not define a valid "useClass", "useValue", "useFactory" or "useExisting" property.
168
+
169
+ Potential causes:
170
+ - The value of "useClass", "useFactory" or "useExisting" is undefined at runtime, often because of a circular import between files. Check your import statements.
171
+ - None of these properties is set, or the one that is set is null.`;
166
172
  export const UNKNOWN_EXPORT_MESSAGE = (token = 'item', module) => {
167
173
  token = isSymbol(token) ? token.toString() : token;
168
174
  return `Nest cannot export a provider/module that is not a part of the currently processed module (${module}). Please verify whether the exported ${token} is available in this particular context.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Prefix that marks a signed cookie value, as written by Express'
3
+ * `res.cookie(name, value, { signed: true })` and read by `cookie-parser`.
4
+ */
5
+ export declare const SIGNED_COOKIE_PREFIX = "s:";
6
+ /**
7
+ * Signs and verifies cookie values with HMAC-SHA256.
8
+ *
9
+ * The format is the one of the `cookie-signature` package (used by
10
+ * `cookie-parser` and `express-session`): `value.signature`, where the
11
+ * signature is the base64 digest without `=` padding. Signed values carry the
12
+ * `s:` prefix, so cookies signed by `cookie-parser`/Express with the same
13
+ * secret keep verifying, and so do un-prefixed values signed by
14
+ * `@fastify/cookie`.
15
+ *
16
+ * With several secrets, values are signed with the first one and verified
17
+ * against each of them (secret rotation).
18
+ */
19
+ export declare class CookieSigner {
20
+ private readonly secrets;
21
+ constructor(secret: string | string[]);
22
+ /**
23
+ * Returns `s:<value>.<signature>`, signed with the first secret.
24
+ */
25
+ sign(value: string): string;
26
+ /**
27
+ * Returns the original value when `input` carries a valid signature for
28
+ * any of the secrets, `undefined` otherwise. The `s:` prefix is optional.
29
+ */
30
+ unsign(input: string): string | undefined;
31
+ private digest;
32
+ }
@@ -0,0 +1,70 @@
1
+ import { createHmac, timingSafeEqual } from 'crypto';
2
+ /**
3
+ * Prefix that marks a signed cookie value, as written by Express'
4
+ * `res.cookie(name, value, { signed: true })` and read by `cookie-parser`.
5
+ */
6
+ export const SIGNED_COOKIE_PREFIX = 's:';
7
+ /**
8
+ * Signs and verifies cookie values with HMAC-SHA256.
9
+ *
10
+ * The format is the one of the `cookie-signature` package (used by
11
+ * `cookie-parser` and `express-session`): `value.signature`, where the
12
+ * signature is the base64 digest without `=` padding. Signed values carry the
13
+ * `s:` prefix, so cookies signed by `cookie-parser`/Express with the same
14
+ * secret keep verifying, and so do un-prefixed values signed by
15
+ * `@fastify/cookie`.
16
+ *
17
+ * With several secrets, values are signed with the first one and verified
18
+ * against each of them (secret rotation).
19
+ */
20
+ export class CookieSigner {
21
+ secrets;
22
+ constructor(secret) {
23
+ const secrets = Array.isArray(secret) ? secret : [secret];
24
+ if (secrets.length === 0 ||
25
+ secrets.some(item => typeof item !== 'string' || item.length === 0)) {
26
+ throw new TypeError('The "cookies.secret" option must be a non-empty string or a non-empty array of non-empty strings.');
27
+ }
28
+ this.secrets = [...secrets];
29
+ }
30
+ /**
31
+ * Returns `s:<value>.<signature>`, signed with the first secret.
32
+ */
33
+ sign(value) {
34
+ return `${SIGNED_COOKIE_PREFIX}${value}.${this.digest(value, this.secrets[0])}`;
35
+ }
36
+ /**
37
+ * Returns the original value when `input` carries a valid signature for
38
+ * any of the secrets, `undefined` otherwise. The `s:` prefix is optional.
39
+ */
40
+ unsign(input) {
41
+ if (typeof input !== 'string') {
42
+ return undefined;
43
+ }
44
+ const signed = input.startsWith(SIGNED_COOKIE_PREFIX)
45
+ ? input.slice(SIGNED_COOKIE_PREFIX.length)
46
+ : input;
47
+ const dotIndex = signed.lastIndexOf('.');
48
+ if (dotIndex === -1) {
49
+ return undefined;
50
+ }
51
+ const value = signed.slice(0, dotIndex);
52
+ const signature = Buffer.from(signed.slice(dotIndex + 1));
53
+ let valid = false;
54
+ for (const secret of this.secrets) {
55
+ const expected = Buffer.from(this.digest(value, secret));
56
+ // Every secret is checked, so timing does not reveal which one matched.
57
+ if (expected.length === signature.length &&
58
+ timingSafeEqual(expected, signature)) {
59
+ valid = true;
60
+ }
61
+ }
62
+ return valid ? value : undefined;
63
+ }
64
+ digest(value, secret) {
65
+ return createHmac('sha256', secret)
66
+ .update(value)
67
+ .digest('base64')
68
+ .replace(/=+$/, '');
69
+ }
70
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Dictionary of cookies keyed by name. Created without a prototype, so names
3
+ * such as `__proto__` or `constructor` are stored as plain entries.
4
+ */
5
+ export type CookieRecord = Record<string, string>;
6
+ /**
7
+ * Parses a `Cookie` request header (RFC 6265, section 5.4) into a
8
+ * prototype-less dictionary.
9
+ *
10
+ * - pairs are separated by `;`, and split on the first `=` only;
11
+ * - names and values are trimmed, and a value wrapped in double quotes is
12
+ * unquoted;
13
+ * - values are percent-decoded, falling back to the raw value when they are
14
+ * not valid percent-encoding;
15
+ * - pairs without `=` or with an empty name are ignored;
16
+ * - when a name occurs more than once, the first occurrence wins (browsers
17
+ * send the most specific path first).
18
+ */
19
+ export declare function parseCookieHeader(header: string | string[] | undefined | null): CookieRecord;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Parses a `Cookie` request header (RFC 6265, section 5.4) into a
3
+ * prototype-less dictionary.
4
+ *
5
+ * - pairs are separated by `;`, and split on the first `=` only;
6
+ * - names and values are trimmed, and a value wrapped in double quotes is
7
+ * unquoted;
8
+ * - values are percent-decoded, falling back to the raw value when they are
9
+ * not valid percent-encoding;
10
+ * - pairs without `=` or with an empty name are ignored;
11
+ * - when a name occurs more than once, the first occurrence wins (browsers
12
+ * send the most specific path first).
13
+ */
14
+ export function parseCookieHeader(header) {
15
+ const cookies = Object.create(null);
16
+ if (!header) {
17
+ return cookies;
18
+ }
19
+ const source = Array.isArray(header) ? header.join('; ') : String(header);
20
+ for (const pair of source.split(';')) {
21
+ const eqIndex = pair.indexOf('=');
22
+ if (eqIndex === -1) {
23
+ continue;
24
+ }
25
+ const name = pair.slice(0, eqIndex).trim();
26
+ if (!name || name in cookies) {
27
+ continue;
28
+ }
29
+ let value = pair.slice(eqIndex + 1).trim();
30
+ if (value.length >= 2 && value[0] === '"' && value.endsWith('"')) {
31
+ value = value.slice(1, -1);
32
+ }
33
+ cookies[name] = safeDecode(value);
34
+ }
35
+ return cookies;
36
+ }
37
+ function safeDecode(value) {
38
+ if (!value.includes('%')) {
39
+ return value;
40
+ }
41
+ try {
42
+ return decodeURIComponent(value);
43
+ }
44
+ catch {
45
+ return value;
46
+ }
47
+ }
@@ -0,0 +1,30 @@
1
+ import type { CookieSigner } from './cookie-signer.js';
2
+ import { CookieRecord } from './parse-cookie-header.js';
3
+ /**
4
+ * Returns the request cookies. `req.cookies`, when a cookie middleware
5
+ * (`cookie-parser`, `@fastify/cookie`) already populated it, takes precedence
6
+ * over parsing the `Cookie` header.
7
+ */
8
+ export declare function getRequestCookies(req: Record<string, any>): CookieRecord;
9
+ /**
10
+ * Returns a single request cookie, or `undefined` when it was not sent. Only
11
+ * own entries count, so names such as `constructor` never resolve to
12
+ * inherited members of a middleware-provided `req.cookies` object.
13
+ */
14
+ export declare function getRequestCookie(req: Record<string, any>, name: string): string | undefined;
15
+ /**
16
+ * Returns the request cookies whose signature verifies, with their unsigned
17
+ * value. Cookies that are not signed, or whose signature does not verify, are
18
+ * left out.
19
+ *
20
+ * Without a signer (no `cookies.secret` option), falls back to
21
+ * `req.signedCookies` as populated by `cookie-parser`, dropping the entries it
22
+ * marked invalid (`false`). Throws when neither is available.
23
+ */
24
+ export declare function getRequestSignedCookies(req: Record<string, any>, signer: CookieSigner | undefined): CookieRecord;
25
+ /**
26
+ * Returns the unsigned value of a single signed cookie, or `undefined` when it
27
+ * is missing or its signature does not verify. Same fallbacks as
28
+ * {@link getRequestSignedCookies}.
29
+ */
30
+ export declare function getRequestSignedCookie(req: Record<string, any>, name: string, signer: CookieSigner | undefined): string | undefined;
@@ -0,0 +1,83 @@
1
+ import { isObject } from '@nestjs/common/internal';
2
+ import { parseCookieHeader } from './parse-cookie-header.js';
3
+ /**
4
+ * Parsed `Cookie` header per request, so the header is parsed at most once
5
+ * however many parameters read cookies.
6
+ */
7
+ const parsedCookiesCache = new WeakMap();
8
+ function getParsedCookieHeader(req) {
9
+ let cookies = parsedCookiesCache.get(req);
10
+ if (!cookies) {
11
+ cookies = parseCookieHeader(req.headers?.cookie);
12
+ parsedCookiesCache.set(req, cookies);
13
+ }
14
+ return cookies;
15
+ }
16
+ /**
17
+ * Returns the request cookies. `req.cookies`, when a cookie middleware
18
+ * (`cookie-parser`, `@fastify/cookie`) already populated it, takes precedence
19
+ * over parsing the `Cookie` header.
20
+ */
21
+ export function getRequestCookies(req) {
22
+ if (isObject(req.cookies)) {
23
+ return req.cookies;
24
+ }
25
+ return getParsedCookieHeader(req);
26
+ }
27
+ /**
28
+ * Returns a single request cookie, or `undefined` when it was not sent. Only
29
+ * own entries count, so names such as `constructor` never resolve to
30
+ * inherited members of a middleware-provided `req.cookies` object.
31
+ */
32
+ export function getRequestCookie(req, name) {
33
+ const cookies = getRequestCookies(req);
34
+ return Object.prototype.hasOwnProperty.call(cookies, name)
35
+ ? cookies[name]
36
+ : undefined;
37
+ }
38
+ /**
39
+ * Returns the request cookies whose signature verifies, with their unsigned
40
+ * value. Cookies that are not signed, or whose signature does not verify, are
41
+ * left out.
42
+ *
43
+ * Without a signer (no `cookies.secret` option), falls back to
44
+ * `req.signedCookies` as populated by `cookie-parser`, dropping the entries it
45
+ * marked invalid (`false`). Throws when neither is available.
46
+ */
47
+ export function getRequestSignedCookies(req, signer) {
48
+ const signedCookies = Object.create(null);
49
+ if (signer) {
50
+ const cookies = getParsedCookieHeader(req);
51
+ for (const name of Object.keys(cookies)) {
52
+ const value = signer.unsign(cookies[name]);
53
+ if (value !== undefined) {
54
+ signedCookies[name] = value;
55
+ }
56
+ }
57
+ return signedCookies;
58
+ }
59
+ if (isObject(req.signedCookies)) {
60
+ for (const [name, value] of Object.entries(req.signedCookies)) {
61
+ // `false` marks an invalid signature. Other values are kept as is,
62
+ // including the objects cookie-parser makes of `j:` JSON cookies.
63
+ if (value !== false) {
64
+ signedCookies[name] = value;
65
+ }
66
+ }
67
+ return signedCookies;
68
+ }
69
+ throw new Error('Cannot read signed cookies: no cookie secret is configured. ' +
70
+ 'Pass "cookies: { secret }" to NestFactory.create().');
71
+ }
72
+ /**
73
+ * Returns the unsigned value of a single signed cookie, or `undefined` when it
74
+ * is missing or its signature does not verify. Same fallbacks as
75
+ * {@link getRequestSignedCookies}.
76
+ */
77
+ export function getRequestSignedCookie(req, name, signer) {
78
+ if (signer) {
79
+ const value = getParsedCookieHeader(req)[name];
80
+ return value === undefined ? undefined : signer.unsign(value);
81
+ }
82
+ return getRequestSignedCookies(req, signer)[name];
83
+ }
@@ -0,0 +1,14 @@
1
+ import type { CookieSerializeOptions } from '@nestjs/common';
2
+ /**
3
+ * Builds the value of a `Set-Cookie` response header (RFC 6265, section 4.1).
4
+ *
5
+ * The value is percent-encoded with `encodeURIComponent()`, whose output only
6
+ * contains RFC 6265 `cookie-octet`s. `path` defaults to `/`. Throws a
7
+ * `TypeError` when the name, the value or an attribute would produce an
8
+ * invalid header or smuggle extra attributes (`;`, CR, LF, ...), and when
9
+ * `sameSite: 'none'` or `partitioned` is set without `secure`, since browsers
10
+ * reject such cookies.
11
+ *
12
+ * Signing is not handled here: pass an already signed value.
13
+ */
14
+ export declare function serializeCookie(name: string, value: string, options?: CookieSerializeOptions): string;