@nest-toolbox/response-envelope 1.8.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 bogdanlupu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # @nest-toolbox/response-envelope
2
+
3
+ Standard API response envelope with interceptor, exception filter, and helpers for NestJS.
4
+
5
+ [![npm version](https://badge.fury.io/js/%40nest-toolbox%2Fresponse-envelope.svg)](https://www.npmjs.com/package/@nest-toolbox/response-envelope)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - 🚀 **Zero-config** — import the module and every route gets a consistent envelope
11
+ - 🎯 **Decorator-first** — `@SkipEnvelope()` and `@ApiMessage()` for per-route control
12
+ - 🛡️ **Consistent error formatting** — class-validator errors auto-parsed into structured field errors
13
+ - 🔄 **Idempotent** — already-wrapped responses pass through untouched
14
+ - ⚙️ **Configurable** — `forRoot()` / `forRootAsync()` for custom defaults
15
+ - 📦 **Manual helpers** — `success()`, `error()`, `paginated()` for WebSockets, queues, etc.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @nest-toolbox/response-envelope
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ // app.module.ts
27
+ import { Module } from '@nestjs/common';
28
+ import { ResponseEnvelopeModule } from '@nest-toolbox/response-envelope';
29
+
30
+ @Module({
31
+ imports: [ResponseEnvelopeModule.forRoot()],
32
+ })
33
+ export class AppModule {}
34
+ ```
35
+
36
+ That's it! Every route now returns a consistent envelope. No interceptors to register, no filters to bind.
37
+
38
+ ## Response Format
39
+
40
+ ### Success
41
+
42
+ ```json
43
+ {
44
+ "success": true,
45
+ "data": { "id": 1, "name": "Alice" },
46
+ "message": "OK",
47
+ "meta": {
48
+ "timestamp": "2025-02-05T07:32:00.000Z",
49
+ "path": "/api/users/1",
50
+ "statusCode": 200
51
+ }
52
+ }
53
+ ```
54
+
55
+ ### Paginated Success
56
+
57
+ ```json
58
+ {
59
+ "success": true,
60
+ "data": [{ "id": 1 }, { "id": 2 }],
61
+ "message": "OK",
62
+ "meta": {
63
+ "timestamp": "2025-02-05T07:32:00.000Z",
64
+ "path": "/api/users",
65
+ "statusCode": 200,
66
+ "pagination": {
67
+ "page": 1,
68
+ "limit": 20,
69
+ "total": 100,
70
+ "totalPages": 5
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Error
77
+
78
+ ```json
79
+ {
80
+ "success": false,
81
+ "data": null,
82
+ "message": "Validation failed",
83
+ "errors": [
84
+ { "field": "email", "message": "must be a valid email" },
85
+ { "field": "name", "message": "should not be empty" }
86
+ ],
87
+ "meta": {
88
+ "timestamp": "2025-02-05T07:32:00.000Z",
89
+ "path": "/api/users",
90
+ "statusCode": 400
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## API Reference
96
+
97
+ ### `ResponseEnvelopeModule.forRoot(options?)`
98
+
99
+ Register the module globally with static options.
100
+
101
+ ```typescript
102
+ ResponseEnvelopeModule.forRoot({
103
+ defaultMessage: 'Success',
104
+ includePath: true,
105
+ includeTimestamp: true,
106
+ });
107
+ ```
108
+
109
+ | Option | Type | Default | Description |
110
+ |--------|------|---------|-------------|
111
+ | `defaultMessage` | `string` | `'OK'` | Default success message when `@ApiMessage()` is not used |
112
+ | `includePath` | `boolean` | `true` | Include the request path in response meta |
113
+ | `includeTimestamp` | `boolean` | `true` | Include the ISO timestamp in response meta |
114
+
115
+ ### `ResponseEnvelopeModule.forRootAsync(options)`
116
+
117
+ Register the module with async factory injection.
118
+
119
+ ```typescript
120
+ ResponseEnvelopeModule.forRootAsync({
121
+ imports: [ConfigModule],
122
+ useFactory: (config: ConfigService) => ({
123
+ defaultMessage: config.get('API_DEFAULT_MESSAGE', 'OK'),
124
+ includePath: config.get('API_INCLUDE_PATH') !== 'false',
125
+ }),
126
+ inject: [ConfigService],
127
+ });
128
+ ```
129
+
130
+ ### `@SkipEnvelope()`
131
+
132
+ Skip the envelope for a specific route handler. The raw return value is sent as-is.
133
+
134
+ ```typescript
135
+ import { SkipEnvelope } from '@nest-toolbox/response-envelope';
136
+
137
+ @Controller()
138
+ export class AppController {
139
+ @Get('health')
140
+ @SkipEnvelope()
141
+ health() {
142
+ return { status: 'ok' }; // sent as-is, no envelope
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### `@ApiMessage(message)`
148
+
149
+ Set a custom success message for a route handler.
150
+
151
+ ```typescript
152
+ import { ApiMessage } from '@nest-toolbox/response-envelope';
153
+
154
+ @Controller('users')
155
+ export class UsersController {
156
+ @Post()
157
+ @ApiMessage('User created successfully')
158
+ create(@Body() dto: CreateUserDto) {
159
+ return this.usersService.create(dto);
160
+ }
161
+ // → { success: true, data: { ... }, message: "User created successfully", meta: { ... } }
162
+ }
163
+ ```
164
+
165
+ ### `success(data, options?)`
166
+
167
+ Manually create a success envelope. Useful outside the HTTP interceptor (WebSocket gateways, message queues, etc.).
168
+
169
+ ```typescript
170
+ import { success } from '@nest-toolbox/response-envelope';
171
+
172
+ const response = success(user, {
173
+ message: 'User found',
174
+ path: '/api/users/1',
175
+ statusCode: 200,
176
+ });
177
+ ```
178
+
179
+ **Options:** `message?: string`, `path?: string`, `statusCode?: number`, `pagination?: PaginationMeta`
180
+
181
+ ### `error(message, options?)`
182
+
183
+ Manually create an error envelope.
184
+
185
+ ```typescript
186
+ import { error } from '@nest-toolbox/response-envelope';
187
+
188
+ const response = error('Validation failed', {
189
+ statusCode: 400,
190
+ errors: [{ field: 'email', message: 'must be a valid email' }],
191
+ path: '/api/users',
192
+ });
193
+ ```
194
+
195
+ **Options:** `errors?: FieldError[]`, `path?: string`, `statusCode?: number`
196
+
197
+ ### `paginated(data, pagination, options?)`
198
+
199
+ Create a paginated success envelope with pagination metadata.
200
+
201
+ ```typescript
202
+ import { paginated } from '@nest-toolbox/response-envelope';
203
+
204
+ const response = paginated(users, { page: 1, limit: 20, total: 100 }, {
205
+ message: 'Users retrieved',
206
+ path: '/api/users',
207
+ });
208
+ // → meta.pagination = { page: 1, limit: 20, total: 100, totalPages: 5 }
209
+ ```
210
+
211
+ **Pagination:** `page: number`, `limit: number`, `total: number`
212
+ **Options:** `message?: string`, `path?: string`, `statusCode?: number`
213
+
214
+ ## Types
215
+
216
+ All types are exported from the package. See [`src/types.ts`](./src/types.ts) for full definitions.
217
+
218
+ | Type | Description |
219
+ |------|-------------|
220
+ | `ApiResponse<T>` | Standard success response envelope |
221
+ | `ApiErrorResponse` | Standard error response envelope |
222
+ | `ResponseMeta` | Metadata attached to every response (timestamp, path, statusCode) |
223
+ | `PaginationMeta` | Pagination info (page, limit, total, totalPages) |
224
+ | `FieldError` | Structured field-level error (`{ field, message }`) |
225
+ | `ResponseEnvelopeOptions` | Static configuration options for `forRoot()` |
226
+ | `ResponseEnvelopeAsyncOptions` | Async configuration options for `forRootAsync()` |
227
+
228
+ ## Integration with Other Toolbox Packages
229
+
230
+ ### @nest-toolbox/typeorm-paginate
231
+
232
+ The `paginated()` helper pairs naturally with [`@nest-toolbox/typeorm-paginate`](../typeorm-paginate):
233
+
234
+ ```typescript
235
+ import { paginate } from '@nest-toolbox/typeorm-paginate';
236
+ import { paginated } from '@nest-toolbox/response-envelope';
237
+
238
+ @Get()
239
+ async findAll(@Query() query: PaginationDto) {
240
+ const result = await paginate(this.userRepository, {
241
+ page: query.page,
242
+ limit: query.limit,
243
+ });
244
+
245
+ return paginated(result.items, {
246
+ page: result.meta.currentPage,
247
+ limit: result.meta.itemsPerPage,
248
+ total: result.meta.totalItems,
249
+ });
250
+ }
251
+ ```
252
+
253
+ ## License
254
+
255
+ MIT © [Bogdan Lupu](https://github.com/lupu60)
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Constants for @nest-toolbox/response-envelope
3
+ */
4
+ /** Metadata key for @SkipEnvelope() decorator */
5
+ export declare const SKIP_ENVELOPE_KEY = "response-envelope:skip";
6
+ /** Metadata key for @ApiMessage() decorator */
7
+ export declare const API_MESSAGE_KEY = "response-envelope:message";
8
+ /** Injection token for module options */
9
+ export declare const RESPONSE_ENVELOPE_OPTIONS = "RESPONSE_ENVELOPE_OPTIONS";
10
+ /** Default success message */
11
+ export declare const DEFAULT_MESSAGE = "OK";
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * Constants for @nest-toolbox/response-envelope
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_MESSAGE = exports.RESPONSE_ENVELOPE_OPTIONS = exports.API_MESSAGE_KEY = exports.SKIP_ENVELOPE_KEY = void 0;
7
+ /** Metadata key for @SkipEnvelope() decorator */
8
+ exports.SKIP_ENVELOPE_KEY = 'response-envelope:skip';
9
+ /** Metadata key for @ApiMessage() decorator */
10
+ exports.API_MESSAGE_KEY = 'response-envelope:message';
11
+ /** Injection token for module options */
12
+ exports.RESPONSE_ENVELOPE_OPTIONS = 'RESPONSE_ENVELOPE_OPTIONS';
13
+ /** Default success message */
14
+ exports.DEFAULT_MESSAGE = 'OK';
15
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,iDAAiD;AACpC,QAAA,iBAAiB,GAAG,wBAAwB,CAAC;AAE1D,+CAA+C;AAClC,QAAA,eAAe,GAAG,2BAA2B,CAAC;AAE3D,yCAAyC;AAC5B,QAAA,yBAAyB,GAAG,2BAA2B,CAAC;AAErE,8BAA8B;AACjB,QAAA,eAAe,GAAG,IAAI,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Decorators for @nest-toolbox/response-envelope
3
+ */
4
+ /**
5
+ * Skip the response envelope for a specific route handler.
6
+ * The raw controller return value will be sent as-is.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * @Get('health')
11
+ * @SkipEnvelope()
12
+ * health() {
13
+ * return { status: 'ok' };
14
+ * }
15
+ * ```
16
+ */
17
+ export declare const SkipEnvelope: () => import("@nestjs/common").CustomDecorator<string>;
18
+ /**
19
+ * Set a custom success message for a route handler's response envelope.
20
+ *
21
+ * @param message - The message to include in the response envelope
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * @Post()
26
+ * @ApiMessage('User created successfully')
27
+ * create(@Body() dto: CreateUserDto) {
28
+ * return this.usersService.create(dto);
29
+ * }
30
+ * ```
31
+ */
32
+ export declare const ApiMessage: (message: string) => import("@nestjs/common").CustomDecorator<string>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * Decorators for @nest-toolbox/response-envelope
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ApiMessage = exports.SkipEnvelope = void 0;
7
+ const common_1 = require("@nestjs/common");
8
+ const constants_1 = require("./constants");
9
+ /**
10
+ * Skip the response envelope for a specific route handler.
11
+ * The raw controller return value will be sent as-is.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * @Get('health')
16
+ * @SkipEnvelope()
17
+ * health() {
18
+ * return { status: 'ok' };
19
+ * }
20
+ * ```
21
+ */
22
+ const SkipEnvelope = () => (0, common_1.SetMetadata)(constants_1.SKIP_ENVELOPE_KEY, true);
23
+ exports.SkipEnvelope = SkipEnvelope;
24
+ /**
25
+ * Set a custom success message for a route handler's response envelope.
26
+ *
27
+ * @param message - The message to include in the response envelope
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * @Post()
32
+ * @ApiMessage('User created successfully')
33
+ * create(@Body() dto: CreateUserDto) {
34
+ * return this.usersService.create(dto);
35
+ * }
36
+ * ```
37
+ */
38
+ const ApiMessage = (message) => (0, common_1.SetMetadata)(constants_1.API_MESSAGE_KEY, message);
39
+ exports.ApiMessage = ApiMessage;
40
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,2CAA6C;AAC7C,2CAAiE;AAEjE;;;;;;;;;;;;GAYG;AACI,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,IAAA,oBAAW,EAAC,6BAAiB,EAAE,IAAI,CAAC,CAAC;AAA1D,QAAA,YAAY,gBAA8C;AAEvE;;;;;;;;;;;;;GAaG;AACI,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,IAAA,oBAAW,EAAC,2BAAe,EAAE,OAAO,CAAC,CAAC;AAAxE,QAAA,UAAU,cAA8D"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Exception filter that formats errors in the standard envelope shape.
3
+ *
4
+ * Handles HttpException (and subclasses like BadRequestException, NotFoundException)
5
+ * as well as unknown errors, formatting them into a consistent ApiErrorResponse.
6
+ */
7
+ import { type ArgumentsHost, type ExceptionFilter } from '@nestjs/common';
8
+ import type { ResponseEnvelopeOptions } from './types';
9
+ export declare class EnvelopeExceptionFilter implements ExceptionFilter {
10
+ private readonly options?;
11
+ constructor(options?: ResponseEnvelopeOptions | undefined);
12
+ catch(exception: unknown, host: ArgumentsHost): void;
13
+ }
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ /**
3
+ * Exception filter that formats errors in the standard envelope shape.
4
+ *
5
+ * Handles HttpException (and subclasses like BadRequestException, NotFoundException)
6
+ * as well as unknown errors, formatting them into a consistent ApiErrorResponse.
7
+ */
8
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
9
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
10
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
11
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
12
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
13
+ };
14
+ var __metadata = (this && this.__metadata) || function (k, v) {
15
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
16
+ };
17
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
18
+ return function (target, key) { decorator(target, key, paramIndex); }
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.EnvelopeExceptionFilter = void 0;
22
+ const common_1 = require("@nestjs/common");
23
+ const constants_1 = require("./constants");
24
+ let EnvelopeExceptionFilter = class EnvelopeExceptionFilter {
25
+ constructor(options) {
26
+ this.options = options;
27
+ }
28
+ catch(exception, host) {
29
+ var _a, _b, _c, _d, _e;
30
+ const ctx = host.switchToHttp();
31
+ const response = ctx.getResponse();
32
+ const request = ctx.getRequest();
33
+ const statusCode = exception instanceof common_1.HttpException ? exception.getStatus() : common_1.HttpStatus.INTERNAL_SERVER_ERROR;
34
+ const message = extractMessage(exception);
35
+ const errors = extractFieldErrors(exception);
36
+ const includePath = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.includePath) !== null && _b !== void 0 ? _b : true;
37
+ const includeTimestamp = (_d = (_c = this.options) === null || _c === void 0 ? void 0 : _c.includeTimestamp) !== null && _d !== void 0 ? _d : true;
38
+ const body = {
39
+ success: false,
40
+ data: null,
41
+ message,
42
+ errors,
43
+ meta: {
44
+ timestamp: includeTimestamp ? new Date().toISOString() : '',
45
+ path: includePath ? ((_e = request.url) !== null && _e !== void 0 ? _e : '') : '',
46
+ statusCode,
47
+ },
48
+ };
49
+ response.status(statusCode).json(body);
50
+ }
51
+ };
52
+ exports.EnvelopeExceptionFilter = EnvelopeExceptionFilter;
53
+ exports.EnvelopeExceptionFilter = EnvelopeExceptionFilter = __decorate([
54
+ (0, common_1.Catch)(),
55
+ (0, common_1.Injectable)(),
56
+ __param(0, (0, common_1.Optional)()),
57
+ __param(0, (0, common_1.Inject)(constants_1.RESPONSE_ENVELOPE_OPTIONS)),
58
+ __metadata("design:paramtypes", [Object])
59
+ ], EnvelopeExceptionFilter);
60
+ /**
61
+ * Extract a human-readable message from the exception.
62
+ */
63
+ function extractMessage(exception) {
64
+ if (exception instanceof common_1.HttpException) {
65
+ const response = exception.getResponse();
66
+ if (typeof response === 'string') {
67
+ return response;
68
+ }
69
+ if (typeof response === 'object' && response !== null) {
70
+ const obj = response;
71
+ if (typeof obj.message === 'string') {
72
+ return obj.message;
73
+ }
74
+ // class-validator returns message as an array
75
+ if (Array.isArray(obj.message) && obj.message.length > 0) {
76
+ return 'Validation failed';
77
+ }
78
+ }
79
+ }
80
+ if (exception instanceof Error) {
81
+ return exception.message;
82
+ }
83
+ return 'Internal server error';
84
+ }
85
+ /**
86
+ * Extract structured field errors from class-validator ValidationPipe responses.
87
+ */
88
+ function extractFieldErrors(exception) {
89
+ if (!(exception instanceof common_1.HttpException)) {
90
+ return [];
91
+ }
92
+ const response = exception.getResponse();
93
+ if (typeof response !== 'object' || response === null) {
94
+ return [];
95
+ }
96
+ const obj = response;
97
+ // class-validator via ValidationPipe returns { message: string[] }
98
+ if (Array.isArray(obj.message)) {
99
+ return obj.message.filter((msg) => typeof msg === 'string').map(parseValidationMessage);
100
+ }
101
+ return [];
102
+ }
103
+ /**
104
+ * Parse a class-validator message like "email must be a valid email" into a FieldError.
105
+ * The first word is typically the field name.
106
+ */
107
+ function parseValidationMessage(message) {
108
+ const spaceIndex = message.indexOf(' ');
109
+ if (spaceIndex === -1) {
110
+ return { field: 'unknown', message };
111
+ }
112
+ return {
113
+ field: message.substring(0, spaceIndex),
114
+ message: message.substring(spaceIndex + 1),
115
+ };
116
+ }
117
+ //# sourceMappingURL=envelope-exception.filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope-exception.filter.js","sourceRoot":"","sources":["../src/envelope-exception.filter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Helper functions for manually creating envelope responses.
3
+ *
4
+ * Use these when you need to construct responses outside of the interceptor,
5
+ * for example in WebSocket gateways or message queues.
6
+ */
7
+ import type { ApiErrorResponse, ApiResponse, FieldError, PaginationMeta } from './types';
8
+ /**
9
+ * Create a success envelope.
10
+ *
11
+ * @param data - The response payload
12
+ * @param options - Optional message, path, statusCode, and pagination
13
+ * @returns A typed ApiResponse envelope
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * return success(user, { message: 'User found', path: '/api/users/1' });
18
+ * ```
19
+ */
20
+ export declare function success<T>(data: T, options?: {
21
+ message?: string;
22
+ path?: string;
23
+ statusCode?: number;
24
+ pagination?: PaginationMeta;
25
+ }): ApiResponse<T>;
26
+ /**
27
+ * Create an error envelope.
28
+ *
29
+ * @param message - The error message
30
+ * @param options - Optional errors array, path, and statusCode
31
+ * @returns A typed ApiErrorResponse envelope
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * return error('Validation failed', {
36
+ * statusCode: 400,
37
+ * errors: [{ field: 'email', message: 'must be a valid email' }],
38
+ * });
39
+ * ```
40
+ */
41
+ export declare function error(message: string, options?: {
42
+ errors?: FieldError[];
43
+ path?: string;
44
+ statusCode?: number;
45
+ }): ApiErrorResponse;
46
+ /**
47
+ * Create a paginated success envelope.
48
+ *
49
+ * @param data - The array of items for the current page
50
+ * @param pagination - Pagination metadata (page, limit, total)
51
+ * @param options - Optional message, path, and statusCode
52
+ * @returns A typed ApiResponse envelope with pagination meta
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * return paginated(users, { page: 1, limit: 20, total: 100 }, {
57
+ * path: '/api/users',
58
+ * });
59
+ * ```
60
+ */
61
+ export declare function paginated<T>(data: T[], pagination: {
62
+ page: number;
63
+ limit: number;
64
+ total: number;
65
+ }, options?: {
66
+ message?: string;
67
+ path?: string;
68
+ statusCode?: number;
69
+ }): ApiResponse<T[]>;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Helper functions for manually creating envelope responses.
4
+ *
5
+ * Use these when you need to construct responses outside of the interceptor,
6
+ * for example in WebSocket gateways or message queues.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.success = success;
10
+ exports.error = error;
11
+ exports.paginated = paginated;
12
+ const constants_1 = require("./constants");
13
+ /**
14
+ * Create a success envelope.
15
+ *
16
+ * @param data - The response payload
17
+ * @param options - Optional message, path, statusCode, and pagination
18
+ * @returns A typed ApiResponse envelope
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * return success(user, { message: 'User found', path: '/api/users/1' });
23
+ * ```
24
+ */
25
+ function success(data, options = {}) {
26
+ var _a, _b, _c;
27
+ const meta = {
28
+ timestamp: new Date().toISOString(),
29
+ path: (_a = options.path) !== null && _a !== void 0 ? _a : '',
30
+ statusCode: (_b = options.statusCode) !== null && _b !== void 0 ? _b : 200,
31
+ };
32
+ if (options.pagination) {
33
+ meta.pagination = options.pagination;
34
+ }
35
+ return {
36
+ success: true,
37
+ data,
38
+ message: (_c = options.message) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_MESSAGE,
39
+ meta,
40
+ };
41
+ }
42
+ /**
43
+ * Create an error envelope.
44
+ *
45
+ * @param message - The error message
46
+ * @param options - Optional errors array, path, and statusCode
47
+ * @returns A typed ApiErrorResponse envelope
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * return error('Validation failed', {
52
+ * statusCode: 400,
53
+ * errors: [{ field: 'email', message: 'must be a valid email' }],
54
+ * });
55
+ * ```
56
+ */
57
+ function error(message, options = {}) {
58
+ var _a, _b, _c;
59
+ return {
60
+ success: false,
61
+ data: null,
62
+ message,
63
+ errors: (_a = options.errors) !== null && _a !== void 0 ? _a : [],
64
+ meta: {
65
+ timestamp: new Date().toISOString(),
66
+ path: (_b = options.path) !== null && _b !== void 0 ? _b : '',
67
+ statusCode: (_c = options.statusCode) !== null && _c !== void 0 ? _c : 500,
68
+ },
69
+ };
70
+ }
71
+ /**
72
+ * Create a paginated success envelope.
73
+ *
74
+ * @param data - The array of items for the current page
75
+ * @param pagination - Pagination metadata (page, limit, total)
76
+ * @param options - Optional message, path, and statusCode
77
+ * @returns A typed ApiResponse envelope with pagination meta
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * return paginated(users, { page: 1, limit: 20, total: 100 }, {
82
+ * path: '/api/users',
83
+ * });
84
+ * ```
85
+ */
86
+ function paginated(data, pagination, options = {}) {
87
+ const totalPages = pagination.limit > 0 ? Math.ceil(pagination.total / pagination.limit) : 0;
88
+ return success(data, Object.assign(Object.assign({}, options), { pagination: {
89
+ page: pagination.page,
90
+ limit: pagination.limit,
91
+ total: pagination.total,
92
+ totalPages,
93
+ } }));
94
+ }
95
+ //# sourceMappingURL=helpers.js.map