@nest-extended/core 0.0.2-beta-13 → 0.0.2-beta-15

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/README.md CHANGED
@@ -1,17 +1,74 @@
1
1
  # @nest-extended/core
2
2
 
3
- This package provides the core building blocks for NestJS applications built with the **NestExtended** ecosystem. It includes generic controllers, decorators, and configuration interfaces designed to work seamlessly with `@nest-extended/mongoose`.
3
+ This package provides the core building blocks for NestJS applications built with the **NestExtended** ecosystem. It includes generic controllers, a dynamic configuration module, interceptors, CLS helpers, and type interfaces designed to work seamlessly with `@nest-extended/mongoose`.
4
4
 
5
5
  ## Key Features
6
6
 
7
- - **Generic Controller (`NestController`)**: A base controller class that handles common CRUD operations (`find`, `get`, `create`, `patch`, `delete`) by delegating to a service implementing `ServiceOptions`.
8
- - **Decorators**: Moved to `@nest-extended/decorators`.
9
- - `@User()`
10
- - `@Public()`
11
- - `@ModifyBody()`
7
+ - **Generic Controller (`NestController`)**: A base controller class that handles common CRUD operations (`find`, `get`, `create`, `patch`, `delete`) by delegating to a service implementing `ServiceOptions`. Auto-wires `@Public()`, `@ModifyBody(setCreatedBy())`, and `@User()` decorators.
8
+ - **Dynamic Module (`NestExtendedModule`)**: A global dynamic module configured via `NestExtendedModule.forRoot(config)` that provides app-wide soft delete configuration and automatic `qs` query parser setup. Injects `NEST_EXTENDED_CONFIG` token.
9
+ - **CLS Helpers**: Utilities for `nestjs-cls` (Continuation Local Storage):
10
+ - `getCurrentUser<T>()` — retrieve the authenticated user from CLS context (set by AuthGuard).
11
+ - `CLS_KEYS.USER` — constant for the CLS user key.
12
+ - **Interceptors**:
13
+ - `NullResponseInterceptor` — throws `NotFoundException` on `null`/`undefined` GET responses.
14
+ - **Configuration Types**:
15
+ - `NestExtendedConfig` — root config with `softDelete` and `queryParser` settings.
16
+ - `SoftDeleteConfig` — `getQuery()` and `getData(user)` for soft delete behavior.
17
+ - `QueryParserConfig` — `depth`, `arrayLimit`, `allowDots` for `qs` query parser.
18
+ - `NEST_EXTENDED_CONFIG` — injection token (`Symbol`).
19
+ - **Type Interfaces**:
20
+ - `ServiceOptions<T>` — interface for `_find`, `_get`, `_create`, `_patch`, `_remove`.
21
+ - `NestServiceOptions` — options for `multi`, `softDelete`, `pagination`.
22
+ - `PaginatedResponse<D>` — typed response with `total`, `$limit`, `$skip`, `data`.
23
+ - `RequestBody` — re-export from `@nest-extended/decorators`.
24
+ - **Default Options**: Configurable defaults for `deleteKey` (`'deleted'`), `defaultPagination` (`true`), `defaultLimit` (`20`), `defaultSkip` (`0`), `multi` (`false`).
25
+ - **Constants**: `WeekDays` enum and `EachSlotDurationInMinutes` (30).
26
+ - **Decorators**: Moved to `@nest-extended/decorators` — use `@User()`, `@Public()`, `@ModifyBody()`, `setCreatedBy()`.
12
27
 
13
28
  ## Usage
14
29
 
