typemold 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 +43 -0
- package/README.md +235 -0
- package/dist/cjs/decorators.js +284 -0
- package/dist/cjs/index.js +74 -0
- package/dist/cjs/mapper.js +153 -0
- package/dist/cjs/nestjs/index.js +12 -0
- package/dist/cjs/nestjs/mapper.module.js +103 -0
- package/dist/cjs/nestjs/mapper.service.js +161 -0
- package/dist/cjs/registry.js +179 -0
- package/dist/cjs/types.js +17 -0
- package/dist/cjs/utils.js +136 -0
- package/dist/esm/decorators.js +274 -0
- package/dist/esm/index.js +51 -0
- package/dist/esm/mapper.js +149 -0
- package/dist/esm/nestjs/index.js +6 -0
- package/dist/esm/nestjs/mapper.module.js +100 -0
- package/dist/esm/nestjs/mapper.service.js +125 -0
- package/dist/esm/registry.js +175 -0
- package/dist/esm/types.js +14 -0
- package/dist/esm/utils.js +127 -0
- package/dist/types/decorators.d.ts +206 -0
- package/dist/types/decorators.d.ts.map +1 -0
- package/dist/types/index.d.ts +46 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/mapper.d.ts +93 -0
- package/dist/types/mapper.d.ts.map +1 -0
- package/dist/types/nestjs/index.d.ts +7 -0
- package/dist/types/nestjs/index.d.ts.map +1 -0
- package/dist/types/nestjs/mapper.module.d.ts +89 -0
- package/dist/types/nestjs/mapper.module.d.ts.map +1 -0
- package/dist/types/nestjs/mapper.service.d.ts +80 -0
- package/dist/types/nestjs/mapper.service.d.ts.map +1 -0
- package/dist/types/registry.d.ts +60 -0
- package/dist/types/registry.d.ts.map +1 -0
- package/dist/types/types.d.ts +120 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils.d.ts +30 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @sevirial/nest-mapper - Core Mapper
|
|
4
|
+
* Main mapper class with static methods for easy usage
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.Mapper = void 0;
|
|
8
|
+
require("reflect-metadata");
|
|
9
|
+
const registry_1 = require("./registry");
|
|
10
|
+
/**
|
|
11
|
+
* Main Mapper class - provides static methods for object mapping
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Basic usage
|
|
15
|
+
* const userDto = Mapper.map(userEntity, UserDto);
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // With field projection
|
|
19
|
+
* const minimalUser = Mapper.map(userEntity, UserDto, { pick: ['username', 'avatar'] });
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // With field groups
|
|
23
|
+
* const publicUser = Mapper.map(userEntity, UserDto, { group: 'public' });
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Array mapping
|
|
27
|
+
* const userDtos = Mapper.mapArray(users, UserDto);
|
|
28
|
+
*/
|
|
29
|
+
class Mapper {
|
|
30
|
+
/**
|
|
31
|
+
* Maps a source object to a target DTO class
|
|
32
|
+
*
|
|
33
|
+
* @param source - Source object to map from
|
|
34
|
+
* @param targetType - Target DTO class constructor
|
|
35
|
+
* @param options - Optional mapping options for field projection
|
|
36
|
+
* @returns Mapped target object
|
|
37
|
+
*/
|
|
38
|
+
static map(source, targetType, options) {
|
|
39
|
+
if (source == null) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const mapper = registry_1.MapperFactory.createMapper(targetType, options);
|
|
43
|
+
const context = {
|
|
44
|
+
...this.globalContext,
|
|
45
|
+
extras: options?.extras,
|
|
46
|
+
depth: 0,
|
|
47
|
+
visited: new WeakMap(),
|
|
48
|
+
};
|
|
49
|
+
return mapper(source, context);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Maps an array of source objects to target DTOs
|
|
53
|
+
*
|
|
54
|
+
* @param sources - Array of source objects
|
|
55
|
+
* @param targetType - Target DTO class constructor
|
|
56
|
+
* @param options - Optional mapping options for field projection
|
|
57
|
+
* @returns Array of mapped target objects
|
|
58
|
+
*/
|
|
59
|
+
static mapArray(sources, targetType, options) {
|
|
60
|
+
if (!sources || !Array.isArray(sources)) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const mapper = registry_1.MapperFactory.createMapper(targetType, options);
|
|
64
|
+
const context = {
|
|
65
|
+
...this.globalContext,
|
|
66
|
+
extras: options?.extras,
|
|
67
|
+
depth: 0,
|
|
68
|
+
visited: new WeakMap(),
|
|
69
|
+
};
|
|
70
|
+
const result = new Array(sources.length);
|
|
71
|
+
for (let i = 0; i < sources.length; i++) {
|
|
72
|
+
result[i] =
|
|
73
|
+
sources[i] != null
|
|
74
|
+
? mapper(sources[i], context)
|
|
75
|
+
: null;
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Maps source to target and returns only specified fields (shorthand)
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const result = Mapper.pick(user, UserDto, ['username', 'avatar']);
|
|
84
|
+
*/
|
|
85
|
+
static pick(source, targetType, fields) {
|
|
86
|
+
return this.map(source, targetType, {
|
|
87
|
+
pick: fields,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Maps source to target excluding specified fields (shorthand)
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const result = Mapper.omit(user, UserDto, ['password', 'email']);
|
|
95
|
+
*/
|
|
96
|
+
static omit(source, targetType, fields) {
|
|
97
|
+
return this.map(source, targetType, {
|
|
98
|
+
omit: fields,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Maps source using a predefined field group (shorthand)
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* const result = Mapper.group(user, UserDto, 'minimal');
|
|
106
|
+
*/
|
|
107
|
+
static group(source, targetType, groupName) {
|
|
108
|
+
return this.map(source, targetType, { group: groupName });
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Creates a reusable mapper function for better performance in loops
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* const mapToUserDto = Mapper.createMapper(UserDto);
|
|
115
|
+
* const users = entities.map(mapToUserDto);
|
|
116
|
+
*/
|
|
117
|
+
static createMapper(targetType, options) {
|
|
118
|
+
const compiledMapper = registry_1.MapperFactory.createMapper(targetType, options);
|
|
119
|
+
const context = {
|
|
120
|
+
...this.globalContext,
|
|
121
|
+
extras: options?.extras,
|
|
122
|
+
};
|
|
123
|
+
return (source) => compiledMapper(source, context);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Registers a type converter for automatic type transformations
|
|
127
|
+
*/
|
|
128
|
+
static registerConverter(converter) {
|
|
129
|
+
this.typeConverters.push(converter);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Sets global context that will be available to all transform functions
|
|
133
|
+
*/
|
|
134
|
+
static setGlobalContext(context) {
|
|
135
|
+
this.globalContext = { ...this.globalContext, ...context };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Clears all cached mappers (useful for testing or hot-reload scenarios)
|
|
139
|
+
*/
|
|
140
|
+
static clearCache() {
|
|
141
|
+
registry_1.MappingRegistry.clearCache();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Gets the compiled mapper for inspection (useful for debugging)
|
|
145
|
+
*/
|
|
146
|
+
static getCompiledMapper(targetType, options) {
|
|
147
|
+
const optionsKey = registry_1.MappingRegistry.getOptionsKey(options);
|
|
148
|
+
return registry_1.MappingRegistry.getCompiledMapper(targetType, optionsKey);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
exports.Mapper = Mapper;
|
|
152
|
+
Mapper.typeConverters = [];
|
|
153
|
+
Mapper.globalContext = {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @sevirial/nest-mapper - NestJS Integration
|
|
4
|
+
* Re-exports for NestJS-specific functionality
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MAPPER_OPTIONS = exports.MapperService = exports.MapperModule = void 0;
|
|
8
|
+
var mapper_module_1 = require("./mapper.module");
|
|
9
|
+
Object.defineProperty(exports, "MapperModule", { enumerable: true, get: function () { return mapper_module_1.MapperModule; } });
|
|
10
|
+
var mapper_service_1 = require("./mapper.service");
|
|
11
|
+
Object.defineProperty(exports, "MapperService", { enumerable: true, get: function () { return mapper_service_1.MapperService; } });
|
|
12
|
+
Object.defineProperty(exports, "MAPPER_OPTIONS", { enumerable: true, get: function () { return mapper_service_1.MAPPER_OPTIONS; } });
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @sevirial/nest-mapper - NestJS MapperModule
|
|
4
|
+
* Dynamic module for NestJS integration
|
|
5
|
+
*/
|
|
6
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
9
|
+
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;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
};
|
|
12
|
+
var MapperModule_1;
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.MapperModule = void 0;
|
|
15
|
+
const common_1 = require("@nestjs/common");
|
|
16
|
+
const mapper_service_1 = require("./mapper.service");
|
|
17
|
+
/**
|
|
18
|
+
* NestJS Module for @sevirial/nest-mapper
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Basic usage (global by default)
|
|
22
|
+
* @Module({
|
|
23
|
+
* imports: [MapperModule.forRoot()],
|
|
24
|
+
* })
|
|
25
|
+
* export class AppModule {}
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // With options
|
|
29
|
+
* @Module({
|
|
30
|
+
* imports: [
|
|
31
|
+
* MapperModule.forRoot({
|
|
32
|
+
* enableValidation: true,
|
|
33
|
+
* converters: [myDateConverter],
|
|
34
|
+
* }),
|
|
35
|
+
* ],
|
|
36
|
+
* })
|
|
37
|
+
* export class AppModule {}
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Async configuration
|
|
41
|
+
* @Module({
|
|
42
|
+
* imports: [
|
|
43
|
+
* MapperModule.forRootAsync({
|
|
44
|
+
* imports: [ConfigModule],
|
|
45
|
+
* useFactory: (config: ConfigService) => ({
|
|
46
|
+
* enableValidation: config.get('ENABLE_VALIDATION'),
|
|
47
|
+
* }),
|
|
48
|
+
* inject: [ConfigService],
|
|
49
|
+
* }),
|
|
50
|
+
* ],
|
|
51
|
+
* })
|
|
52
|
+
* export class AppModule {}
|
|
53
|
+
*/
|
|
54
|
+
let MapperModule = MapperModule_1 = class MapperModule {
|
|
55
|
+
/**
|
|
56
|
+
* Configure the mapper module with static options
|
|
57
|
+
*/
|
|
58
|
+
static forRoot(options) {
|
|
59
|
+
const isGlobal = options?.isGlobal ?? true;
|
|
60
|
+
const optionsProvider = {
|
|
61
|
+
provide: mapper_service_1.MAPPER_OPTIONS,
|
|
62
|
+
useValue: options || {},
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
module: MapperModule_1,
|
|
66
|
+
global: isGlobal,
|
|
67
|
+
providers: [optionsProvider, mapper_service_1.MapperService],
|
|
68
|
+
exports: [mapper_service_1.MapperService],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Configure the mapper module with async options (factory pattern)
|
|
73
|
+
*/
|
|
74
|
+
static forRootAsync(options) {
|
|
75
|
+
const isGlobal = options.isGlobal ?? true;
|
|
76
|
+
const asyncOptionsProvider = {
|
|
77
|
+
provide: mapper_service_1.MAPPER_OPTIONS,
|
|
78
|
+
useFactory: options.useFactory,
|
|
79
|
+
inject: options.inject || [],
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
module: MapperModule_1,
|
|
83
|
+
global: isGlobal,
|
|
84
|
+
imports: options.imports || [],
|
|
85
|
+
providers: [asyncOptionsProvider, mapper_service_1.MapperService],
|
|
86
|
+
exports: [mapper_service_1.MapperService],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* For feature modules that need the mapper (when not using isGlobal)
|
|
91
|
+
*/
|
|
92
|
+
static forFeature() {
|
|
93
|
+
return {
|
|
94
|
+
module: MapperModule_1,
|
|
95
|
+
providers: [mapper_service_1.MapperService],
|
|
96
|
+
exports: [mapper_service_1.MapperService],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
exports.MapperModule = MapperModule;
|
|
101
|
+
exports.MapperModule = MapperModule = MapperModule_1 = __decorate([
|
|
102
|
+
(0, common_1.Module)({})
|
|
103
|
+
], MapperModule);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @sevirial/nest-mapper - NestJS MapperService
|
|
4
|
+
* Injectable service for NestJS dependency injection
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
23
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
24
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
25
|
+
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;
|
|
26
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
27
|
+
};
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
46
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
47
|
+
};
|
|
48
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
49
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
50
|
+
};
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.MapperService = exports.MAPPER_OPTIONS = void 0;
|
|
53
|
+
const common_1 = require("@nestjs/common");
|
|
54
|
+
const mapper_1 = require("../mapper");
|
|
55
|
+
/**
|
|
56
|
+
* Injection token for MapperModule options
|
|
57
|
+
*/
|
|
58
|
+
exports.MAPPER_OPTIONS = Symbol("MAPPER_OPTIONS");
|
|
59
|
+
/**
|
|
60
|
+
* Injectable mapper service for NestJS
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* @Injectable()
|
|
64
|
+
* export class UserService {
|
|
65
|
+
* constructor(private readonly mapper: MapperService) {}
|
|
66
|
+
*
|
|
67
|
+
* async getUser(id: string): Promise<UserDto> {
|
|
68
|
+
* const user = await this.userRepo.findOne(id);
|
|
69
|
+
* return this.mapper.map(user, UserDto);
|
|
70
|
+
* }
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
73
|
+
let MapperService = class MapperService {
|
|
74
|
+
constructor(options) {
|
|
75
|
+
this.validator = null;
|
|
76
|
+
this.options = options || {};
|
|
77
|
+
// Register custom converters if provided
|
|
78
|
+
if (this.options.converters) {
|
|
79
|
+
for (const converter of this.options.converters) {
|
|
80
|
+
mapper_1.Mapper.registerConverter(converter);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Set global context if extras provided
|
|
84
|
+
if (this.options.globalExtras) {
|
|
85
|
+
mapper_1.Mapper.setGlobalContext({ extras: this.options.globalExtras });
|
|
86
|
+
}
|
|
87
|
+
// Try to load class-validator if validation is enabled
|
|
88
|
+
if (this.options.enableValidation) {
|
|
89
|
+
this.initializeValidator();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Lazily loads class-validator for hybrid integration
|
|
94
|
+
*/
|
|
95
|
+
async initializeValidator() {
|
|
96
|
+
try {
|
|
97
|
+
this.validator = await Promise.resolve().then(() => __importStar(require("class-validator")));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
console.warn("[@sevirial/nest-mapper] class-validator not found. Validation will be skipped.");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Maps a source object to a target DTO
|
|
105
|
+
*/
|
|
106
|
+
map(source, targetType, options) {
|
|
107
|
+
return mapper_1.Mapper.map(source, targetType, options);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Maps an array of source objects to target DTOs
|
|
111
|
+
*/
|
|
112
|
+
mapArray(sources, targetType, options) {
|
|
113
|
+
return mapper_1.Mapper.mapArray(sources, targetType, options);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Maps and validates the result using class-validator (if enabled)
|
|
117
|
+
*
|
|
118
|
+
* @throws ValidationError[] if validation fails
|
|
119
|
+
*/
|
|
120
|
+
async mapAndValidate(source, targetType, options) {
|
|
121
|
+
const result = this.map(source, targetType, options);
|
|
122
|
+
if (this.validator && this.options.enableValidation) {
|
|
123
|
+
const errors = await this.validator.validate(result);
|
|
124
|
+
if (errors.length > 0) {
|
|
125
|
+
throw errors;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Pick specific fields (shorthand)
|
|
132
|
+
*/
|
|
133
|
+
pick(source, targetType, fields) {
|
|
134
|
+
return mapper_1.Mapper.pick(source, targetType, fields);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Omit specific fields (shorthand)
|
|
138
|
+
*/
|
|
139
|
+
omit(source, targetType, fields) {
|
|
140
|
+
return mapper_1.Mapper.omit(source, targetType, fields);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Use a field group (shorthand)
|
|
144
|
+
*/
|
|
145
|
+
group(source, targetType, groupName) {
|
|
146
|
+
return mapper_1.Mapper.group(source, targetType, groupName);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Creates a reusable mapper function
|
|
150
|
+
*/
|
|
151
|
+
createMapper(targetType, options) {
|
|
152
|
+
return mapper_1.Mapper.createMapper(targetType, options);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
exports.MapperService = MapperService;
|
|
156
|
+
exports.MapperService = MapperService = __decorate([
|
|
157
|
+
(0, common_1.Injectable)(),
|
|
158
|
+
__param(0, (0, common_1.Optional)()),
|
|
159
|
+
__param(0, (0, common_1.Inject)(exports.MAPPER_OPTIONS)),
|
|
160
|
+
__metadata("design:paramtypes", [Object])
|
|
161
|
+
], MapperService);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @sevirial/nest-mapper - Mapping Registry
|
|
4
|
+
* Singleton registry for storing and caching mapping configurations
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MapperFactory = exports.MappingRegistry = void 0;
|
|
8
|
+
require("reflect-metadata");
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
const utils_1 = require("./utils");
|
|
11
|
+
/**
|
|
12
|
+
* Global mapping registry - singleton pattern for performance
|
|
13
|
+
*/
|
|
14
|
+
class MappingRegistryClass {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.registry = new Map();
|
|
17
|
+
this.compiledMappers = new WeakMap();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Gets or creates a registry entry for a target DTO class
|
|
21
|
+
*/
|
|
22
|
+
getEntry(targetType) {
|
|
23
|
+
let entry = this.registry.get(targetType);
|
|
24
|
+
if (!entry) {
|
|
25
|
+
entry = this.createEntryFromMetadata(targetType);
|
|
26
|
+
this.registry.set(targetType, entry);
|
|
27
|
+
}
|
|
28
|
+
return entry;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a mapping entry by reading decorator metadata
|
|
32
|
+
*/
|
|
33
|
+
createEntryFromMetadata(targetType) {
|
|
34
|
+
const propertyMappings = Reflect.getMetadata(types_1.METADATA_KEYS.PROPERTY_MAPPINGS, targetType) ||
|
|
35
|
+
new Map();
|
|
36
|
+
const fieldGroups = Reflect.getMetadata(types_1.METADATA_KEYS.FIELD_GROUPS, targetType) || new Map();
|
|
37
|
+
return {
|
|
38
|
+
targetType,
|
|
39
|
+
propertyConfigs: propertyMappings,
|
|
40
|
+
fieldGroups,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Gets a compiled mapper for the given target type and options signature
|
|
45
|
+
*/
|
|
46
|
+
getCompiledMapper(targetType, optionsKey = "default") {
|
|
47
|
+
const typeMappers = this.compiledMappers.get(targetType);
|
|
48
|
+
return typeMappers?.get(optionsKey);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Stores a compiled mapper for reuse
|
|
52
|
+
*/
|
|
53
|
+
setCompiledMapper(targetType, optionsKey, mapper) {
|
|
54
|
+
let typeMappers = this.compiledMappers.get(targetType);
|
|
55
|
+
if (!typeMappers) {
|
|
56
|
+
typeMappers = new Map();
|
|
57
|
+
this.compiledMappers.set(targetType, typeMappers);
|
|
58
|
+
}
|
|
59
|
+
typeMappers.set(optionsKey, mapper);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Clears all cached mappers (useful for testing)
|
|
63
|
+
*/
|
|
64
|
+
clearCache() {
|
|
65
|
+
this.registry.clear();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generates a cache key for mapping options
|
|
69
|
+
*/
|
|
70
|
+
getOptionsKey(options) {
|
|
71
|
+
if (!options)
|
|
72
|
+
return "default";
|
|
73
|
+
if (options.pick)
|
|
74
|
+
return `pick:${options.pick.sort().join(",")}`;
|
|
75
|
+
if (options.omit)
|
|
76
|
+
return `omit:${options.omit.sort().join(",")}`;
|
|
77
|
+
if (options.group)
|
|
78
|
+
return `group:${options.group}`;
|
|
79
|
+
return "default";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Singleton instance
|
|
84
|
+
*/
|
|
85
|
+
exports.MappingRegistry = new MappingRegistryClass();
|
|
86
|
+
/**
|
|
87
|
+
* Mapper Factory - Creates optimized mapping functions
|
|
88
|
+
*/
|
|
89
|
+
class MapperFactory {
|
|
90
|
+
/**
|
|
91
|
+
* Creates a compiled mapper for the given target type with optional field projection
|
|
92
|
+
*/
|
|
93
|
+
static createMapper(targetType, options) {
|
|
94
|
+
const entry = exports.MappingRegistry.getEntry(targetType);
|
|
95
|
+
const optionsKey = exports.MappingRegistry.getOptionsKey(options);
|
|
96
|
+
// Check cache first
|
|
97
|
+
let compiledMapper = exports.MappingRegistry.getCompiledMapper(targetType, optionsKey);
|
|
98
|
+
if (compiledMapper) {
|
|
99
|
+
return compiledMapper;
|
|
100
|
+
}
|
|
101
|
+
// Determine which properties to include
|
|
102
|
+
const propertiesToMap = this.getPropertiesToMap(entry, options);
|
|
103
|
+
// Build the compiled mapper
|
|
104
|
+
compiledMapper = this.buildMapper(targetType, entry, propertiesToMap);
|
|
105
|
+
// Cache for reuse
|
|
106
|
+
exports.MappingRegistry.setCompiledMapper(targetType, optionsKey, compiledMapper);
|
|
107
|
+
return compiledMapper;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Determines which properties to map based on options
|
|
111
|
+
*/
|
|
112
|
+
static getPropertiesToMap(entry, options) {
|
|
113
|
+
const allConfigs = Array.from(entry.propertyConfigs.values()).filter((config) => !config.ignore);
|
|
114
|
+
if (!options) {
|
|
115
|
+
return allConfigs;
|
|
116
|
+
}
|
|
117
|
+
// Field group selection
|
|
118
|
+
if (options.group) {
|
|
119
|
+
const groupFields = entry.fieldGroups.get(options.group);
|
|
120
|
+
if (groupFields) {
|
|
121
|
+
return allConfigs.filter((config) => groupFields.has(config.targetKey));
|
|
122
|
+
}
|
|
123
|
+
return []; // Group not found, return empty
|
|
124
|
+
}
|
|
125
|
+
// Pick specific fields
|
|
126
|
+
if (options.pick && options.pick.length > 0) {
|
|
127
|
+
const pickSet = new Set(options.pick.map(String));
|
|
128
|
+
return allConfigs.filter((config) => pickSet.has(config.targetKey));
|
|
129
|
+
}
|
|
130
|
+
// Omit specific fields
|
|
131
|
+
if (options.omit && options.omit.length > 0) {
|
|
132
|
+
const omitSet = new Set(options.omit.map(String));
|
|
133
|
+
return allConfigs.filter((config) => !omitSet.has(config.targetKey));
|
|
134
|
+
}
|
|
135
|
+
return allConfigs;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Builds an optimized mapping function
|
|
139
|
+
*/
|
|
140
|
+
static buildMapper(targetType, entry, properties) {
|
|
141
|
+
// Separate transform functions from path mappings for optimization
|
|
142
|
+
const pathMappings = [];
|
|
143
|
+
const transformMappings = [];
|
|
144
|
+
for (const prop of properties) {
|
|
145
|
+
if (prop.isTransform && typeof prop.source === "function") {
|
|
146
|
+
transformMappings.push({
|
|
147
|
+
target: prop.targetKey,
|
|
148
|
+
transform: prop.source,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
pathMappings.push({
|
|
153
|
+
target: prop.targetKey,
|
|
154
|
+
source: prop.source,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Return optimized mapper function
|
|
159
|
+
return (source, context) => {
|
|
160
|
+
if (source == null) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
// Create new instance or plain object based on target type
|
|
164
|
+
const result = new targetType();
|
|
165
|
+
// Apply path mappings (optimized)
|
|
166
|
+
for (let i = 0; i < pathMappings.length; i++) {
|
|
167
|
+
const mapping = pathMappings[i];
|
|
168
|
+
result[mapping.target] = (0, utils_1.getNestedValue)(source, mapping.source);
|
|
169
|
+
}
|
|
170
|
+
// Apply transform mappings
|
|
171
|
+
for (let i = 0; i < transformMappings.length; i++) {
|
|
172
|
+
const mapping = transformMappings[i];
|
|
173
|
+
result[mapping.target] = mapping.transform(source, context);
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.MapperFactory = MapperFactory;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @sevirial/nest-mapper - Type Definitions
|
|
4
|
+
* Core types for the high-performance object mapper
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.METADATA_KEYS = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Metadata key constants
|
|
10
|
+
*/
|
|
11
|
+
exports.METADATA_KEYS = {
|
|
12
|
+
PROPERTY_MAPPINGS: Symbol("sevirial:property-mappings"),
|
|
13
|
+
FIELD_GROUPS: Symbol("sevirial:field-groups"),
|
|
14
|
+
AUTO_MAP: Symbol("sevirial:auto-map"),
|
|
15
|
+
IGNORE: Symbol("sevirial:ignore"),
|
|
16
|
+
NESTED_TYPE: Symbol("sevirial:nested-type"),
|
|
17
|
+
};
|