@ezilemdodana/nest-mapper 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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,315 @@
1
+ # Nest Mapper
2
+
3
+ A lightweight, dependency-minimal, metadata-based object mapper for NestJS.
4
+ Built for explicit, predictable DTO mapping without heavy libraries.
5
+
6
+ ---
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @ezilemdodana/nest-mapper reflect-metadata
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Prerequisite
17
+
18
+ Ensure `reflect-metadata` is imported **once** in your application entry point (usually `main.ts`):
19
+
20
+ ```ts
21
+ import 'reflect-metadata';
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Overview
27
+
28
+ Nest Mapper provides a simple `MapperService` and a single decorator, `@AutoMap()`, to map objects to DTOs.
29
+
30
+ ---
31
+
32
+ ## Key features
33
+
34
+ - **Explicit, opt-in mapping via decorators**
35
+ - **Same-key mapping by default**
36
+ - **Support for different source keys**
37
+ - **Nested source paths (dot notation)**
38
+ - **Sync and async mapping**
39
+ - **Array and Promise support**
40
+ - **Validation for required fields**
41
+ - **Collect-all-errors mode**
42
+ - **No heavy dependencies**
43
+
44
+ ---
45
+
46
+ ## Recommended: Dependency Injection
47
+
48
+ ### Inject into a service or controller
49
+
50
+ ```ts
51
+ import { Injectable } from '@nestjs/common';
52
+ import { MapperService } from '@ezilemdodana/nest-mapper';
53
+
54
+ @Injectable()
55
+ export class UsersService {
56
+ constructor(private readonly mapper: MapperService) {}
57
+
58
+ getUserDto(entity: UserEntity): UserDto {
59
+ return this.mapper.map(entity as any, UserDto, {
60
+ requireDecorators: true,
61
+ validateRequired: true,
62
+ });
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Register once in your module
68
+
69
+ ```ts
70
+ import { Module } from '@nestjs/common';
71
+ import { MapperService } from '@ezilemdodana/nest-mapper';
72
+
73
+ @Module({
74
+ providers: [MapperService],
75
+ exports: [MapperService],
76
+ })
77
+ export class UsersModule {}
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Alternative: Manual Instantiation (No NestJS DI)
83
+
84
+ Useful for:
85
+ - Scripts
86
+ - Tests
87
+ - Non-Nest environments
88
+
89
+ ```ts
90
+ import { MapperService } from '@ezilemdodana/nest-mapper';
91
+
92
+ const mapper = new MapperService();
93
+ const dto = mapper.map(entity as any, UserDto);
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Basic Usage (Same Key Mapping)
99
+
100
+ When source and destination property names are the same, use `@AutoMap()`.
101
+
102
+ ```ts
103
+ import { MapperService, AutoMap } from '@ezilemdodana/nest-mapper';
104
+
105
+ class UserEntity {
106
+ firstName = 'Hello';
107
+ lastName = 'World';
108
+ }
109
+
110
+ class UserDto {
111
+ @AutoMap()
112
+ firstName!: string;
113
+
114
+ @AutoMap()
115
+ lastName!: string;
116
+ }
117
+
118
+ const mapper = new MapperService();
119
+ const dto = mapper.map(new UserEntity() as any, UserDto);
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Mapping From a Different Source Key
125
+
126
+ If the source and destination property names differ, pass the source key to `@AutoMap()`.
127
+
128
+ ```ts
129
+ import { MapperService, AutoMap } from '@ezilemdodana/nest-mapper';
130
+
131
+ class UserEntity {
132
+ first_name = 'Hello';
133
+ last_name = 'World';
134
+ }
135
+
136
+ class UserDto {
137
+ @AutoMap('first_name')
138
+ firstName!: string;
139
+
140
+ @AutoMap('last_name')
141
+ lastName!: string;
142
+ }
143
+
144
+ const mapper = new MapperService();
145
+ const dto = mapper.map(new UserEntity() as any, UserDto);
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Nested Source Path Mapping (Dot Notation)
151
+
152
+ ```ts
153
+ import { MapperService, AutoMap } from '@ezilemdodana/nest-mapper';
154
+
155
+ class UserEntity {
156
+ user = {
157
+ address: {
158
+ street: 'Main Road',
159
+ city: 'Cape Town',
160
+ },
161
+ };
162
+ }
163
+
164
+ class AddressDto {
165
+ @AutoMap('user.address.street')
166
+ street!: string;
167
+
168
+ @AutoMap('user.address.city')
169
+ city!: string;
170
+ }
171
+
172
+ const mapper = new MapperService();
173
+ const dto = mapper.map(new UserEntity() as any, AddressDto);
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Validation (Required Fields)
179
+
180
+ ```ts
181
+ import { MapperService, AutoMap } from '@ezilemdodana/nest-mapper';
182
+
183
+ class UserEntity {
184
+ first_name = 'Hello';
185
+ }
186
+
187
+ class UserDto {
188
+ @AutoMap('first_name', { required: true })
189
+ firstName!: string;
190
+
191
+ @AutoMap('last_name', { required: true, label: 'Surname' })
192
+ lastName!: string;
193
+ }
194
+
195
+ const mapper = new MapperService();
196
+ ```
197
+
198
+ ### Throw mode (default)
199
+
200
+ ```ts
201
+ import { MapperValidationError } from '@ezilemdodana/nest-mapper';
202
+
203
+ try {
204
+ const dto = mapper.map(new UserEntity() as any, UserDto, {
205
+ requireDecorators: true,
206
+ validateRequired: true,
207
+ validationMode: 'throw',
208
+ });
209
+ } catch (e) {
210
+ if (e instanceof MapperValidationError) {
211
+ console.log(e.issues);
212
+ }
213
+ }
214
+ ```
215
+
216
+ ### Collect-all-errors mode
217
+
218
+ ```ts
219
+ const result = mapper.mapSafe(new UserEntity() as any, UserDto, {
220
+ requireDecorators: true,
221
+ validateRequired: true,
222
+ });
223
+
224
+ console.log(result.value);
225
+ console.log(result.errors);
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Async Mapping
231
+
232
+ ### Map a single Promise source
233
+
234
+ ```ts
235
+ const dto = await mapper.mapAsync(
236
+ Promise.resolve(new UserEntity() as any),
237
+ UserDto,
238
+ );
239
+ ```
240
+
241
+ ### Map an array
242
+
243
+ ```ts
244
+ const dtos = mapper.mapArray(
245
+ [new UserEntity() as any, new UserEntity() as any],
246
+ UserDto,
247
+ );
248
+ ```
249
+
250
+ ### Map async arrays
251
+
252
+ ```ts
253
+ const dtos = await mapper.mapArrayAsync(
254
+ [Promise.resolve(new UserEntity() as any)],
255
+ UserDto,
256
+ );
257
+ ```
258
+
259
+ ### Async safe mapping
260
+
261
+ ```ts
262
+ const result = await mapper.mapSafeAsync(
263
+ Promise.resolve(new UserEntity() as any),
264
+ UserDto,
265
+ );
266
+
267
+ console.log(result.value);
268
+ console.log(result.errors);
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Important Notes
274
+
275
+ This mapper iterates over **runtime DTO keys**.
276
+ Ensure all mapped properties are decorated with `@AutoMap()`.
277
+
278
+ ```ts
279
+ class UserDto {
280
+ @AutoMap()
281
+ firstName!: string;
282
+ }
283
+ ```
284
+
285
+ If a property is not decorated and `requireDecorators` is enabled, it will not be mapped.
286
+
287
+ ---
288
+
289
+ ## API Summary
290
+
291
+ ```ts
292
+ mapper.map(source, DestinationClass, options)
293
+ mapper.mapArray(sources, DestinationClass, options)
294
+
295
+ await mapper.mapAsync(sourceOrPromise, DestinationClass, options)
296
+ await mapper.mapArrayAsync(arrayOrPromise, DestinationClass, options)
297
+
298
+ mapper.mapSafe(source, DestinationClass, options)
299
+ mapper.mapArraySafe(sources, DestinationClass, options)
300
+
301
+ await mapper.mapSafeAsync(sourceOrPromise, DestinationClass, options)
302
+ await mapper.mapArraySafeAsync(arrayOrPromise, DestinationClass, options)
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Why Nest Mapper?
308
+
309
+ - No class-transformer
310
+ - No implicit magic
311
+ - Minimal reflection
312
+ - Clear, maintainable DTO mapping
313
+ - Designed specifically for NestJS
314
+
315
+ ---
@@ -0,0 +1 @@
1
+ export declare const AUTO_MAP_METADATA_KEY: string;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AUTO_MAP_METADATA_KEY = void 0;
4
+ exports.AUTO_MAP_METADATA_KEY = 'nest-mapper:auto-map';
@@ -0,0 +1,13 @@
1
+ import { AutoMapOptions } from '../types/auto-map.types';
2
+ /**
3
+ * Marks a DTO property as mappable.
4
+ * - @AutoMap() -> maps from same key
5
+ * - @AutoMap({ required: true }) -> same key + required
6
+ * - @AutoMap('first_name') -> maps from different key
7
+ * - @AutoMap('user.address.street') -> nested source path
8
+ * - @AutoMap('first_name', { required: true }) -> required mapping
9
+ */
10
+ export declare function AutoMap(): PropertyDecorator;
11
+ export declare function AutoMap(options: AutoMapOptions): PropertyDecorator;
12
+ export declare function AutoMap(sourceKey: string): PropertyDecorator;
13
+ export declare function AutoMap(sourceKey: string, options: AutoMapOptions): PropertyDecorator;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AutoMap = AutoMap;
4
+ const metadata_constants_1 = require("../constants/metadata.constants");
5
+ function AutoMap(sourceKeyOrOptions, maybeOptions) {
6
+ return (target, propertyKey) => {
7
+ const sourceKey = typeof sourceKeyOrOptions === 'string'
8
+ ? sourceKeyOrOptions
9
+ : String(propertyKey);
10
+ const options = typeof sourceKeyOrOptions === 'object'
11
+ ? sourceKeyOrOptions
12
+ : (maybeOptions ?? {});
13
+ const metadata = { sourceKey, options };
14
+ Reflect.defineMetadata(metadata_constants_1.AUTO_MAP_METADATA_KEY, metadata, target, propertyKey);
15
+ };
16
+ }
@@ -0,0 +1,18 @@
1
+ export type MapperValidationIssue = {
2
+ /**
3
+ * Destination path (e.g. "UserDto.firstName" or "UserDto.address.street")
4
+ */
5
+ destinationPath: string;
6
+ /**
7
+ * Source path/key (e.g. "first_name" or "user.address.street")
8
+ */
9
+ sourcePath: string;
10
+ /**
11
+ * Human-readable message
12
+ */
13
+ message: string;
14
+ };
15
+ export declare class MapperValidationError extends Error {
16
+ readonly issues: MapperValidationIssue[];
17
+ constructor(issues: MapperValidationIssue[]);
18
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MapperValidationError = void 0;
4
+ class MapperValidationError extends Error {
5
+ constructor(issues) {
6
+ super(issues[0]?.message ?? 'Mapper validation failed.');
7
+ this.name = 'MapperValidationError';
8
+ this.issues = issues;
9
+ }
10
+ }
11
+ exports.MapperValidationError = MapperValidationError;
@@ -0,0 +1,7 @@
1
+ import 'reflect-metadata';
2
+ export * from './mapper.service';
3
+ export * from './decorators/auto-map.decorator';
4
+ export * from './constants/metadata.constants';
5
+ export * from './errors/mapper.errors';
6
+ export * from './types/auto-map.types';
7
+ export * from './utils/path.utils';
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ require("reflect-metadata");
18
+ __exportStar(require("./mapper.service"), exports);
19
+ __exportStar(require("./decorators/auto-map.decorator"), exports);
20
+ __exportStar(require("./constants/metadata.constants"), exports);
21
+ __exportStar(require("./errors/mapper.errors"), exports);
22
+ __exportStar(require("./types/auto-map.types"), exports);
23
+ __exportStar(require("./utils/path.utils"), exports);
@@ -0,0 +1,48 @@
1
+ import 'reflect-metadata';
2
+ import { MapperValidationIssue } from './errors/mapper.errors';
3
+ type AnyRecord = Record<string, unknown>;
4
+ export type MapperOptions = {
5
+ /**
6
+ * If true, throws when a destination property is not decorated with @AutoMap().
7
+ * This prevents “silent no mapping”.
8
+ */
9
+ requireDecorators?: boolean;
10
+ /**
11
+ * If true, required fields produce validation issues.
12
+ */
13
+ validateRequired?: boolean;
14
+ /**
15
+ * Controls behaviour when validation issues exist:
16
+ * - "throw" -> throw MapperValidationError on first mapping call
17
+ * - "collect" -> return issues (via mapSafe / mapArraySafe) without throwing
18
+ */
19
+ validationMode?: 'throw' | 'collect';
20
+ /**
21
+ * If true, supports destination keys like "address.street" (nested destination paths).
22
+ * By default, destination mapping uses the DTO property name exactly.
23
+ */
24
+ allowNestedDestinationPaths?: boolean;
25
+ };
26
+ export type MapperResult<T> = {
27
+ value: T;
28
+ errors: MapperValidationIssue[];
29
+ };
30
+ export declare class MapperService {
31
+ /**
32
+ * Standard mapping. Throws if validationMode is "throw" and issues exist.
33
+ */
34
+ map<TDestination extends object>(source: AnyRecord, destinationClass: new () => TDestination, options?: MapperOptions): TDestination;
35
+ mapArray<TDestination extends object>(sources: AnyRecord[], destinationClass: new () => TDestination, options?: MapperOptions): TDestination[];
36
+ mapAsync<TDestination extends object>(source: AnyRecord | Promise<AnyRecord>, destinationClass: new () => TDestination, options?: MapperOptions): Promise<TDestination>;
37
+ mapArrayAsync<TDestination extends object>(sources: AnyRecord[] | Promise<AnyRecord[]> | Array<AnyRecord | Promise<AnyRecord>>, destinationClass: new () => TDestination, options?: MapperOptions): Promise<TDestination[]>;
38
+ /**
39
+ * Safe mapping: never throws. Always returns { value, errors }.
40
+ * Use this for collect-all-errors mode.
41
+ */
42
+ mapSafe<TDestination extends object>(source: AnyRecord, destinationClass: new () => TDestination, options?: MapperOptions): MapperResult<TDestination>;
43
+ mapArraySafe<TDestination extends object>(sources: AnyRecord[], destinationClass: new () => TDestination, options?: MapperOptions): Array<MapperResult<TDestination>>;
44
+ mapSafeAsync<TDestination extends object>(source: AnyRecord | Promise<AnyRecord>, destinationClass: new () => TDestination, options?: MapperOptions): Promise<MapperResult<TDestination>>;
45
+ mapArraySafeAsync<TDestination extends object>(sources: AnyRecord[] | Promise<AnyRecord[]> | Array<AnyRecord | Promise<AnyRecord>>, destinationClass: new () => TDestination, options?: MapperOptions): Promise<Array<MapperResult<TDestination>>>;
46
+ private mapInternal;
47
+ }
48
+ export {};
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MapperService = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ require("reflect-metadata");
12
+ const metadata_constants_1 = require("./constants/metadata.constants");
13
+ const mapper_errors_1 = require("./errors/mapper.errors");
14
+ const path_utils_1 = require("./utils/path.utils");
15
+ const defaultOptions = {
16
+ requireDecorators: false,
17
+ validateRequired: true,
18
+ validationMode: 'throw',
19
+ allowNestedDestinationPaths: false,
20
+ };
21
+ let MapperService = class MapperService {
22
+ /**
23
+ * Standard mapping. Throws if validationMode is "throw" and issues exist.
24
+ */
25
+ map(source, destinationClass, options) {
26
+ const result = this.mapInternal(source, destinationClass, options);
27
+ if (result.errors.length && result.__cfg?.validationMode === 'throw') {
28
+ throw new mapper_errors_1.MapperValidationError(result.errors);
29
+ }
30
+ return result.value;
31
+ }
32
+ mapArray(sources, destinationClass, options) {
33
+ return sources.map((src) => this.map(src, destinationClass, options));
34
+ }
35
+ async mapAsync(source, destinationClass, options) {
36
+ const resolvedSource = await Promise.resolve(source);
37
+ return this.map(resolvedSource, destinationClass, options);
38
+ }
39
+ async mapArrayAsync(sources, destinationClass, options) {
40
+ const resolved = await Promise.resolve(sources);
41
+ const resolvedSources = await Promise.all(resolved.map((s) => Promise.resolve(s)));
42
+ return resolvedSources.map((src) => this.map(src, destinationClass, options));
43
+ }
44
+ /**
45
+ * Safe mapping: never throws. Always returns { value, errors }.
46
+ * Use this for collect-all-errors mode.
47
+ */
48
+ mapSafe(source, destinationClass, options) {
49
+ const cfg = { ...defaultOptions, ...(options ?? {}), validationMode: 'collect' };
50
+ return this.mapInternal(source, destinationClass, cfg);
51
+ }
52
+ mapArraySafe(sources, destinationClass, options) {
53
+ return sources.map((src) => this.mapSafe(src, destinationClass, options));
54
+ }
55
+ async mapSafeAsync(source, destinationClass, options) {
56
+ const resolvedSource = await Promise.resolve(source);
57
+ return this.mapSafe(resolvedSource, destinationClass, options);
58
+ }
59
+ async mapArraySafeAsync(sources, destinationClass, options) {
60
+ const resolved = await Promise.resolve(sources);
61
+ const resolvedSources = await Promise.all(resolved.map((s) => Promise.resolve(s)));
62
+ return resolvedSources.map((src) => this.mapSafe(src, destinationClass, options));
63
+ }
64
+ mapInternal(source, destinationClass, options) {
65
+ const cfg = { ...defaultOptions, ...(options ?? {}) };
66
+ const instance = new destinationClass();
67
+ const errors = [];
68
+ /**
69
+ * IMPORTANT:
70
+ * This iterates over runtime keys of the DTO instance.
71
+ * That means your DTO fields should exist at runtime OR you should ensure
72
+ * @AutoMap() is applied to fields you expect to map (recommended).
73
+ */
74
+ for (const destinationKey of Object.keys(instance)) {
75
+ const metadata = Reflect.getMetadata(metadata_constants_1.AUTO_MAP_METADATA_KEY, instance, destinationKey);
76
+ // Enforce decorator presence if requested
77
+ if (!metadata) {
78
+ if (cfg.requireDecorators) {
79
+ errors.push({
80
+ destinationPath: `${destinationClass.name}.${destinationKey}`,
81
+ sourcePath: destinationKey,
82
+ message: `Missing @AutoMap() on destination property "${destinationClass.name}.${destinationKey}".`,
83
+ });
84
+ }
85
+ else {
86
+ // Same-key fallback (optional behaviour)
87
+ const value = source[destinationKey];
88
+ if (value !== undefined) {
89
+ instance[destinationKey] = value;
90
+ }
91
+ }
92
+ continue;
93
+ }
94
+ // Source can be a nested path (dot notation)
95
+ const sourceValue = (0, path_utils_1.getValueByPath)(source, metadata.sourceKey);
96
+ // Required validation
97
+ if (cfg.validateRequired && metadata.options.required && sourceValue === undefined) {
98
+ const label = metadata.options.label ? ` (${metadata.options.label})` : '';
99
+ errors.push({
100
+ destinationPath: `${destinationClass.name}.${destinationKey}`,
101
+ sourcePath: metadata.sourceKey,
102
+ message: `Required mapping missing for "${destinationClass.name}.${destinationKey}"${label} (source "${metadata.sourceKey}").`,
103
+ });
104
+ continue;
105
+ }
106
+ // Assign if present
107
+ if (sourceValue !== undefined) {
108
+ if (cfg.allowNestedDestinationPaths && destinationKey.includes('.')) {
109
+ // Optional: allow destinationKey to be nested (rare)
110
+ (0, path_utils_1.setValueByPath)(instance, destinationKey, sourceValue);
111
+ }
112
+ else {
113
+ instance[destinationKey] = sourceValue;
114
+ }
115
+ }
116
+ }
117
+ return {
118
+ value: instance,
119
+ errors,
120
+ __cfg: cfg,
121
+ };
122
+ }
123
+ };
124
+ exports.MapperService = MapperService;
125
+ exports.MapperService = MapperService = __decorate([
126
+ (0, common_1.Injectable)()
127
+ ], MapperService);
@@ -0,0 +1,14 @@
1
+ export type AutoMapOptions = {
2
+ /**
3
+ * If true, a validation error is produced when the source value is missing/undefined.
4
+ */
5
+ required?: boolean;
6
+ /**
7
+ * Optional custom display label for error messages (e.g. "User First Name").
8
+ */
9
+ label?: string;
10
+ };
11
+ export type AutoMapMetadata = {
12
+ sourceKey: string;
13
+ options: AutoMapOptions;
14
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reads a nested value from an object using dot-notation path.
3
+ * Example: getValueByPath(obj, "user.address.street")
4
+ */
5
+ export declare function getValueByPath(obj: unknown, path: string): unknown;
6
+ /**
7
+ * Sets a nested value on an object using dot-notation path.
8
+ * Example: setValueByPath(dto, "address.street", "Main Rd")
9
+ */
10
+ export declare function setValueByPath(obj: unknown, path: string, value: unknown): void;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getValueByPath = getValueByPath;
4
+ exports.setValueByPath = setValueByPath;
5
+ /**
6
+ * Reads a nested value from an object using dot-notation path.
7
+ * Example: getValueByPath(obj, "user.address.street")
8
+ */
9
+ function getValueByPath(obj, path) {
10
+ if (!obj || typeof obj !== 'object')
11
+ return undefined;
12
+ // Fast path for simple keys
13
+ if (!path.includes('.'))
14
+ return obj[path];
15
+ const parts = path.split('.').filter(Boolean);
16
+ let current = obj;
17
+ for (const part of parts) {
18
+ if (!current || typeof current !== 'object')
19
+ return undefined;
20
+ current = current[part];
21
+ }
22
+ return current;
23
+ }
24
+ /**
25
+ * Sets a nested value on an object using dot-notation path.
26
+ * Example: setValueByPath(dto, "address.street", "Main Rd")
27
+ */
28
+ function setValueByPath(obj, path, value) {
29
+ if (!obj || typeof obj !== 'object')
30
+ return;
31
+ // Fast path for simple keys
32
+ if (!path.includes('.')) {
33
+ obj[path] = value;
34
+ return;
35
+ }
36
+ const parts = path.split('.').filter(Boolean);
37
+ const last = parts.pop();
38
+ if (!last)
39
+ return;
40
+ let current = obj;
41
+ for (const part of parts) {
42
+ const next = current[part];
43
+ if (!next || typeof next !== 'object') {
44
+ current[part] = {};
45
+ }
46
+ current = current[part];
47
+ }
48
+ current[last] = value;
49
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@ezilemdodana/nest-mapper",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight metadata-based mapper for NestJS",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": ["dist", "README.md", "LICENSE"],
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.build.json",
10
+ "prepublishOnly": "npm run build"
11
+ },
12
+ "keywords": [
13
+ "nestjs",
14
+ "mapper",
15
+ "dto",
16
+ "typescript",
17
+ "reflect-metadata"
18
+ ],
19
+ "author": "Ezile Mdodana",
20
+ "license": "MIT",
21
+ "peerDependencies": {
22
+ "@nestjs/common": ">=9",
23
+ "reflect-metadata": ">=0.1.13"
24
+ },
25
+ "devDependencies": {
26
+ "@nestjs/common": "^10.0.0",
27
+ "reflect-metadata": "^0.1.13",
28
+ "typescript": "^5.4.0"
29
+ }
30
+ }