30
+ ### NestExtendedModule
31
+
32
+ Configure soft delete behavior and query parser globally:
33
+
34
+ ```typescript
35
+ import { NestExtendedModule } from '@nest-extended/core';
36
+
37
+ @Module({
38
+ imports: [
39
+ NestExtendedModule.forRoot({
40
+ softDelete: {
41
+ getQuery: () => ({ deleted: { $ne: true } }),
42
+ getData: (user) => ({
43
+ deleted: true,
44
+ deletedBy: user?._id,
45
+ deletedAt: new Date(),
46
+ }),
47
+ },
48
+ // Query parser is enabled by default with qs (depth: 20, arrayLimit: 100)
49
+ // Customize or disable:
50
+ // queryParser: { depth: 10, arrayLimit: 50, allowDots: true },
51
+ // queryParser: false, // disable qs, use Express default
52
+ }),
53
+ ],
54
+ })
55
+ export class AppModule {}
56
+ ```
57
+
58
+ #### Query Parser
59
+
60
+ The module automatically configures `qs` as the Express query parser on application bootstrap. This enables proper parsing of deeply nested query objects and arrays (required by `@nest-extended/mongoose` query features like `$populate`, `$sort`, etc.).
61
+
62
+ | Option | Type | Default | Description |
63
+ |---|---|---|---|
64
+ | `depth` | `number` | `20` | Maximum nesting depth for query objects |
65
+ | `arrayLimit` | `number` | `100` | Maximum number of array elements |
66
+ | `allowDots` | `boolean` | `false` | Allow dot notation in query keys |
67
+
68
+ - **Enabled by default**: No configuration needed — just use `NestExtendedModule.forRoot()`.
69
+ - **Custom options**: Pass a `QueryParserConfig` object to `queryParser`.
70
+ - **Disable**: Set `queryParser: false` to use Express's default query parser.
71
+
15
72
  ### NestController
16
73
 
17
74
  Extend `NestController` to automatically expose standard CRUD endpoints.
@@ -28,6 +85,29 @@ export class MyController extends NestController<MyResource> {
28
85
  }
29
86
  ```
30
87
 
88
+ ### NullResponseInterceptor
89
+
90
+ Register globally to auto-throw 404 on null GET responses:
91
+
92
+ ```typescript
93
+ import { NullResponseInterceptor } from '@nest-extended/core';
94
+ import { APP_INTERCEPTOR } from '@nestjs/core';
95
+
96
+ providers: [
97
+ { provide: APP_INTERCEPTOR, useClass: NullResponseInterceptor },
98
+ ]
99
+ ```
100
+
101
+ ### CLS Helper
102
+
103
+ Retrieve the authenticated user from CLS context (useful in services):
104
+
105
+ ```typescript
106
+ import { getCurrentUser } from '@nest-extended/core';
107
+
108
+ const user = getCurrentUser();
109
+ ```
110
+
31
111
  ### Decorators
32
112
 
33
113
  Decorators have been moved to their own package.
@@ -35,3 +115,22 @@ Decorators have been moved to their own package.
35
115
  ```typescript
36
116
  import { User, Public, ModifyBody, setCreatedBy } from '@nest-extended/decorators';
37
117
  ```
118
+
119
+ ## Exported API
120
+
121
+ | Export | Type | Description |
122
+ |---|---|---|
123
+ | `NestController` | Class | Generic CRUD controller base class |
124
+ | `NestExtendedModule` | Module | Dynamic config module (`.forRoot()`) |
125
+ | `options` | Object | Default options (deleteKey, pagination, limits) |
126
+ | `getCurrentUser` | Function | Get user from CLS context |
127
+ | `CLS_KEYS` | Const | CLS key constants |
128
+ | `NullResponseInterceptor` | Interceptor | 404 on null GET responses |
129
+ | `NestExtendedConfig` | Interface | Root configuration type |
130
+ | `SoftDeleteConfig` | Interface | Soft delete config type |
131
+ | `QueryParserConfig` | Interface | Query parser options (depth, arrayLimit, allowDots) |
132
+ | `NEST_EXTENDED_CONFIG` | Symbol | DI injection token |
133
+ | `ServiceOptions<T>` | Interface | Service method contract |
134
+ | `NestServiceOptions` | Type | Service behavior options |
135
+ | `PaginatedResponse<D>` | Interface | Paginated response type |
136
+ | `RequestBody` | Type | Re-exported typed request body |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-extended/core",
3
- "version": "0.0.2-beta-13",
3
+ "version": "0.0.2-beta-15",
4
4
  "private": false,
5
5
  "contributors": [
6
6
  {
@@ -18,6 +18,7 @@
18
18
  "main": "./src/index.js",
19
19
  "types": "./src/index.d.ts",
20
20
  "dependencies": {
21
+ "qs": "^6.13.0",
21
22
  "tslib": "^2.3.0"
22
23
  }
23
24
  }
@@ -1,5 +1,10 @@
1
- import { DynamicModule } from '@nestjs/common';
1
+ import { DynamicModule, OnApplicationBootstrap } from '@nestjs/common';
2
+ import { HttpAdapterHost } from '@nestjs/core';
2
3
  import { NestExtendedConfig } from '../types/nest-extended.config';
3
- export declare class NestExtendedModule {
4
+ export declare class NestExtendedModule implements OnApplicationBootstrap {
5
+ private readonly config;
6
+ private readonly httpAdapterHost;
7
+ constructor(config: NestExtendedConfig, httpAdapterHost: HttpAdapterHost);
8
+ onApplicationBootstrap(): void;
4
9
  static forRoot(config?: NestExtendedConfig): DynamicModule;
5
10
  }
@@ -4,8 +4,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.NestExtendedModule = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const common_1 = require("@nestjs/common");
7
+ const core_1 = require("@nestjs/core");
7
8
  const nest_extended_config_1 = require("../types/nest-extended.config");
9
+ const qs = require("qs");
8
10
  let NestExtendedModule = NestExtendedModule_1 = class NestExtendedModule {
11
+ constructor(config, httpAdapterHost) {
12
+ this.config = config;
13
+ this.httpAdapterHost = httpAdapterHost;
14
+ }
15
+ onApplicationBootstrap() {
16
+ var _a, _b, _c, _d;
17
+ const queryParserConfig = this.config.queryParser;
18
+ if (queryParserConfig === false)
19
+ return;
20
+ // Apply qs query parser to the Express app
21
+ const httpAdapter = (_a = this.httpAdapterHost) === null || _a === void 0 ? void 0 : _a.httpAdapter;
22
+ if (httpAdapter) {
23
+ const app = httpAdapter.getInstance();
24
+ const options = typeof queryParserConfig === 'object' ? queryParserConfig : {};
25
+ const depth = (_b = options.depth) !== null && _b !== void 0 ? _b : 20;
26
+ const arrayLimit = (_c = options.arrayLimit) !== null && _c !== void 0 ? _c : 100;
27
+ const allowDots = (_d = options.allowDots) !== null && _d !== void 0 ? _d : false;
28
+ app.set('query parser', (str) => qs.parse(str, { depth, arrayLimit, allowDots }));
29
+ }
30
+ }
9
31
  static forRoot(config = {}) {
10
32
  return {
11
33
  module: NestExtendedModule_1,
@@ -22,6 +44,8 @@ let NestExtendedModule = NestExtendedModule_1 = class NestExtendedModule {
22
44
  };
23
45
  exports.NestExtendedModule = NestExtendedModule;
24
46
  exports.NestExtendedModule = NestExtendedModule = NestExtendedModule_1 = tslib_1.__decorate([
25
- (0, common_1.Module)({})
47
+ (0, common_1.Module)({}),
48
+ tslib_1.__param(0, (0, common_1.Inject)(nest_extended_config_1.NEST_EXTENDED_CONFIG)),
49
+ tslib_1.__metadata("design:paramtypes", [Object, core_1.HttpAdapterHost])
26
50
  ], NestExtendedModule);
27
51
  //# sourceMappingURL=nest-extended.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nest-extended.module.js","sourceRoot":"","sources":["../../../../../packages/core/src/lib/nest-extended.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAAuD;AACvD,wEAAyF;AAGlF,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAC3B,MAAM,CAAC,OAAO,CAAC,SAA6B,EAAE;QAC1C,OAAO;YACH,MAAM,EAAE,oBAAkB;YAC1B,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE;gBACP;oBACI,OAAO,EAAE,2CAAoB;oBAC7B,QAAQ,EAAE,MAAM;iBACnB;aACJ;YACD,OAAO,EAAE,CAAC,2CAAoB,CAAC;SAClC,CAAC;IACN,CAAC;CACJ,CAAA;AAdY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,kBAAkB,CAc9B"}
1
+ {"version":3,"file":"nest-extended.module.js","sourceRoot":"","sources":["../../../../../packages/core/src/lib/nest-extended.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAAuF;AACvF,uCAA+C;AAC/C,wEAAyF;AACzF,yBAAyB;AAGlB,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAC3B,YACmD,MAA0B,EACxD,eAAgC;QADF,WAAM,GAAN,MAAM,CAAoB;QACxD,oBAAe,GAAf,eAAe,CAAiB;IAClD,CAAC;IAEJ,sBAAsB;;QAClB,MAAM,iBAAiB,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QAClD,IAAI,iBAAiB,KAAK,KAAK;YAAE,OAAO;QAExC,2CAA2C;QAC3C,MAAM,WAAW,GAAG,MAAA,IAAI,CAAC,eAAe,0CAAE,WAAW,CAAC;QACtD,IAAI,WAAW,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,OAAO,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,KAAK,GAAG,MAAA,OAAO,CAAC,KAAK,mCAAI,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,MAAA,OAAO,CAAC,UAAU,mCAAI,GAAG,CAAC;YAC7C,MAAM,SAAS,GAAG,MAAA,OAAO,CAAC,SAAS,mCAAI,KAAK,CAAC;YAE7C,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,GAAW,EAAE,EAAE,CACpC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAClD,CAAC;QACN,CAAC;IACL,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,SAA6B,EAAE;QAC1C,OAAO;YACH,MAAM,EAAE,oBAAkB;YAC1B,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE;gBACP;oBACI,OAAO,EAAE,2CAAoB;oBAC7B,QAAQ,EAAE,MAAM;iBACnB;aACJ;YACD,OAAO,EAAE,CAAC,2CAAoB,CAAC;SAClC,CAAC;IACN,CAAC;CACJ,CAAA;AAtCY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,eAAM,EAAC,EAAE,CAAC;IAGF,mBAAA,IAAA,eAAM,EAAC,2CAAoB,CAAC,CAAA;qDACK,sBAAe;GAH5C,kBAAkB,CAsC9B"}
@@ -16,12 +16,27 @@ export interface SoftDeleteConfig {
16
16
  */
17
17
  getData: (user: any) => Record<string, any>;
18
18
  }
19
+ export interface QueryParserConfig {
20
+ /** Maximum depth for nested objects. Default: 20 */
21
+ depth?: number;
22
+ /** Maximum number of array elements. Default: 100 */
23
+ arrayLimit?: number;
24
+ /** Allow dot notation in query keys. Default: false */
25
+ allowDots?: boolean;
26
+ }
19
27
  export interface NestExtendedConfig {
20
28
  /**
21
29
  * Soft delete configuration.
22
30
  * If not provided, default soft delete behavior is used.
23
31
  */
24
32
  softDelete?: SoftDeleteConfig;
33
+ /**
34
+ * Query parser configuration using `qs`.
35
+ * - `true` or `undefined` (default): enables qs with defaults (depth: 20, arrayLimit: 100, allowDots: false)
36
+ * - `QueryParserConfig` object: enables qs with custom options
37
+ * - `false`: disables the qs query parser (uses Express default)
38
+ */
39
+ queryParser?: QueryParserConfig | boolean;
25
40
  }
26
41
  /**
27
42
  * Injection token for NestExtended configuration.
@@ -1 +1 @@
1
- {"version":3,"file":"nest-extended.config.js","sourceRoot":"","sources":["../../../../../packages/core/src/types/nest-extended.config.ts"],"names":[],"mappings":";;;AA4BA;;;GAGG;AACU,QAAA,oBAAoB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC"}
1
+ {"version":3,"file":"nest-extended.config.js","sourceRoot":"","sources":["../../../../../packages/core/src/types/nest-extended.config.ts"],"names":[],"mappings":";;;AA6CA;;;GAGG;AACU,QAAA,oBAAoB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC"}