@rtpaulino/entity 0.14.2 → 0.16.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/entity-utils.d.ts +86 -3
- package/dist/lib/entity-utils.d.ts.map +1 -1
- package/dist/lib/entity-utils.js +104 -3
- package/dist/lib/entity-utils.js.map +1 -1
- package/dist/lib/property.d.ts +26 -3
- package/dist/lib/property.d.ts.map +1 -1
- package/dist/lib/property.js +58 -7
- package/dist/lib/property.js.map +1 -1
- package/dist/lib/validators.d.ts +57 -0
- package/dist/lib/validators.d.ts.map +1 -1
- package/dist/lib/validators.js +134 -0
- package/dist/lib/validators.js.map +1 -1
- package/dist/lib/zod-property.d.ts +35 -0
- package/dist/lib/zod-property.d.ts.map +1 -0
- package/dist/lib/zod-property.js +73 -0
- package/dist/lib/zod-property.js.map +1 -0
- package/package.json +10 -3
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nexport * from './lib/entity.js';\nexport * from './lib/entity-utils.js';\nexport * from './lib/types.js';\nexport * from './lib/property.js';\nexport * from './lib/validation-error.js';\nexport * from './lib/problem.js';\nexport * from './lib/validation-utils.js';\nexport * from './lib/primitive-deserializers.js';\nexport * from './lib/validators.js';\n"],"names":[],"mappings":"AAAA,OAAO,mBAAmB;AAE1B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,iBAAiB;AAC/B,cAAc,oBAAoB;AAClC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,4BAA4B;AAC1C,cAAc,mCAAmC;AACjD,cAAc,sBAAsB"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import 'reflect-metadata';\n\nexport * from './lib/entity.js';\nexport * from './lib/entity-utils.js';\nexport * from './lib/types.js';\nexport * from './lib/property.js';\nexport * from './lib/validation-error.js';\nexport * from './lib/problem.js';\nexport * from './lib/validation-utils.js';\nexport * from './lib/primitive-deserializers.js';\nexport * from './lib/validators.js';\nexport * from './lib/zod-property.js';\n"],"names":[],"mappings":"AAAA,OAAO,mBAAmB;AAE1B,cAAc,kBAAkB;AAChC,cAAc,wBAAwB;AACtC,cAAc,iBAAiB;AAC/B,cAAc,oBAAoB;AAClC,cAAc,4BAA4B;AAC1C,cAAc,mBAAmB;AACjC,cAAc,4BAA4B;AAC1C,cAAc,mCAAmC;AACjD,cAAc,sBAAsB;AACpC,cAAc,wBAAwB"}
|
|
@@ -112,7 +112,7 @@ export declare class EntityUtils {
|
|
|
112
112
|
* - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored
|
|
113
113
|
* - Property validators run first, then entity validators
|
|
114
114
|
* - Validators can be synchronous or asynchronous
|
|
115
|
-
* - Problems are accessible via EntityUtils.
|
|
115
|
+
* - Problems are accessible via EntityUtils.getProblems()
|
|
116
116
|
* - Raw input data is accessible via EntityUtils.getRawInput()
|
|
117
117
|
*
|
|
118
118
|
* @example
|
|
@@ -135,6 +135,55 @@ export declare class EntityUtils {
|
|
|
135
135
|
static parse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, options?: {
|
|
136
136
|
strict?: boolean;
|
|
137
137
|
}): Promise<T>;
|
|
138
|
+
/**
|
|
139
|
+
* Safely deserializes a plain object to an entity instance without throwing errors
|
|
140
|
+
*
|
|
141
|
+
* @param entityClass - The entity class constructor. Must accept a data object parameter.
|
|
142
|
+
* @param plainObject - The plain object to deserialize
|
|
143
|
+
* @param options - Parse options (strict mode)
|
|
144
|
+
* @returns Promise resolving to a result object with success flag, data, and problems
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* Similar to parse() but returns a result object instead of throwing errors:
|
|
148
|
+
* - On success with strict: true - returns { success: true, data, problems: [] }
|
|
149
|
+
* - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)
|
|
150
|
+
* - On failure - returns { success: false, data: undefined, problems: [...] }
|
|
151
|
+
*
|
|
152
|
+
* All deserialization and validation rules from parse() apply.
|
|
153
|
+
* See parse() documentation for detailed deserialization behavior.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* @Entity()
|
|
158
|
+
* class User {
|
|
159
|
+
* @Property({ type: () => String }) name!: string;
|
|
160
|
+
* @Property({ type: () => Number }) age!: number;
|
|
161
|
+
*
|
|
162
|
+
* constructor(data: Partial<User>) {
|
|
163
|
+
* Object.assign(this, data);
|
|
164
|
+
* }
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* const result = await EntityUtils.safeParse(User, { name: 'John', age: 30 });
|
|
168
|
+
* if (result.success) {
|
|
169
|
+
* console.log(result.data); // User instance
|
|
170
|
+
* console.log(result.problems); // [] or soft problems if not strict
|
|
171
|
+
* } else {
|
|
172
|
+
* console.log(result.problems); // Hard problems
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
static safeParse<T extends object>(entityClass: new (data: any) => T, plainObject: unknown, options?: {
|
|
177
|
+
strict?: boolean;
|
|
178
|
+
}): Promise<{
|
|
179
|
+
success: true;
|
|
180
|
+
data: T;
|
|
181
|
+
problems: Problem[];
|
|
182
|
+
} | {
|
|
183
|
+
success: false;
|
|
184
|
+
data: undefined;
|
|
185
|
+
problems: Problem[];
|
|
186
|
+
}>;
|
|
138
187
|
/**
|
|
139
188
|
* Deserializes a single value according to the type metadata
|
|
140
189
|
* @private
|
|
@@ -190,11 +239,28 @@ export declare class EntityUtils {
|
|
|
190
239
|
* @example
|
|
191
240
|
* ```typescript
|
|
192
241
|
* const user = EntityUtils.parse(User, data);
|
|
193
|
-
* const problems = EntityUtils.
|
|
242
|
+
* const problems = EntityUtils.getProblems(user);
|
|
194
243
|
* console.log(problems); // [Problem, ...]
|
|
195
244
|
* ```
|
|
196
245
|
*/
|
|
197
|
-
static
|
|
246
|
+
static getProblems<T extends object>(instance: T): Problem[];
|
|
247
|
+
/**
|
|
248
|
+
* Sets the validation problems for an entity instance
|
|
249
|
+
*
|
|
250
|
+
* @param instance - The entity instance
|
|
251
|
+
* @param problems - Array of Problems to associate with the instance
|
|
252
|
+
*
|
|
253
|
+
* @remarks
|
|
254
|
+
* - Overwrites any existing problems for the instance
|
|
255
|
+
* - Pass an empty array to clear problems
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* const user = new User({ name: 'John' });
|
|
260
|
+
* EntityUtils.setProblems(user, [new Problem({ property: 'name', message: 'Invalid name' })]);
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
static setProblems<T extends object>(instance: T, problems: Problem[]): void;
|
|
198
264
|
/**
|
|
199
265
|
* Gets the raw input data that was used to create an entity instance
|
|
200
266
|
*
|
|
@@ -213,6 +279,23 @@ export declare class EntityUtils {
|
|
|
213
279
|
* ```
|
|
214
280
|
*/
|
|
215
281
|
static getRawInput<T extends object>(instance: T): Record<string, unknown> | undefined;
|
|
282
|
+
/**
|
|
283
|
+
* Sets the raw input data for an entity instance
|
|
284
|
+
*
|
|
285
|
+
* @param instance - The entity instance
|
|
286
|
+
* @param rawInput - The raw input object to associate with the instance
|
|
287
|
+
*
|
|
288
|
+
* @remarks
|
|
289
|
+
* - Overwrites any existing raw input for the instance
|
|
290
|
+
* - Pass undefined to clear the raw input
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* const user = new User({ name: 'John' });
|
|
295
|
+
* EntityUtils.setRawInput(user, { name: 'John', age: 30 });
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
static setRawInput<T extends object>(instance: T, rawInput: Record<string, unknown> | undefined): void;
|
|
216
299
|
/**
|
|
217
300
|
* Gets all entity validator method names for an entity
|
|
218
301
|
* @private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,eAAe,EAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBvC,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM;IAmB5C,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAoChD,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,eAAe,GAAG,SAAS;IA8B9B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IA2B9C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,EAC1B,SAAS,EAAE,CAAC,EACZ,SAAS,EAAE,CAAC,GACX;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE;IAoC/D,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAaxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAmBnE;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAsD7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;WACU,KAAK,CAAC,CAAC,SAAS,MAAM,EACjC,WAAW,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,EACjC,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,CAAC,CAAC;IA8Gb;;;OAGG;mBACkB,gBAAgB;IAiErC;;;;OAIG;mBACkB,sBAAsB;IA0B3C;;;;OAIG;mBACkB,qBAAqB;IAkC1C;;;OAGG;mBACkB,qBAAqB;IAoD1C;;;;;;;;;;;;;;;;;OAiBG;WACU,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAkCxE;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"entity-utils.d.ts","sourceRoot":"","sources":["../../src/lib/entity-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,eAAe,EAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBvC,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM;IAmB5C,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAoChD,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,eAAe,GAAG,SAAS;IA8B9B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IA2B9C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,EAC1B,SAAS,EAAE,CAAC,EACZ,SAAS,EAAE,CAAC,GACX;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE;IAoC/D,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAaxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAmBnE;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAsD7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;WACU,KAAK,CAAC,CAAC,SAAS,MAAM,EACjC,WAAW,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,EACjC,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,CAAC,CAAC;IA8Gb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;WACU,SAAS,CAAC,CAAC,SAAS,MAAM,EACrC,WAAW,EAAE,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC,EACjC,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CACN;QACE,OAAO,EAAE,IAAI,CAAC;QACd,IAAI,EAAE,CAAC,CAAC;QACR,QAAQ,EAAE,OAAO,EAAE,CAAC;KACrB,GACD;QACE,OAAO,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,SAAS,CAAC;QAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;KACrB,CACJ;IAsBD;;;OAGG;mBACkB,gBAAgB;IAiErC;;;;OAIG;mBACkB,sBAAsB;IA0B3C;;;;OAIG;mBACkB,qBAAqB;IAkC1C;;;OAGG;mBACkB,qBAAqB;IAoD1C;;;;;;;;;;;;;;;;;OAiBG;WACU,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAkCxE;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,EAAE;IAI5D;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI;IAQ5E;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,MAAM,EACjC,QAAQ,EAAE,CAAC,GACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAItC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,MAAM,EACjC,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC5C,IAAI;IAQP;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;CA6BnC"}
|
package/dist/lib/entity-utils.js
CHANGED
|
@@ -280,7 +280,7 @@ export class EntityUtils {
|
|
|
280
280
|
* - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored
|
|
281
281
|
* - Property validators run first, then entity validators
|
|
282
282
|
* - Validators can be synchronous or asynchronous
|
|
283
|
-
* - Problems are accessible via EntityUtils.
|
|
283
|
+
* - Problems are accessible via EntityUtils.getProblems()
|
|
284
284
|
* - Raw input data is accessible via EntityUtils.getRawInput()
|
|
285
285
|
*
|
|
286
286
|
* @example
|
|
@@ -379,6 +379,63 @@ export class EntityUtils {
|
|
|
379
379
|
return instance;
|
|
380
380
|
}
|
|
381
381
|
/**
|
|
382
|
+
* Safely deserializes a plain object to an entity instance without throwing errors
|
|
383
|
+
*
|
|
384
|
+
* @param entityClass - The entity class constructor. Must accept a data object parameter.
|
|
385
|
+
* @param plainObject - The plain object to deserialize
|
|
386
|
+
* @param options - Parse options (strict mode)
|
|
387
|
+
* @returns Promise resolving to a result object with success flag, data, and problems
|
|
388
|
+
*
|
|
389
|
+
* @remarks
|
|
390
|
+
* Similar to parse() but returns a result object instead of throwing errors:
|
|
391
|
+
* - On success with strict: true - returns { success: true, data, problems: [] }
|
|
392
|
+
* - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)
|
|
393
|
+
* - On failure - returns { success: false, data: undefined, problems: [...] }
|
|
394
|
+
*
|
|
395
|
+
* All deserialization and validation rules from parse() apply.
|
|
396
|
+
* See parse() documentation for detailed deserialization behavior.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* @Entity()
|
|
401
|
+
* class User {
|
|
402
|
+
* @Property({ type: () => String }) name!: string;
|
|
403
|
+
* @Property({ type: () => Number }) age!: number;
|
|
404
|
+
*
|
|
405
|
+
* constructor(data: Partial<User>) {
|
|
406
|
+
* Object.assign(this, data);
|
|
407
|
+
* }
|
|
408
|
+
* }
|
|
409
|
+
*
|
|
410
|
+
* const result = await EntityUtils.safeParse(User, { name: 'John', age: 30 });
|
|
411
|
+
* if (result.success) {
|
|
412
|
+
* console.log(result.data); // User instance
|
|
413
|
+
* console.log(result.problems); // [] or soft problems if not strict
|
|
414
|
+
* } else {
|
|
415
|
+
* console.log(result.problems); // Hard problems
|
|
416
|
+
* }
|
|
417
|
+
* ```
|
|
418
|
+
*/ static async safeParse(entityClass, plainObject, options) {
|
|
419
|
+
try {
|
|
420
|
+
const data = await this.parse(entityClass, plainObject, options);
|
|
421
|
+
const problems = this.getProblems(data);
|
|
422
|
+
return {
|
|
423
|
+
success: true,
|
|
424
|
+
data,
|
|
425
|
+
problems
|
|
426
|
+
};
|
|
427
|
+
} catch (error) {
|
|
428
|
+
if (error instanceof ValidationError) {
|
|
429
|
+
return {
|
|
430
|
+
success: false,
|
|
431
|
+
data: undefined,
|
|
432
|
+
problems: error.problems
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
throw error;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
382
439
|
* Deserializes a single value according to the type metadata
|
|
383
440
|
* @private
|
|
384
441
|
*/ static async deserializeValue(value, options) {
|
|
@@ -566,13 +623,35 @@ export class EntityUtils {
|
|
|
566
623
|
* @example
|
|
567
624
|
* ```typescript
|
|
568
625
|
* const user = EntityUtils.parse(User, data);
|
|
569
|
-
* const problems = EntityUtils.
|
|
626
|
+
* const problems = EntityUtils.getProblems(user);
|
|
570
627
|
* console.log(problems); // [Problem, ...]
|
|
571
628
|
* ```
|
|
572
|
-
*/ static
|
|
629
|
+
*/ static getProblems(instance) {
|
|
573
630
|
return problemsStorage.get(instance) || [];
|
|
574
631
|
}
|
|
575
632
|
/**
|
|
633
|
+
* Sets the validation problems for an entity instance
|
|
634
|
+
*
|
|
635
|
+
* @param instance - The entity instance
|
|
636
|
+
* @param problems - Array of Problems to associate with the instance
|
|
637
|
+
*
|
|
638
|
+
* @remarks
|
|
639
|
+
* - Overwrites any existing problems for the instance
|
|
640
|
+
* - Pass an empty array to clear problems
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* ```typescript
|
|
644
|
+
* const user = new User({ name: 'John' });
|
|
645
|
+
* EntityUtils.setProblems(user, [new Problem({ property: 'name', message: 'Invalid name' })]);
|
|
646
|
+
* ```
|
|
647
|
+
*/ static setProblems(instance, problems) {
|
|
648
|
+
if (problems.length === 0) {
|
|
649
|
+
problemsStorage.delete(instance);
|
|
650
|
+
} else {
|
|
651
|
+
problemsStorage.set(instance, problems);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
576
655
|
* Gets the raw input data that was used to create an entity instance
|
|
577
656
|
*
|
|
578
657
|
* @param instance - The entity instance
|
|
@@ -592,6 +671,28 @@ export class EntityUtils {
|
|
|
592
671
|
return rawInputStorage.get(instance);
|
|
593
672
|
}
|
|
594
673
|
/**
|
|
674
|
+
* Sets the raw input data for an entity instance
|
|
675
|
+
*
|
|
676
|
+
* @param instance - The entity instance
|
|
677
|
+
* @param rawInput - The raw input object to associate with the instance
|
|
678
|
+
*
|
|
679
|
+
* @remarks
|
|
680
|
+
* - Overwrites any existing raw input for the instance
|
|
681
|
+
* - Pass undefined to clear the raw input
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```typescript
|
|
685
|
+
* const user = new User({ name: 'John' });
|
|
686
|
+
* EntityUtils.setRawInput(user, { name: 'John', age: 30 });
|
|
687
|
+
* ```
|
|
688
|
+
*/ static setRawInput(instance, rawInput) {
|
|
689
|
+
if (rawInput === undefined) {
|
|
690
|
+
rawInputStorage.delete(instance);
|
|
691
|
+
} else {
|
|
692
|
+
rawInputStorage.set(instance, rawInput);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
595
696
|
* Gets all entity validator method names for an entity
|
|
596
697
|
* @private
|
|
597
698
|
*/ static getEntityValidators(target) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/entity-utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n} from './types.js';\nimport { isEqualWith } from 'lodash-es';\nimport { ValidationError } from './validation-error.js';\nimport { Problem } from './problem.js';\nimport {\n prependPropertyPath,\n prependArrayIndex,\n createValidationError,\n combinePropertyPaths,\n} from './validation-utils.js';\nimport {\n isPrimitiveConstructor,\n deserializePrimitive,\n} from './primitive-deserializers.js';\nimport { ok } from 'assert';\n\n/**\n * WeakMap to store validation problems for entity instances\n */\nconst problemsStorage = new WeakMap<object, Problem[]>();\n\n/**\n * WeakMap to store raw input data for entity instances\n */\nconst rawInputStorage = new WeakMap<object, Record<string, unknown>>();\n\nexport class EntityUtils {\n /**\n * Checks if a given object is an instance of a class decorated with @Entity()\n * or if the provided value is an entity class itself\n *\n * @param obj - The object or class to check\n * @returns true if the object is an entity instance or entity class, false otherwise\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * const user = new User();\n * console.log(EntityUtils.isEntity(user)); // true\n * console.log(EntityUtils.isEntity(User)); // true\n * console.log(EntityUtils.isEntity({})); // false\n * ```\n */\n static isEntity(obj: unknown): obj is object {\n if (obj == null) {\n return false;\n }\n\n // Check if obj is a constructor function (class)\n if (typeof obj === 'function') {\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, obj);\n }\n\n // Check if obj is an object instance\n if (typeof obj !== 'object' || Array.isArray(obj)) {\n return false;\n }\n\n const constructor = Object.getPrototypeOf(obj).constructor;\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, constructor);\n }\n\n static sameEntity(a: object, b: object): boolean {\n if (!this.isEntity(a) || !this.isEntity(b)) {\n return false;\n }\n\n return Object.getPrototypeOf(a) === Object.getPrototypeOf(b);\n }\n\n static getPropertyKeys(target: object): string[] {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n const keys: string[] = [];\n const seen = new Set<string>();\n\n // Walk the prototype chain to collect all inherited properties\n while (currentProto && currentProto !== Object.prototype) {\n // Use getOwnMetadata to only get metadata directly on this prototype\n const protoKeys: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, currentProto) || [];\n\n for (const key of protoKeys) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return keys;\n }\n\n static getPropertyOptions(\n target: object,\n propertyKey: string,\n ): PropertyOptions | undefined {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n // Walk the prototype chain to find the property options\n while (currentProto && currentProto !== Object.prototype) {\n const protoOptions: Record<string, PropertyOptions> =\n Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, currentProto) ||\n {};\n\n if (protoOptions[propertyKey]) {\n return protoOptions[propertyKey];\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return undefined;\n }\n\n static equals(a: unknown, b: unknown): boolean {\n return isEqualWith(a, b, (val1, val2) => {\n if (this.isEntity(val1)) {\n if (!this.sameEntity(val1, val2)) {\n return false;\n }\n\n const diff = this.diff(val1, val2);\n\n return diff.length === 0;\n } else if (\n val1 != null &&\n val2 != null &&\n typeof val1 === 'object' &&\n !Array.isArray(val1) &&\n typeof val2 === 'object' &&\n !Array.isArray(val2) &&\n 'equals' in val1 &&\n typeof val1.equals === 'function'\n ) {\n return val1.equals(val2);\n }\n\n return undefined;\n });\n }\n\n static diff<T extends object>(\n oldEntity: T,\n newEntity: T,\n ): { property: string; oldValue: unknown; newValue: unknown }[] {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute diff');\n }\n\n const diffs: { property: string; oldValue: unknown; newValue: unknown }[] =\n [];\n\n const keys = this.getPropertyKeys(oldEntity);\n\n for (const key of keys) {\n const oldValue = (oldEntity as any)[key];\n const newValue = (newEntity as any)[key];\n\n // Check if there's a custom equals function for this property\n const propertyOptions = this.getPropertyOptions(oldEntity, key);\n\n let areEqual: boolean;\n if (oldValue == null && newValue == null) {\n areEqual = oldValue === newValue;\n } else if (oldValue == null || newValue == null) {\n areEqual = false;\n } else {\n areEqual = propertyOptions?.equals\n ? propertyOptions.equals(oldValue, newValue)\n : this.equals(oldValue, newValue);\n }\n\n if (!areEqual) {\n diffs.push({ property: key, oldValue, newValue });\n }\n }\n\n return diffs;\n }\n\n static changes<T extends object>(oldEntity: T, newEntity: T): Partial<T> {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute changes');\n }\n\n const diff = this.diff(oldEntity, newEntity);\n\n return diff.reduce((acc, { property, newValue }) => {\n (acc as any)[property] = newValue;\n return acc;\n }, {} as Partial<T>);\n }\n\n /**\n * Serializes an entity to a plain object, converting only properties decorated with @Property()\n *\n * @param entity - The entity instance to serialize\n * @returns A plain object containing only the serialized decorated properties\n *\n * @remarks\n * Serialization rules:\n * - Only properties decorated with @Property() are included\n * - If a property has a custom toJSON() method, it will be used\n * - Nested entities are recursively serialized using EntityUtils.toJSON()\n * - Arrays are mapped with toJSON() applied to each element\n * - Date objects are serialized to ISO strings\n * - bigint values are serialized to strings\n * - undefined values are excluded from the output\n * - null values are included in the output\n * - Circular references are not supported (will cause stack overflow)\n *\n * @example\n * ```typescript\n * @Entity()\n * class Address {\n * @Property() street: string;\n * @Property() city: string;\n * }\n *\n * @Entity()\n * class User {\n * @Property() name: string;\n * @Property() address: Address;\n * @Property() createdAt: Date;\n * undecorated: string; // Will not be serialized\n * }\n *\n * const user = new User();\n * user.name = 'John';\n * user.address = new Address();\n * user.address.street = '123 Main St';\n * user.address.city = 'Boston';\n * user.createdAt = new Date('2024-01-01');\n * user.undecorated = 'ignored';\n *\n * const json = EntityUtils.toJSON(user);\n * // {\n * // name: 'John',\n * // address: { street: '123 Main St', city: 'Boston' },\n * // createdAt: '2024-01-01T00:00:00.000Z'\n * // }\n * ```\n */\n static toJSON<T extends object>(entity: T): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const keys = this.getPropertyKeys(entity);\n\n for (const key of keys) {\n const value = (entity as any)[key];\n\n // Skip undefined values\n if (value === undefined) {\n continue;\n }\n\n const options = this.getPropertyOptions(entity, key);\n result[key] = this.serializeValue(value, options);\n }\n\n return result;\n }\n\n /**\n * Serializes a single value according to the toJSON rules\n * @private\n */\n private static serializeValue(\n value: unknown,\n options?: PropertyOptions,\n ): unknown {\n if (value === null) {\n return null;\n }\n\n if (value === undefined) {\n return undefined;\n }\n\n const passthrough = options?.passthrough === true;\n if (passthrough) {\n return value;\n }\n\n if (Array.isArray(value)) {\n if (options?.serialize) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return value.map((item) => options.serialize!(item as any));\n }\n return value.map((item) => this.serializeValue(item));\n }\n\n if (options?.serialize) {\n return options.serialize(value as any);\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (typeof value === 'bigint') {\n return value.toString();\n }\n\n if (this.isEntity(value)) {\n return this.toJSON(value);\n }\n\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n\n throw new Error(\n `Cannot serialize value of type '${typeof value}'. Use passthrough: true in @Property() to explicitly allow serialization of unknown types.`,\n );\n }\n\n /**\n * Deserializes a plain object to an entity instance\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param options - Parse options (strict mode)\n * @returns Promise resolving to a new instance of the entity with deserialized values\n *\n * @remarks\n * Deserialization rules:\n * - All @Property() decorators must include type metadata for parse() to work\n * - Properties without type metadata will throw an error\n * - Required properties (optional !== true) must be present and not null/undefined\n * - Optional properties (optional === true) can be undefined or null\n * - Arrays are supported with the array: true option\n * - Nested entities are recursively deserialized\n * - Type conversion is strict (no coercion)\n * - Entity constructors must accept a required data parameter\n *\n * Validation behavior:\n * - If strict: true - both HARD and SOFT problems throw ValidationError\n * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored\n * - Property validators run first, then entity validators\n * - Validators can be synchronous or asynchronous\n * - Problems are accessible via EntityUtils.problems()\n * - Raw input data is accessible via EntityUtils.getRawInput()\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const json = { name: 'John', age: 30 };\n * const user = await EntityUtils.parse(User, json);\n * const userStrict = await EntityUtils.parse(User, json, { strict: true });\n * ```\n */\n static async parse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options?: { strict?: boolean },\n ): Promise<T> {\n if (plainObject == null) {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n if (Array.isArray(plainObject)) {\n throw createValidationError(`Expects an object but received array`);\n }\n if (typeof plainObject !== 'object') {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n\n const strict = options?.strict ?? false;\n const keys = this.getPropertyKeys(entityClass.prototype);\n const data: Record<string, unknown> = {};\n const hardProblems: Problem[] = [];\n\n for (const key of keys) {\n const propertyOptions = this.getPropertyOptions(\n entityClass.prototype,\n key,\n );\n\n if (!propertyOptions) {\n hardProblems.push(\n new Problem({\n property: key,\n message: `Property has no metadata. This should not happen if @Property() was used correctly.`,\n }),\n );\n continue;\n }\n\n const value = (plainObject as Record<string, unknown>)[key];\n\n if (propertyOptions.passthrough === true) {\n data[key] = value;\n continue;\n }\n\n const isOptional = propertyOptions.optional === true;\n\n if (!(key in plainObject)) {\n if (!isOptional) {\n hardProblems.push(\n new Problem({\n property: key,\n message: 'Required property is missing from input',\n }),\n );\n }\n continue;\n }\n\n if (value === null || value === undefined) {\n if (!isOptional) {\n hardProblems.push(\n new Problem({\n property: key,\n message: 'Cannot be null or undefined',\n }),\n );\n }\n data[key] = value;\n continue;\n }\n\n try {\n data[key] = await this.deserializeValue(value, propertyOptions);\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependPropertyPath(key, error);\n hardProblems.push(...problems);\n } else if (error instanceof Error) {\n hardProblems.push(\n new Problem({\n property: key,\n message: error.message,\n }),\n );\n } else {\n throw error;\n }\n }\n }\n\n if (hardProblems.length > 0) {\n throw new ValidationError(hardProblems);\n }\n\n const instance = new entityClass(data);\n\n rawInputStorage.set(instance, plainObject as Record<string, unknown>);\n\n const problems = await this.validate(instance);\n\n if (problems.length > 0) {\n if (strict) {\n throw new ValidationError(problems);\n } else {\n problemsStorage.set(instance, problems);\n }\n }\n\n return instance;\n }\n\n /**\n * Deserializes a single value according to the type metadata\n * @private\n */\n private static async deserializeValue(\n value: unknown,\n options: PropertyOptions,\n ): Promise<unknown> {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n const isArray = options.array === true;\n const isSparse = options.sparse === true;\n\n if (isArray) {\n if (!Array.isArray(value)) {\n throw createValidationError(\n `Expects an array but received ${typeof value}`,\n );\n }\n\n const arrayProblems: Problem[] = [];\n const result: unknown[] = [];\n\n for (let index = 0; index < value.length; index++) {\n const item = value[index];\n if (item === null || item === undefined) {\n if (!isSparse) {\n arrayProblems.push(\n new Problem({\n property: `[${index}]`,\n message: 'Cannot be null or undefined.',\n }),\n );\n }\n result.push(item);\n } else {\n try {\n if (options.deserialize) {\n result.push(options.deserialize(item));\n } else {\n result.push(\n await this.deserializeSingleValue(item, typeConstructor),\n );\n }\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependArrayIndex(index, error);\n arrayProblems.push(...problems);\n } else {\n throw error;\n }\n }\n }\n }\n\n if (arrayProblems.length > 0) {\n throw new ValidationError(arrayProblems);\n }\n\n return result;\n }\n\n if (options.deserialize) {\n return options.deserialize(value);\n }\n\n return await this.deserializeSingleValue(value, typeConstructor);\n }\n\n /**\n * Deserializes a single non-array value\n * Reports validation errors with empty property (caller will prepend context)\n * @private\n */\n private static async deserializeSingleValue(\n value: unknown,\n typeConstructor: any,\n ): Promise<unknown> {\n if (isPrimitiveConstructor(typeConstructor)) {\n return deserializePrimitive(value, typeConstructor);\n }\n\n if (this.isEntity(typeConstructor)) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw createValidationError(\n `Expects an object but received ${typeof value}`,\n );\n }\n\n return await this.parse(\n typeConstructor as new (data: any) => object,\n value as Record<string, unknown>,\n );\n }\n\n throw createValidationError(\n `Has unknown type constructor. Supported types are: String, Number, Boolean, Date, BigInt, and @Entity() classes. Use passthrough: true to explicitly allow unknown types.`,\n );\n }\n\n /**\n * Validates a property value by running validators and nested entity validation.\n * Prepends the property path to all returned problems.\n * @private\n */\n private static async validatePropertyValue(\n propertyPath: string,\n value: unknown,\n validators: PropertyOptions['validators'],\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n\n if (validators) {\n for (const validator of validators) {\n const validatorProblems = await validator({ value });\n // Prepend propertyPath to all problems\n for (const problem of validatorProblems) {\n problems.push(\n new Problem({\n property: combinePropertyPaths(propertyPath, problem.property),\n message: problem.message,\n }),\n );\n }\n }\n }\n\n if (EntityUtils.isEntity(value)) {\n const nestedProblems = await EntityUtils.validate(value);\n const prependedProblems = prependPropertyPath(\n propertyPath,\n new ValidationError(nestedProblems),\n );\n problems.push(...prependedProblems);\n }\n\n return problems;\n }\n\n /**\n * Runs property validators for a given property value\n * @private\n */\n private static async runPropertyValidators(\n key: string,\n value: unknown,\n options: PropertyOptions,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const isArray = options?.array === true;\n const isPassthrough = options?.passthrough === true;\n\n if (isPassthrough || !isArray) {\n const valueProblems = await this.validatePropertyValue(\n key,\n value,\n options.validators,\n );\n problems.push(...valueProblems);\n } else {\n ok(Array.isArray(value), 'Value must be an array for array property');\n\n const arrayValidators = options.arrayValidators || [];\n for (const validator of arrayValidators) {\n const validatorProblems = await validator({ value });\n for (const problem of validatorProblems) {\n problems.push(\n new Problem({\n property: combinePropertyPaths(key, problem.property),\n message: problem.message,\n }),\n );\n }\n }\n\n const validators = options.validators || [];\n if (validators.length > 0) {\n for (let i = 0; i < value.length; i++) {\n const element = value[i];\n if (element !== null && element !== undefined) {\n const elementPath = `${key}[${i}]`;\n const elementProblems = await this.validatePropertyValue(\n elementPath,\n element,\n validators,\n );\n problems.push(...elementProblems);\n }\n }\n }\n }\n\n return problems;\n }\n\n /**\n * Validates an entity instance by running all property and entity validators\n *\n * @param instance - The entity instance to validate\n * @returns Promise resolving to array of Problems found during validation (empty if valid)\n *\n * @remarks\n * - Property validators run first, then entity validators\n * - Each validator can be synchronous or asynchronous\n * - Empty array means no problems found\n *\n * @example\n * ```typescript\n * const user = new User({ name: '', age: -5 });\n * const problems = await EntityUtils.validate(user);\n * console.log(problems); // [Problem, Problem, ...]\n * ```\n */\n static async validate<T extends object>(instance: T): Promise<Problem[]> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot validate non-entity instance');\n }\n\n const problems: Problem[] = [];\n\n const keys = this.getPropertyKeys(instance);\n for (const key of keys) {\n const options = this.getPropertyOptions(instance, key);\n if (options) {\n const value = (instance as any)[key];\n if (value != null) {\n const validationProblems = await this.runPropertyValidators(\n key,\n value,\n options,\n );\n problems.push(...validationProblems);\n }\n }\n }\n\n const entityValidators = this.getEntityValidators(instance);\n for (const validatorMethod of entityValidators) {\n const validatorProblems = await (instance as any)[validatorMethod]();\n if (Array.isArray(validatorProblems)) {\n problems.push(...validatorProblems);\n }\n }\n\n return problems;\n }\n\n /**\n * Gets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @returns Array of Problems (empty if no problems or instance not parsed)\n *\n * @remarks\n * - Only returns problems from the last parse() call\n * - Returns empty array if instance was not created via parse()\n * - Returns empty array if parse() was called with strict: true\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, data);\n * const problems = EntityUtils.problems(user);\n * console.log(problems); // [Problem, ...]\n * ```\n */\n static problems<T extends object>(instance: T): Problem[] {\n return problemsStorage.get(instance) || [];\n }\n\n /**\n * Gets the raw input data that was used to create an entity instance\n *\n * @param instance - The entity instance\n * @returns The raw input object, or undefined if not available\n *\n * @remarks\n * - Only available for instances created via parse()\n * - Returns a reference to the original input data (not a copy)\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, { name: 'John', age: 30 });\n * const rawInput = EntityUtils.getRawInput(user);\n * console.log(rawInput); // { name: 'John', age: 30 }\n * ```\n */\n static getRawInput<T extends object>(\n instance: T,\n ): Record<string, unknown> | undefined {\n return rawInputStorage.get(instance);\n }\n\n /**\n * Gets all entity validator method names for an entity\n * @private\n */\n private static getEntityValidators(target: object): string[] {\n let currentProto: any;\n\n if (target.constructor && target === target.constructor.prototype) {\n currentProto = target;\n } else {\n currentProto = Object.getPrototypeOf(target);\n }\n\n const validators: string[] = [];\n const seen = new Set<string>();\n\n while (currentProto && currentProto !== Object.prototype) {\n const protoValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, currentProto) ||\n [];\n\n for (const validator of protoValidators) {\n if (!seen.has(validator)) {\n seen.add(validator);\n validators.push(validator);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return validators;\n }\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","isEqualWith","ValidationError","Problem","prependPropertyPath","prependArrayIndex","createValidationError","combinePropertyPaths","isPrimitiveConstructor","deserializePrimitive","ok","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","sameEntity","a","b","getPropertyKeys","target","currentProto","prototype","keys","seen","Set","protoKeys","getOwnMetadata","key","has","add","push","getPropertyOptions","propertyKey","protoOptions","undefined","equals","val1","val2","diff","length","oldEntity","newEntity","Error","diffs","oldValue","newValue","propertyOptions","areEqual","property","changes","reduce","acc","toJSON","entity","result","value","options","serializeValue","passthrough","serialize","map","item","Date","toISOString","toString","parse","entityClass","plainObject","strict","data","hardProblems","message","isOptional","optional","deserializeValue","error","problems","instance","set","validate","typeConstructor","type","array","isSparse","sparse","arrayProblems","index","deserialize","deserializeSingleValue","validatePropertyValue","propertyPath","validators","validator","validatorProblems","problem","nestedProblems","prependedProblems","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","element","elementPath","elementProblems","validationProblems","entityValidators","getEntityValidators","validatorMethod","get","getRawInput","protoValidators"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/entity-utils.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n} from './types.js';\nimport { isEqualWith } from 'lodash-es';\nimport { ValidationError } from './validation-error.js';\nimport { Problem } from './problem.js';\nimport {\n prependPropertyPath,\n prependArrayIndex,\n createValidationError,\n combinePropertyPaths,\n} from './validation-utils.js';\nimport {\n isPrimitiveConstructor,\n deserializePrimitive,\n} from './primitive-deserializers.js';\nimport { ok } from 'assert';\n\n/**\n * WeakMap to store validation problems for entity instances\n */\nconst problemsStorage = new WeakMap<object, Problem[]>();\n\n/**\n * WeakMap to store raw input data for entity instances\n */\nconst rawInputStorage = new WeakMap<object, Record<string, unknown>>();\n\nexport class EntityUtils {\n /**\n * Checks if a given object is an instance of a class decorated with @Entity()\n * or if the provided value is an entity class itself\n *\n * @param obj - The object or class to check\n * @returns true if the object is an entity instance or entity class, false otherwise\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * const user = new User();\n * console.log(EntityUtils.isEntity(user)); // true\n * console.log(EntityUtils.isEntity(User)); // true\n * console.log(EntityUtils.isEntity({})); // false\n * ```\n */\n static isEntity(obj: unknown): obj is object {\n if (obj == null) {\n return false;\n }\n\n // Check if obj is a constructor function (class)\n if (typeof obj === 'function') {\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, obj);\n }\n\n // Check if obj is an object instance\n if (typeof obj !== 'object' || Array.isArray(obj)) {\n return false;\n }\n\n const constructor = Object.getPrototypeOf(obj).constructor;\n return Reflect.hasMetadata(ENTITY_METADATA_KEY, constructor);\n }\n\n static sameEntity(a: object, b: object): boolean {\n if (!this.isEntity(a) || !this.isEntity(b)) {\n return false;\n }\n\n return Object.getPrototypeOf(a) === Object.getPrototypeOf(b);\n }\n\n static getPropertyKeys(target: object): string[] {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n const keys: string[] = [];\n const seen = new Set<string>();\n\n // Walk the prototype chain to collect all inherited properties\n while (currentProto && currentProto !== Object.prototype) {\n // Use getOwnMetadata to only get metadata directly on this prototype\n const protoKeys: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, currentProto) || [];\n\n for (const key of protoKeys) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return keys;\n }\n\n static getPropertyOptions(\n target: object,\n propertyKey: string,\n ): PropertyOptions | undefined {\n // Determine if we're dealing with a prototype or an instance\n let currentProto: any;\n\n // Check if target is a prototype by checking if it has a constructor property\n // and if target === target.constructor.prototype\n if (target.constructor && target === target.constructor.prototype) {\n // target is already a prototype\n currentProto = target;\n } else {\n // target is an instance, get its prototype\n currentProto = Object.getPrototypeOf(target);\n }\n\n // Walk the prototype chain to find the property options\n while (currentProto && currentProto !== Object.prototype) {\n const protoOptions: Record<string, PropertyOptions> =\n Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, currentProto) ||\n {};\n\n if (protoOptions[propertyKey]) {\n return protoOptions[propertyKey];\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return undefined;\n }\n\n static equals(a: unknown, b: unknown): boolean {\n return isEqualWith(a, b, (val1, val2) => {\n if (this.isEntity(val1)) {\n if (!this.sameEntity(val1, val2)) {\n return false;\n }\n\n const diff = this.diff(val1, val2);\n\n return diff.length === 0;\n } else if (\n val1 != null &&\n val2 != null &&\n typeof val1 === 'object' &&\n !Array.isArray(val1) &&\n typeof val2 === 'object' &&\n !Array.isArray(val2) &&\n 'equals' in val1 &&\n typeof val1.equals === 'function'\n ) {\n return val1.equals(val2);\n }\n\n return undefined;\n });\n }\n\n static diff<T extends object>(\n oldEntity: T,\n newEntity: T,\n ): { property: string; oldValue: unknown; newValue: unknown }[] {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute diff');\n }\n\n const diffs: { property: string; oldValue: unknown; newValue: unknown }[] =\n [];\n\n const keys = this.getPropertyKeys(oldEntity);\n\n for (const key of keys) {\n const oldValue = (oldEntity as any)[key];\n const newValue = (newEntity as any)[key];\n\n // Check if there's a custom equals function for this property\n const propertyOptions = this.getPropertyOptions(oldEntity, key);\n\n let areEqual: boolean;\n if (oldValue == null && newValue == null) {\n areEqual = oldValue === newValue;\n } else if (oldValue == null || newValue == null) {\n areEqual = false;\n } else {\n areEqual = propertyOptions?.equals\n ? propertyOptions.equals(oldValue, newValue)\n : this.equals(oldValue, newValue);\n }\n\n if (!areEqual) {\n diffs.push({ property: key, oldValue, newValue });\n }\n }\n\n return diffs;\n }\n\n static changes<T extends object>(oldEntity: T, newEntity: T): Partial<T> {\n if (!this.sameEntity(oldEntity, newEntity)) {\n throw new Error('Entities must be of the same type to compute changes');\n }\n\n const diff = this.diff(oldEntity, newEntity);\n\n return diff.reduce((acc, { property, newValue }) => {\n (acc as any)[property] = newValue;\n return acc;\n }, {} as Partial<T>);\n }\n\n /**\n * Serializes an entity to a plain object, converting only properties decorated with @Property()\n *\n * @param entity - The entity instance to serialize\n * @returns A plain object containing only the serialized decorated properties\n *\n * @remarks\n * Serialization rules:\n * - Only properties decorated with @Property() are included\n * - If a property has a custom toJSON() method, it will be used\n * - Nested entities are recursively serialized using EntityUtils.toJSON()\n * - Arrays are mapped with toJSON() applied to each element\n * - Date objects are serialized to ISO strings\n * - bigint values are serialized to strings\n * - undefined values are excluded from the output\n * - null values are included in the output\n * - Circular references are not supported (will cause stack overflow)\n *\n * @example\n * ```typescript\n * @Entity()\n * class Address {\n * @Property() street: string;\n * @Property() city: string;\n * }\n *\n * @Entity()\n * class User {\n * @Property() name: string;\n * @Property() address: Address;\n * @Property() createdAt: Date;\n * undecorated: string; // Will not be serialized\n * }\n *\n * const user = new User();\n * user.name = 'John';\n * user.address = new Address();\n * user.address.street = '123 Main St';\n * user.address.city = 'Boston';\n * user.createdAt = new Date('2024-01-01');\n * user.undecorated = 'ignored';\n *\n * const json = EntityUtils.toJSON(user);\n * // {\n * // name: 'John',\n * // address: { street: '123 Main St', city: 'Boston' },\n * // createdAt: '2024-01-01T00:00:00.000Z'\n * // }\n * ```\n */\n static toJSON<T extends object>(entity: T): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n const keys = this.getPropertyKeys(entity);\n\n for (const key of keys) {\n const value = (entity as any)[key];\n\n // Skip undefined values\n if (value === undefined) {\n continue;\n }\n\n const options = this.getPropertyOptions(entity, key);\n result[key] = this.serializeValue(value, options);\n }\n\n return result;\n }\n\n /**\n * Serializes a single value according to the toJSON rules\n * @private\n */\n private static serializeValue(\n value: unknown,\n options?: PropertyOptions,\n ): unknown {\n if (value === null) {\n return null;\n }\n\n if (value === undefined) {\n return undefined;\n }\n\n const passthrough = options?.passthrough === true;\n if (passthrough) {\n return value;\n }\n\n if (Array.isArray(value)) {\n if (options?.serialize) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return value.map((item) => options.serialize!(item as any));\n }\n return value.map((item) => this.serializeValue(item));\n }\n\n if (options?.serialize) {\n return options.serialize(value as any);\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (typeof value === 'bigint') {\n return value.toString();\n }\n\n if (this.isEntity(value)) {\n return this.toJSON(value);\n }\n\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n\n throw new Error(\n `Cannot serialize value of type '${typeof value}'. Use passthrough: true in @Property() to explicitly allow serialization of unknown types.`,\n );\n }\n\n /**\n * Deserializes a plain object to an entity instance\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param options - Parse options (strict mode)\n * @returns Promise resolving to a new instance of the entity with deserialized values\n *\n * @remarks\n * Deserialization rules:\n * - All @Property() decorators must include type metadata for parse() to work\n * - Properties without type metadata will throw an error\n * - Required properties (optional !== true) must be present and not null/undefined\n * - Optional properties (optional === true) can be undefined or null\n * - Arrays are supported with the array: true option\n * - Nested entities are recursively deserialized\n * - Type conversion is strict (no coercion)\n * - Entity constructors must accept a required data parameter\n *\n * Validation behavior:\n * - If strict: true - both HARD and SOFT problems throw ValidationError\n * - If strict: false (default) - HARD problems throw ValidationError, SOFT problems stored\n * - Property validators run first, then entity validators\n * - Validators can be synchronous or asynchronous\n * - Problems are accessible via EntityUtils.getProblems()\n * - Raw input data is accessible via EntityUtils.getRawInput()\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const json = { name: 'John', age: 30 };\n * const user = await EntityUtils.parse(User, json);\n * const userStrict = await EntityUtils.parse(User, json, { strict: true });\n * ```\n */\n static async parse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options?: { strict?: boolean },\n ): Promise<T> {\n if (plainObject == null) {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n if (Array.isArray(plainObject)) {\n throw createValidationError(`Expects an object but received array`);\n }\n if (typeof plainObject !== 'object') {\n throw createValidationError(\n `Expects an object but received ${typeof plainObject}`,\n );\n }\n\n const strict = options?.strict ?? false;\n const keys = this.getPropertyKeys(entityClass.prototype);\n const data: Record<string, unknown> = {};\n const hardProblems: Problem[] = [];\n\n for (const key of keys) {\n const propertyOptions = this.getPropertyOptions(\n entityClass.prototype,\n key,\n );\n\n if (!propertyOptions) {\n hardProblems.push(\n new Problem({\n property: key,\n message: `Property has no metadata. This should not happen if @Property() was used correctly.`,\n }),\n );\n continue;\n }\n\n const value = (plainObject as Record<string, unknown>)[key];\n\n if (propertyOptions.passthrough === true) {\n data[key] = value;\n continue;\n }\n\n const isOptional = propertyOptions.optional === true;\n\n if (!(key in plainObject)) {\n if (!isOptional) {\n hardProblems.push(\n new Problem({\n property: key,\n message: 'Required property is missing from input',\n }),\n );\n }\n continue;\n }\n\n if (value === null || value === undefined) {\n if (!isOptional) {\n hardProblems.push(\n new Problem({\n property: key,\n message: 'Cannot be null or undefined',\n }),\n );\n }\n data[key] = value;\n continue;\n }\n\n try {\n data[key] = await this.deserializeValue(value, propertyOptions);\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependPropertyPath(key, error);\n hardProblems.push(...problems);\n } else if (error instanceof Error) {\n hardProblems.push(\n new Problem({\n property: key,\n message: error.message,\n }),\n );\n } else {\n throw error;\n }\n }\n }\n\n if (hardProblems.length > 0) {\n throw new ValidationError(hardProblems);\n }\n\n const instance = new entityClass(data);\n\n rawInputStorage.set(instance, plainObject as Record<string, unknown>);\n\n const problems = await this.validate(instance);\n\n if (problems.length > 0) {\n if (strict) {\n throw new ValidationError(problems);\n } else {\n problemsStorage.set(instance, problems);\n }\n }\n\n return instance;\n }\n\n /**\n * Safely deserializes a plain object to an entity instance without throwing errors\n *\n * @param entityClass - The entity class constructor. Must accept a data object parameter.\n * @param plainObject - The plain object to deserialize\n * @param options - Parse options (strict mode)\n * @returns Promise resolving to a result object with success flag, data, and problems\n *\n * @remarks\n * Similar to parse() but returns a result object instead of throwing errors:\n * - On success with strict: true - returns { success: true, data, problems: [] }\n * - On success with strict: false - returns { success: true, data, problems: [...] } (may include soft problems)\n * - On failure - returns { success: false, data: undefined, problems: [...] }\n *\n * All deserialization and validation rules from parse() apply.\n * See parse() documentation for detailed deserialization behavior.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) name!: string;\n * @Property({ type: () => Number }) age!: number;\n *\n * constructor(data: Partial<User>) {\n * Object.assign(this, data);\n * }\n * }\n *\n * const result = await EntityUtils.safeParse(User, { name: 'John', age: 30 });\n * if (result.success) {\n * console.log(result.data); // User instance\n * console.log(result.problems); // [] or soft problems if not strict\n * } else {\n * console.log(result.problems); // Hard problems\n * }\n * ```\n */\n static async safeParse<T extends object>(\n entityClass: new (data: any) => T,\n plainObject: unknown,\n options?: { strict?: boolean },\n ): Promise<\n | {\n success: true;\n data: T;\n problems: Problem[];\n }\n | {\n success: false;\n data: undefined;\n problems: Problem[];\n }\n > {\n try {\n const data = await this.parse(entityClass, plainObject, options);\n const problems = this.getProblems(data);\n\n return {\n success: true,\n data,\n problems,\n };\n } catch (error) {\n if (error instanceof ValidationError) {\n return {\n success: false,\n data: undefined,\n problems: error.problems,\n };\n }\n throw error;\n }\n }\n\n /**\n * Deserializes a single value according to the type metadata\n * @private\n */\n private static async deserializeValue(\n value: unknown,\n options: PropertyOptions,\n ): Promise<unknown> {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const typeConstructor = options.type!();\n const isArray = options.array === true;\n const isSparse = options.sparse === true;\n\n if (isArray) {\n if (!Array.isArray(value)) {\n throw createValidationError(\n `Expects an array but received ${typeof value}`,\n );\n }\n\n const arrayProblems: Problem[] = [];\n const result: unknown[] = [];\n\n for (let index = 0; index < value.length; index++) {\n const item = value[index];\n if (item === null || item === undefined) {\n if (!isSparse) {\n arrayProblems.push(\n new Problem({\n property: `[${index}]`,\n message: 'Cannot be null or undefined.',\n }),\n );\n }\n result.push(item);\n } else {\n try {\n if (options.deserialize) {\n result.push(options.deserialize(item));\n } else {\n result.push(\n await this.deserializeSingleValue(item, typeConstructor),\n );\n }\n } catch (error) {\n if (error instanceof ValidationError) {\n const problems = prependArrayIndex(index, error);\n arrayProblems.push(...problems);\n } else {\n throw error;\n }\n }\n }\n }\n\n if (arrayProblems.length > 0) {\n throw new ValidationError(arrayProblems);\n }\n\n return result;\n }\n\n if (options.deserialize) {\n return options.deserialize(value);\n }\n\n return await this.deserializeSingleValue(value, typeConstructor);\n }\n\n /**\n * Deserializes a single non-array value\n * Reports validation errors with empty property (caller will prepend context)\n * @private\n */\n private static async deserializeSingleValue(\n value: unknown,\n typeConstructor: any,\n ): Promise<unknown> {\n if (isPrimitiveConstructor(typeConstructor)) {\n return deserializePrimitive(value, typeConstructor);\n }\n\n if (this.isEntity(typeConstructor)) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw createValidationError(\n `Expects an object but received ${typeof value}`,\n );\n }\n\n return await this.parse(\n typeConstructor as new (data: any) => object,\n value as Record<string, unknown>,\n );\n }\n\n throw createValidationError(\n `Has unknown type constructor. Supported types are: String, Number, Boolean, Date, BigInt, and @Entity() classes. Use passthrough: true to explicitly allow unknown types.`,\n );\n }\n\n /**\n * Validates a property value by running validators and nested entity validation.\n * Prepends the property path to all returned problems.\n * @private\n */\n private static async validatePropertyValue(\n propertyPath: string,\n value: unknown,\n validators: PropertyOptions['validators'],\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n\n if (validators) {\n for (const validator of validators) {\n const validatorProblems = await validator({ value });\n // Prepend propertyPath to all problems\n for (const problem of validatorProblems) {\n problems.push(\n new Problem({\n property: combinePropertyPaths(propertyPath, problem.property),\n message: problem.message,\n }),\n );\n }\n }\n }\n\n if (EntityUtils.isEntity(value)) {\n const nestedProblems = await EntityUtils.validate(value);\n const prependedProblems = prependPropertyPath(\n propertyPath,\n new ValidationError(nestedProblems),\n );\n problems.push(...prependedProblems);\n }\n\n return problems;\n }\n\n /**\n * Runs property validators for a given property value\n * @private\n */\n private static async runPropertyValidators(\n key: string,\n value: unknown,\n options: PropertyOptions,\n ): Promise<Problem[]> {\n const problems: Problem[] = [];\n const isArray = options?.array === true;\n const isPassthrough = options?.passthrough === true;\n\n if (isPassthrough || !isArray) {\n const valueProblems = await this.validatePropertyValue(\n key,\n value,\n options.validators,\n );\n problems.push(...valueProblems);\n } else {\n ok(Array.isArray(value), 'Value must be an array for array property');\n\n const arrayValidators = options.arrayValidators || [];\n for (const validator of arrayValidators) {\n const validatorProblems = await validator({ value });\n for (const problem of validatorProblems) {\n problems.push(\n new Problem({\n property: combinePropertyPaths(key, problem.property),\n message: problem.message,\n }),\n );\n }\n }\n\n const validators = options.validators || [];\n if (validators.length > 0) {\n for (let i = 0; i < value.length; i++) {\n const element = value[i];\n if (element !== null && element !== undefined) {\n const elementPath = `${key}[${i}]`;\n const elementProblems = await this.validatePropertyValue(\n elementPath,\n element,\n validators,\n );\n problems.push(...elementProblems);\n }\n }\n }\n }\n\n return problems;\n }\n\n /**\n * Validates an entity instance by running all property and entity validators\n *\n * @param instance - The entity instance to validate\n * @returns Promise resolving to array of Problems found during validation (empty if valid)\n *\n * @remarks\n * - Property validators run first, then entity validators\n * - Each validator can be synchronous or asynchronous\n * - Empty array means no problems found\n *\n * @example\n * ```typescript\n * const user = new User({ name: '', age: -5 });\n * const problems = await EntityUtils.validate(user);\n * console.log(problems); // [Problem, Problem, ...]\n * ```\n */\n static async validate<T extends object>(instance: T): Promise<Problem[]> {\n if (!this.isEntity(instance)) {\n throw new Error('Cannot validate non-entity instance');\n }\n\n const problems: Problem[] = [];\n\n const keys = this.getPropertyKeys(instance);\n for (const key of keys) {\n const options = this.getPropertyOptions(instance, key);\n if (options) {\n const value = (instance as any)[key];\n if (value != null) {\n const validationProblems = await this.runPropertyValidators(\n key,\n value,\n options,\n );\n problems.push(...validationProblems);\n }\n }\n }\n\n const entityValidators = this.getEntityValidators(instance);\n for (const validatorMethod of entityValidators) {\n const validatorProblems = await (instance as any)[validatorMethod]();\n if (Array.isArray(validatorProblems)) {\n problems.push(...validatorProblems);\n }\n }\n\n return problems;\n }\n\n /**\n * Gets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @returns Array of Problems (empty if no problems or instance not parsed)\n *\n * @remarks\n * - Only returns problems from the last parse() call\n * - Returns empty array if instance was not created via parse()\n * - Returns empty array if parse() was called with strict: true\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, data);\n * const problems = EntityUtils.getProblems(user);\n * console.log(problems); // [Problem, ...]\n * ```\n */\n static getProblems<T extends object>(instance: T): Problem[] {\n return problemsStorage.get(instance) || [];\n }\n\n /**\n * Sets the validation problems for an entity instance\n *\n * @param instance - The entity instance\n * @param problems - Array of Problems to associate with the instance\n *\n * @remarks\n * - Overwrites any existing problems for the instance\n * - Pass an empty array to clear problems\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setProblems(user, [new Problem({ property: 'name', message: 'Invalid name' })]);\n * ```\n */\n static setProblems<T extends object>(instance: T, problems: Problem[]): void {\n if (problems.length === 0) {\n problemsStorage.delete(instance);\n } else {\n problemsStorage.set(instance, problems);\n }\n }\n\n /**\n * Gets the raw input data that was used to create an entity instance\n *\n * @param instance - The entity instance\n * @returns The raw input object, or undefined if not available\n *\n * @remarks\n * - Only available for instances created via parse()\n * - Returns a reference to the original input data (not a copy)\n *\n * @example\n * ```typescript\n * const user = EntityUtils.parse(User, { name: 'John', age: 30 });\n * const rawInput = EntityUtils.getRawInput(user);\n * console.log(rawInput); // { name: 'John', age: 30 }\n * ```\n */\n static getRawInput<T extends object>(\n instance: T,\n ): Record<string, unknown> | undefined {\n return rawInputStorage.get(instance);\n }\n\n /**\n * Sets the raw input data for an entity instance\n *\n * @param instance - The entity instance\n * @param rawInput - The raw input object to associate with the instance\n *\n * @remarks\n * - Overwrites any existing raw input for the instance\n * - Pass undefined to clear the raw input\n *\n * @example\n * ```typescript\n * const user = new User({ name: 'John' });\n * EntityUtils.setRawInput(user, { name: 'John', age: 30 });\n * ```\n */\n static setRawInput<T extends object>(\n instance: T,\n rawInput: Record<string, unknown> | undefined,\n ): void {\n if (rawInput === undefined) {\n rawInputStorage.delete(instance);\n } else {\n rawInputStorage.set(instance, rawInput);\n }\n }\n\n /**\n * Gets all entity validator method names for an entity\n * @private\n */\n private static getEntityValidators(target: object): string[] {\n let currentProto: any;\n\n if (target.constructor && target === target.constructor.prototype) {\n currentProto = target;\n } else {\n currentProto = Object.getPrototypeOf(target);\n }\n\n const validators: string[] = [];\n const seen = new Set<string>();\n\n while (currentProto && currentProto !== Object.prototype) {\n const protoValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, currentProto) ||\n [];\n\n for (const validator of protoValidators) {\n if (!seen.has(validator)) {\n seen.add(validator);\n validators.push(validator);\n }\n }\n\n currentProto = Object.getPrototypeOf(currentProto);\n }\n\n return validators;\n }\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","isEqualWith","ValidationError","Problem","prependPropertyPath","prependArrayIndex","createValidationError","combinePropertyPaths","isPrimitiveConstructor","deserializePrimitive","ok","problemsStorage","WeakMap","rawInputStorage","EntityUtils","isEntity","obj","Reflect","hasMetadata","Array","isArray","constructor","Object","getPrototypeOf","sameEntity","a","b","getPropertyKeys","target","currentProto","prototype","keys","seen","Set","protoKeys","getOwnMetadata","key","has","add","push","getPropertyOptions","propertyKey","protoOptions","undefined","equals","val1","val2","diff","length","oldEntity","newEntity","Error","diffs","oldValue","newValue","propertyOptions","areEqual","property","changes","reduce","acc","toJSON","entity","result","value","options","serializeValue","passthrough","serialize","map","item","Date","toISOString","toString","parse","entityClass","plainObject","strict","data","hardProblems","message","isOptional","optional","deserializeValue","error","problems","instance","set","validate","safeParse","getProblems","success","typeConstructor","type","array","isSparse","sparse","arrayProblems","index","deserialize","deserializeSingleValue","validatePropertyValue","propertyPath","validators","validator","validatorProblems","problem","nestedProblems","prependedProblems","runPropertyValidators","isPassthrough","valueProblems","arrayValidators","i","element","elementPath","elementProblems","validationProblems","entityValidators","getEntityValidators","validatorMethod","get","setProblems","delete","getRawInput","setRawInput","rawInput","protoValidators"],"mappings":"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"}
|
package/dist/lib/property.d.ts
CHANGED
|
@@ -27,9 +27,20 @@ export declare function Property<T, C extends CtorLike<T>>(options: PropertyOpti
|
|
|
27
27
|
*
|
|
28
28
|
* @StringProperty({ optional: true })
|
|
29
29
|
* nickname?: string;
|
|
30
|
+
*
|
|
31
|
+
* @StringProperty({ minLength: 3, maxLength: 50 })
|
|
32
|
+
* username!: string;
|
|
33
|
+
*
|
|
34
|
+
* @StringProperty({ pattern: /^[a-z]+$/ })
|
|
35
|
+
* slug!: string;
|
|
30
36
|
* }
|
|
31
37
|
*/
|
|
32
|
-
export declare function StringProperty(options?: Omit<PropertyOptions<string, StringConstructor>, 'type'>
|
|
38
|
+
export declare function StringProperty(options?: Omit<PropertyOptions<string, StringConstructor>, 'type'> & {
|
|
39
|
+
minLength?: number;
|
|
40
|
+
maxLength?: number;
|
|
41
|
+
pattern?: RegExp;
|
|
42
|
+
patternMessage?: string;
|
|
43
|
+
}): PropertyDecorator;
|
|
33
44
|
/**
|
|
34
45
|
* Helper decorator for enum properties (string enums)
|
|
35
46
|
* Validates that the string value matches one of the enum values
|
|
@@ -59,9 +70,15 @@ export declare function EnumProperty<T extends Record<string, string>>(enumType:
|
|
|
59
70
|
*
|
|
60
71
|
* @NumberProperty({ optional: true })
|
|
61
72
|
* score?: number;
|
|
73
|
+
*
|
|
74
|
+
* @NumberProperty({ min: 0, max: 100 })
|
|
75
|
+
* percentage!: number;
|
|
62
76
|
* }
|
|
63
77
|
*/
|
|
64
|
-
export declare function NumberProperty(options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>
|
|
78
|
+
export declare function NumberProperty(options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'> & {
|
|
79
|
+
min?: number;
|
|
80
|
+
max?: number;
|
|
81
|
+
}): PropertyDecorator;
|
|
65
82
|
/**
|
|
66
83
|
* Helper decorator for integer properties
|
|
67
84
|
* Validates that the number is an integer (no decimal places)
|
|
@@ -140,9 +157,15 @@ export declare function EntityProperty<T, C extends AnyCtor<T> & {
|
|
|
140
157
|
*
|
|
141
158
|
* @ArrayProperty(() => String, { sparse: true })
|
|
142
159
|
* sparseList!: (string | null)[];
|
|
160
|
+
*
|
|
161
|
+
* @ArrayProperty(() => String, { minLength: 1, maxLength: 10 })
|
|
162
|
+
* limitedList!: string[];
|
|
143
163
|
* }
|
|
144
164
|
*/
|
|
145
|
-
export declare function ArrayProperty<T, C extends CtorLike<T>>(type: () => C, options?: Omit<PropertyOptions<T, C>, 'type' | 'array'>
|
|
165
|
+
export declare function ArrayProperty<T, C extends CtorLike<T>>(type: () => C, options?: Omit<PropertyOptions<T, C>, 'type' | 'array'> & {
|
|
166
|
+
minLength?: number;
|
|
167
|
+
maxLength?: number;
|
|
168
|
+
}): PropertyDecorator;
|
|
146
169
|
/**
|
|
147
170
|
* Helper decorator for passthrough properties that bypass type validation.
|
|
148
171
|
* Use this for generic types like Record<string, unknown>, any, or custom objects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property.d.ts","sourceRoot":"","sources":["../../src/lib/property.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,OAAO,EACP,KAAK,QAAQ,EAIb,eAAe,EAChB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"property.d.ts","sourceRoot":"","sources":["../../src/lib/property.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/property.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-wrapper-object-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { isEqual } from 'lodash-es';
|
|
2
2
|
import { PROPERTY_METADATA_KEY, PROPERTY_OPTIONS_METADATA_KEY } from './types.js';
|
|
3
|
-
import { enumValidator, intValidator } from './validators.js';
|
|
3
|
+
import { enumValidator, intValidator, minLengthValidator, maxLengthValidator, patternValidator, minValidator, maxValidator, arrayMinLengthValidator, arrayMaxLengthValidator } from './validators.js';
|
|
4
4
|
/**
|
|
5
5
|
* Property decorator that marks class properties with metadata.
|
|
6
6
|
* This decorator can be used to identify and track properties within classes.
|
|
@@ -68,11 +68,32 @@ import { enumValidator, intValidator } from './validators.js';
|
|
|
68
68
|
*
|
|
69
69
|
* @StringProperty({ optional: true })
|
|
70
70
|
* nickname?: string;
|
|
71
|
+
*
|
|
72
|
+
* @StringProperty({ minLength: 3, maxLength: 50 })
|
|
73
|
+
* username!: string;
|
|
74
|
+
*
|
|
75
|
+
* @StringProperty({ pattern: /^[a-z]+$/ })
|
|
76
|
+
* slug!: string;
|
|
71
77
|
* }
|
|
72
78
|
*/ export function StringProperty(options) {
|
|
79
|
+
const validators = [
|
|
80
|
+
...options?.validators || []
|
|
81
|
+
];
|
|
82
|
+
if (options?.minLength !== undefined) {
|
|
83
|
+
validators.unshift(minLengthValidator(options.minLength));
|
|
84
|
+
}
|
|
85
|
+
if (options?.maxLength !== undefined) {
|
|
86
|
+
validators.unshift(maxLengthValidator(options.maxLength));
|
|
87
|
+
}
|
|
88
|
+
if (options?.pattern !== undefined) {
|
|
89
|
+
validators.unshift(patternValidator(options.pattern, options.patternMessage));
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
92
|
+
const { minLength, maxLength, pattern, patternMessage, ...restOptions } = options || {};
|
|
73
93
|
return Property({
|
|
74
|
-
...
|
|
75
|
-
type: ()=>String
|
|
94
|
+
...restOptions,
|
|
95
|
+
type: ()=>String,
|
|
96
|
+
validators: validators.length > 0 ? validators : undefined
|
|
76
97
|
});
|
|
77
98
|
}
|
|
78
99
|
/**
|
|
@@ -115,11 +136,26 @@ import { enumValidator, intValidator } from './validators.js';
|
|
|
115
136
|
*
|
|
116
137
|
* @NumberProperty({ optional: true })
|
|
117
138
|
* score?: number;
|
|
139
|
+
*
|
|
140
|
+
* @NumberProperty({ min: 0, max: 100 })
|
|
141
|
+
* percentage!: number;
|
|
118
142
|
* }
|
|
119
143
|
*/ export function NumberProperty(options) {
|
|
144
|
+
const validators = [
|
|
145
|
+
...options?.validators || []
|
|
146
|
+
];
|
|
147
|
+
if (options?.min !== undefined) {
|
|
148
|
+
validators.unshift(minValidator(options.min));
|
|
149
|
+
}
|
|
150
|
+
if (options?.max !== undefined) {
|
|
151
|
+
validators.unshift(maxValidator(options.max));
|
|
152
|
+
}
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
154
|
+
const { min, max, ...restOptions } = options || {};
|
|
120
155
|
return Property({
|
|
121
|
-
...
|
|
122
|
-
type: ()=>Number
|
|
156
|
+
...restOptions,
|
|
157
|
+
type: ()=>Number,
|
|
158
|
+
validators: validators.length > 0 ? validators : undefined
|
|
123
159
|
});
|
|
124
160
|
}
|
|
125
161
|
/**
|
|
@@ -225,12 +261,27 @@ import { enumValidator, intValidator } from './validators.js';
|
|
|
225
261
|
*
|
|
226
262
|
* @ArrayProperty(() => String, { sparse: true })
|
|
227
263
|
* sparseList!: (string | null)[];
|
|
264
|
+
*
|
|
265
|
+
* @ArrayProperty(() => String, { minLength: 1, maxLength: 10 })
|
|
266
|
+
* limitedList!: string[];
|
|
228
267
|
* }
|
|
229
268
|
*/ export function ArrayProperty(type, options) {
|
|
269
|
+
const arrayValidators = [
|
|
270
|
+
...options?.arrayValidators || []
|
|
271
|
+
];
|
|
272
|
+
if (options?.minLength !== undefined) {
|
|
273
|
+
arrayValidators.unshift(arrayMinLengthValidator(options.minLength));
|
|
274
|
+
}
|
|
275
|
+
if (options?.maxLength !== undefined) {
|
|
276
|
+
arrayValidators.unshift(arrayMaxLengthValidator(options.maxLength));
|
|
277
|
+
}
|
|
278
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
279
|
+
const { minLength, maxLength, ...restOptions } = options || {};
|
|
230
280
|
return Property({
|
|
231
|
-
...
|
|
281
|
+
...restOptions,
|
|
232
282
|
type,
|
|
233
|
-
array: true
|
|
283
|
+
array: true,
|
|
284
|
+
arrayValidators: arrayValidators.length > 0 ? arrayValidators : undefined
|
|
234
285
|
});
|
|
235
286
|
}
|
|
236
287
|
/**
|
package/dist/lib/property.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/property.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-wrapper-object-types */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isEqual } from 'lodash-es';\nimport {\n AnyCtor,\n type CtorLike,\n type InstanceOfCtorLike,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n} from './types.js';\nimport { enumValidator, intValidator } from './validators.js';\n\n/**\n * Property decorator that marks class properties with metadata.\n * This decorator can be used to identify and track properties within classes.\n *\n * @param options - Configuration for the property (type is required)\n *\n * @example\n * class User {\n * @Property({ type: () => String })\n * name: string;\n *\n * @Property({ type: () => String, equals: (a, b) => a.toLowerCase() === b.toLowerCase() })\n * email: string;\n *\n * @Property({ type: () => Number })\n * age: number;\n * }\n */\nexport function Property<T, C extends CtorLike<T>>(\n options: PropertyOptions<T, C>,\n): PropertyDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingProperties: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, target) || [];\n\n if (!existingProperties.includes(propertyKey)) {\n existingProperties.push(propertyKey);\n }\n\n Reflect.defineMetadata(PROPERTY_METADATA_KEY, existingProperties, target);\n\n if (options.passthrough === true) {\n if (options.array === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and array: true. Passthrough cannot be combined with array.`,\n );\n }\n if (options.optional === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and optional: true. Passthrough cannot be combined with optional.`,\n );\n }\n if (options.sparse === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and sparse: true. Passthrough cannot be combined with sparse.`,\n );\n }\n if (\n options.serialize !== undefined ||\n options.deserialize !== undefined\n ) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and custom serialize/deserialize functions. Passthrough cannot be combined with serialize or deserialize.`,\n );\n }\n }\n\n if (options.sparse === true && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has sparse: true but array is not true. The sparse option only applies to arrays.`,\n );\n }\n\n if (options.arrayValidators && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has arrayValidators defined but array is not true. The arrayValidators option only applies to arrays.`,\n );\n }\n\n // Validate serialize/deserialize pairing\n const hasSerialize = options.serialize !== undefined;\n const hasDeserialize = options.deserialize !== undefined;\n if (hasSerialize !== hasDeserialize) {\n throw new Error(\n `Property '${propertyKey}' must define both serialize and deserialize functions, or neither. Found only ${hasSerialize ? 'serialize' : 'deserialize'}.`,\n );\n }\n\n const existingOptions: Record<\n string,\n PropertyOptions<any, any>\n > = Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, target) || {};\n\n existingOptions[propertyKey] = options;\n\n Reflect.defineMetadata(\n PROPERTY_OPTIONS_METADATA_KEY,\n existingOptions,\n target,\n );\n };\n}\n\n/**\n * Helper decorator for string properties\n * @example\n * class User {\n * @StringProperty()\n * name!: string;\n *\n * @StringProperty({ optional: true })\n * nickname?: string;\n * }\n */\nexport function StringProperty(\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => String });\n}\n\n/**\n * Helper decorator for enum properties (string enums)\n * Validates that the string value matches one of the enum values\n * @param enumType - The enum object (e.g., MyEnum)\n * @param options - Additional property options\n * @example\n * enum Status {\n * Active = 'active',\n * Inactive = 'inactive'\n * }\n *\n * class User {\n * @EnumProperty(Status)\n * status!: Status;\n *\n * @EnumProperty(Status, { optional: true })\n * previousStatus?: Status;\n * }\n */\nexport function EnumProperty<T extends Record<string, string>>(\n enumType: T,\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [enumValidator(enumType), ...options.validators]\n : [enumValidator(enumType)];\n\n return Property({ ...options, type: () => String, validators });\n}\n\n/**\n * Helper decorator for number properties\n * @example\n * class User {\n * @NumberProperty()\n * age!: number;\n *\n * @NumberProperty({ optional: true })\n * score?: number;\n * }\n */\nexport function NumberProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Number });\n}\n\n/**\n * Helper decorator for integer properties\n * Validates that the number is an integer (no decimal places)\n * @example\n * class User {\n * @IntProperty()\n * age!: number;\n *\n * @IntProperty({ optional: true })\n * count?: number;\n * }\n */\nexport function IntProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [intValidator(), ...options.validators]\n : [intValidator()];\n\n return Property({ ...options, type: () => Number, validators });\n}\n\n/**\n * Helper decorator for boolean properties\n * @example\n * class User {\n * @BooleanProperty()\n * active!: boolean;\n *\n * @BooleanProperty({ optional: true })\n * verified?: boolean;\n * }\n */\nexport function BooleanProperty(\n options?: Omit<PropertyOptions<boolean, BooleanConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Boolean });\n}\n\n/**\n * Helper decorator for Date properties\n * @example\n * class User {\n * @DateProperty()\n * createdAt!: Date;\n *\n * @DateProperty({ optional: true })\n * deletedAt?: Date;\n * }\n */\nexport function DateProperty(\n options?: Omit<PropertyOptions<Date, DateConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Date });\n}\n\n/**\n * Helper decorator for BigInt properties\n * @example\n * class User {\n * @BigIntProperty()\n * id!: bigint;\n *\n * @BigIntProperty({ optional: true })\n * balance?: bigint;\n * }\n */\nexport function BigIntProperty(\n options?: Omit<PropertyOptions<bigint, BigIntConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => BigInt });\n}\n\n/**\n * Helper decorator for entity properties\n * @example\n * class User {\n * @EntityProperty(() => Address)\n * address!: Address;\n *\n * @EntityProperty(() => Profile, { optional: true })\n * profile?: Profile;\n * }\n */\nexport function EntityProperty<\n T,\n C extends AnyCtor<T> & { new (data: any): T },\n>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type'>,\n): PropertyDecorator {\n return Property<T, C>({ ...options, type });\n}\n\n/**\n * Helper decorator for array properties\n * @example\n * class User {\n * @ArrayProperty(() => String)\n * tags!: string[];\n *\n * @ArrayProperty(() => Phone)\n * phones!: Phone[];\n *\n * @ArrayProperty(() => Number, { optional: true })\n * scores?: number[];\n *\n * @ArrayProperty(() => String, { sparse: true })\n * sparseList!: (string | null)[];\n * }\n */\nexport function ArrayProperty<T, C extends CtorLike<T>>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type' | 'array'>,\n): PropertyDecorator {\n return Property({ ...options, type, array: true });\n}\n\n/**\n * Helper decorator for passthrough properties that bypass type validation.\n * Use this for generic types like Record<string, unknown>, any, or custom objects.\n * @example\n * class Config {\n * @PassthroughProperty()\n * metadata!: Record<string, unknown>;\n *\n * @PassthroughProperty()\n * customData!: any;\n * }\n */\nexport function PassthroughProperty(): PropertyDecorator {\n // Use a dummy type since type is mandatory but not used with passthrough\n return Property({ type: () => Object, passthrough: true });\n}\n\nexport const StringifiableProperty = <\n T extends { equals?(other: T): boolean; toString(): string },\n C extends CtorLike<T> & { parse(value: string): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n): PropertyDecorator =>\n Property<T, C>({\n ...data,\n type,\n equals: (a, b) => (a.equals ? a.equals(b) : a.toString() === b.toString()),\n serialize: (value) => value.toString(),\n deserialize: (value) => {\n if (typeof value === 'string') {\n return type().parse(value) as InstanceOfCtorLike<C>;\n }\n throw new Error(`Invalid value ${type().name}: ${String(value)}`);\n },\n });\n\nexport const SerializableProperty = <\n T extends { equals?(other: T): boolean; toJSON(): unknown },\n C extends CtorLike<T> & { parse(value: unknown): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n) =>\n Property({\n ...data,\n type,\n equals: (a: T, b: T) =>\n a.equals ? a.equals(b) : isEqual(a.toJSON(), b.toJSON()),\n serialize: (value: T) => value.toJSON(),\n deserialize: (value: unknown) => {\n return type().parse(value) as InstanceOfCtorLike<C>;\n },\n });\n"],"names":["isEqual","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","enumValidator","intValidator","Property","options","target","propertyKey","existingProperties","Reflect","getOwnMetadata","includes","push","defineMetadata","passthrough","array","Error","optional","sparse","serialize","undefined","deserialize","arrayValidators","hasSerialize","hasDeserialize","existingOptions","StringProperty","type","String","EnumProperty","enumType","validators","NumberProperty","Number","IntProperty","BooleanProperty","Boolean","DateProperty","Date","BigIntProperty","BigInt","EntityProperty","ArrayProperty","PassthroughProperty","Object","StringifiableProperty","data","equals","a","b","toString","value","parse","name","SerializableProperty","toJSON"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/property.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-wrapper-object-types */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { isEqual } from 'lodash-es';\nimport {\n AnyCtor,\n type CtorLike,\n type InstanceOfCtorLike,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n} from './types.js';\nimport {\n enumValidator,\n intValidator,\n minLengthValidator,\n maxLengthValidator,\n patternValidator,\n minValidator,\n maxValidator,\n arrayMinLengthValidator,\n arrayMaxLengthValidator,\n} from './validators.js';\n\n/**\n * Property decorator that marks class properties with metadata.\n * This decorator can be used to identify and track properties within classes.\n *\n * @param options - Configuration for the property (type is required)\n *\n * @example\n * class User {\n * @Property({ type: () => String })\n * name: string;\n *\n * @Property({ type: () => String, equals: (a, b) => a.toLowerCase() === b.toLowerCase() })\n * email: string;\n *\n * @Property({ type: () => Number })\n * age: number;\n * }\n */\nexport function Property<T, C extends CtorLike<T>>(\n options: PropertyOptions<T, C>,\n): PropertyDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingProperties: string[] =\n Reflect.getOwnMetadata(PROPERTY_METADATA_KEY, target) || [];\n\n if (!existingProperties.includes(propertyKey)) {\n existingProperties.push(propertyKey);\n }\n\n Reflect.defineMetadata(PROPERTY_METADATA_KEY, existingProperties, target);\n\n if (options.passthrough === true) {\n if (options.array === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and array: true. Passthrough cannot be combined with array.`,\n );\n }\n if (options.optional === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and optional: true. Passthrough cannot be combined with optional.`,\n );\n }\n if (options.sparse === true) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and sparse: true. Passthrough cannot be combined with sparse.`,\n );\n }\n if (\n options.serialize !== undefined ||\n options.deserialize !== undefined\n ) {\n throw new Error(\n `Property '${propertyKey}' has passthrough: true and custom serialize/deserialize functions. Passthrough cannot be combined with serialize or deserialize.`,\n );\n }\n }\n\n if (options.sparse === true && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has sparse: true but array is not true. The sparse option only applies to arrays.`,\n );\n }\n\n if (options.arrayValidators && options.array !== true) {\n throw new Error(\n `Property '${propertyKey}' has arrayValidators defined but array is not true. The arrayValidators option only applies to arrays.`,\n );\n }\n\n // Validate serialize/deserialize pairing\n const hasSerialize = options.serialize !== undefined;\n const hasDeserialize = options.deserialize !== undefined;\n if (hasSerialize !== hasDeserialize) {\n throw new Error(\n `Property '${propertyKey}' must define both serialize and deserialize functions, or neither. Found only ${hasSerialize ? 'serialize' : 'deserialize'}.`,\n );\n }\n\n const existingOptions: Record<\n string,\n PropertyOptions<any, any>\n > = Reflect.getOwnMetadata(PROPERTY_OPTIONS_METADATA_KEY, target) || {};\n\n existingOptions[propertyKey] = options;\n\n Reflect.defineMetadata(\n PROPERTY_OPTIONS_METADATA_KEY,\n existingOptions,\n target,\n );\n };\n}\n\n/**\n * Helper decorator for string properties\n * @example\n * class User {\n * @StringProperty()\n * name!: string;\n *\n * @StringProperty({ optional: true })\n * nickname?: string;\n *\n * @StringProperty({ minLength: 3, maxLength: 50 })\n * username!: string;\n *\n * @StringProperty({ pattern: /^[a-z]+$/ })\n * slug!: string;\n * }\n */\nexport function StringProperty(\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'> & {\n minLength?: number;\n maxLength?: number;\n pattern?: RegExp;\n patternMessage?: string;\n },\n): PropertyDecorator {\n const validators = [...(options?.validators || [])];\n\n if (options?.minLength !== undefined) {\n validators.unshift(minLengthValidator(options.minLength));\n }\n if (options?.maxLength !== undefined) {\n validators.unshift(maxLengthValidator(options.maxLength));\n }\n if (options?.pattern !== undefined) {\n validators.unshift(\n patternValidator(options.pattern, options.patternMessage),\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { minLength, maxLength, pattern, patternMessage, ...restOptions } =\n options || {};\n\n return Property({\n ...restOptions,\n type: () => String,\n validators: validators.length > 0 ? validators : undefined,\n });\n}\n\n/**\n * Helper decorator for enum properties (string enums)\n * Validates that the string value matches one of the enum values\n * @param enumType - The enum object (e.g., MyEnum)\n * @param options - Additional property options\n * @example\n * enum Status {\n * Active = 'active',\n * Inactive = 'inactive'\n * }\n *\n * class User {\n * @EnumProperty(Status)\n * status!: Status;\n *\n * @EnumProperty(Status, { optional: true })\n * previousStatus?: Status;\n * }\n */\nexport function EnumProperty<T extends Record<string, string>>(\n enumType: T,\n options?: Omit<PropertyOptions<string, StringConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [enumValidator(enumType), ...options.validators]\n : [enumValidator(enumType)];\n\n return Property({ ...options, type: () => String, validators });\n}\n\n/**\n * Helper decorator for number properties\n * @example\n * class User {\n * @NumberProperty()\n * age!: number;\n *\n * @NumberProperty({ optional: true })\n * score?: number;\n *\n * @NumberProperty({ min: 0, max: 100 })\n * percentage!: number;\n * }\n */\nexport function NumberProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'> & {\n min?: number;\n max?: number;\n },\n): PropertyDecorator {\n const validators = [...(options?.validators || [])];\n\n if (options?.min !== undefined) {\n validators.unshift(minValidator(options.min));\n }\n if (options?.max !== undefined) {\n validators.unshift(maxValidator(options.max));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { min, max, ...restOptions } = options || {};\n\n return Property({\n ...restOptions,\n type: () => Number,\n validators: validators.length > 0 ? validators : undefined,\n });\n}\n\n/**\n * Helper decorator for integer properties\n * Validates that the number is an integer (no decimal places)\n * @example\n * class User {\n * @IntProperty()\n * age!: number;\n *\n * @IntProperty({ optional: true })\n * count?: number;\n * }\n */\nexport function IntProperty(\n options?: Omit<PropertyOptions<number, NumberConstructor>, 'type'>,\n): PropertyDecorator {\n const validators = options?.validators\n ? [intValidator(), ...options.validators]\n : [intValidator()];\n\n return Property({ ...options, type: () => Number, validators });\n}\n\n/**\n * Helper decorator for boolean properties\n * @example\n * class User {\n * @BooleanProperty()\n * active!: boolean;\n *\n * @BooleanProperty({ optional: true })\n * verified?: boolean;\n * }\n */\nexport function BooleanProperty(\n options?: Omit<PropertyOptions<boolean, BooleanConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Boolean });\n}\n\n/**\n * Helper decorator for Date properties\n * @example\n * class User {\n * @DateProperty()\n * createdAt!: Date;\n *\n * @DateProperty({ optional: true })\n * deletedAt?: Date;\n * }\n */\nexport function DateProperty(\n options?: Omit<PropertyOptions<Date, DateConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => Date });\n}\n\n/**\n * Helper decorator for BigInt properties\n * @example\n * class User {\n * @BigIntProperty()\n * id!: bigint;\n *\n * @BigIntProperty({ optional: true })\n * balance?: bigint;\n * }\n */\nexport function BigIntProperty(\n options?: Omit<PropertyOptions<bigint, BigIntConstructor>, 'type'>,\n): PropertyDecorator {\n return Property({ ...options, type: () => BigInt });\n}\n\n/**\n * Helper decorator for entity properties\n * @example\n * class User {\n * @EntityProperty(() => Address)\n * address!: Address;\n *\n * @EntityProperty(() => Profile, { optional: true })\n * profile?: Profile;\n * }\n */\nexport function EntityProperty<\n T,\n C extends AnyCtor<T> & { new (data: any): T },\n>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type'>,\n): PropertyDecorator {\n return Property<T, C>({ ...options, type });\n}\n\n/**\n * Helper decorator for array properties\n * @example\n * class User {\n * @ArrayProperty(() => String)\n * tags!: string[];\n *\n * @ArrayProperty(() => Phone)\n * phones!: Phone[];\n *\n * @ArrayProperty(() => Number, { optional: true })\n * scores?: number[];\n *\n * @ArrayProperty(() => String, { sparse: true })\n * sparseList!: (string | null)[];\n *\n * @ArrayProperty(() => String, { minLength: 1, maxLength: 10 })\n * limitedList!: string[];\n * }\n */\nexport function ArrayProperty<T, C extends CtorLike<T>>(\n type: () => C,\n options?: Omit<PropertyOptions<T, C>, 'type' | 'array'> & {\n minLength?: number;\n maxLength?: number;\n },\n): PropertyDecorator {\n const arrayValidators = [...(options?.arrayValidators || [])];\n\n if (options?.minLength !== undefined) {\n arrayValidators.unshift(arrayMinLengthValidator(options.minLength));\n }\n if (options?.maxLength !== undefined) {\n arrayValidators.unshift(arrayMaxLengthValidator(options.maxLength));\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { minLength, maxLength, ...restOptions } = options || {};\n\n return Property({\n ...restOptions,\n type,\n array: true,\n arrayValidators: arrayValidators.length > 0 ? arrayValidators : undefined,\n });\n}\n\n/**\n * Helper decorator for passthrough properties that bypass type validation.\n * Use this for generic types like Record<string, unknown>, any, or custom objects.\n * @example\n * class Config {\n * @PassthroughProperty()\n * metadata!: Record<string, unknown>;\n *\n * @PassthroughProperty()\n * customData!: any;\n * }\n */\nexport function PassthroughProperty(): PropertyDecorator {\n // Use a dummy type since type is mandatory but not used with passthrough\n return Property({ type: () => Object, passthrough: true });\n}\n\nexport const StringifiableProperty = <\n T extends { equals?(other: T): boolean; toString(): string },\n C extends CtorLike<T> & { parse(value: string): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n): PropertyDecorator =>\n Property<T, C>({\n ...data,\n type,\n equals: (a, b) => (a.equals ? a.equals(b) : a.toString() === b.toString()),\n serialize: (value) => value.toString(),\n deserialize: (value) => {\n if (typeof value === 'string') {\n return type().parse(value) as InstanceOfCtorLike<C>;\n }\n throw new Error(`Invalid value ${type().name}: ${String(value)}`);\n },\n });\n\nexport const SerializableProperty = <\n T extends { equals?(other: T): boolean; toJSON(): unknown },\n C extends CtorLike<T> & { parse(value: unknown): T },\n>(\n type: () => C,\n data: Omit<\n PropertyOptions<T, C>,\n 'serialize' | 'deserialize' | 'passthrough' | 'type' | 'equals'\n > = {},\n) =>\n Property({\n ...data,\n type,\n equals: (a: T, b: T) =>\n a.equals ? a.equals(b) : isEqual(a.toJSON(), b.toJSON()),\n serialize: (value: T) => value.toJSON(),\n deserialize: (value: unknown) => {\n return type().parse(value) as InstanceOfCtorLike<C>;\n },\n });\n"],"names":["isEqual","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","enumValidator","intValidator","minLengthValidator","maxLengthValidator","patternValidator","minValidator","maxValidator","arrayMinLengthValidator","arrayMaxLengthValidator","Property","options","target","propertyKey","existingProperties","Reflect","getOwnMetadata","includes","push","defineMetadata","passthrough","array","Error","optional","sparse","serialize","undefined","deserialize","arrayValidators","hasSerialize","hasDeserialize","existingOptions","StringProperty","validators","minLength","unshift","maxLength","pattern","patternMessage","restOptions","type","String","length","EnumProperty","enumType","NumberProperty","min","max","Number","IntProperty","BooleanProperty","Boolean","DateProperty","Date","BigIntProperty","BigInt","EntityProperty","ArrayProperty","PassthroughProperty","Object","StringifiableProperty","data","equals","a","b","toString","value","parse","name","SerializableProperty","toJSON"],"mappings":"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"}
|
package/dist/lib/validators.d.ts
CHANGED
|
@@ -15,4 +15,61 @@ export declare function enumValidator<T extends Record<string, string>>(enumType
|
|
|
15
15
|
* const validator = intValidator();
|
|
16
16
|
*/
|
|
17
17
|
export declare function intValidator(): PropertyValidator<number>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a validator that checks if a string meets a minimum length requirement
|
|
20
|
+
* @param minLength - The minimum required length
|
|
21
|
+
* @returns A validator function that validates string length
|
|
22
|
+
* @example
|
|
23
|
+
* const validator = minLengthValidator(3);
|
|
24
|
+
*/
|
|
25
|
+
export declare function minLengthValidator(minLength: number): PropertyValidator<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a validator that checks if a string does not exceed a maximum length
|
|
28
|
+
* @param maxLength - The maximum allowed length
|
|
29
|
+
* @returns A validator function that validates string length
|
|
30
|
+
* @example
|
|
31
|
+
* const validator = maxLengthValidator(100);
|
|
32
|
+
*/
|
|
33
|
+
export declare function maxLengthValidator(maxLength: number): PropertyValidator<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a validator that checks if a string matches a regular expression pattern
|
|
36
|
+
* @param pattern - The regular expression pattern to match
|
|
37
|
+
* @param message - Optional custom error message
|
|
38
|
+
* @returns A validator function that validates string pattern
|
|
39
|
+
* @example
|
|
40
|
+
* const validator = patternValidator(/^[a-z]+$/, 'Must contain only lowercase letters');
|
|
41
|
+
*/
|
|
42
|
+
export declare function patternValidator(pattern: RegExp, message?: string): PropertyValidator<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a validator that checks if a number meets a minimum value requirement
|
|
45
|
+
* @param min - The minimum allowed value (inclusive)
|
|
46
|
+
* @returns A validator function that validates number minimum
|
|
47
|
+
* @example
|
|
48
|
+
* const validator = minValidator(0);
|
|
49
|
+
*/
|
|
50
|
+
export declare function minValidator(min: number): PropertyValidator<number>;
|
|
51
|
+
/**
|
|
52
|
+
* Creates a validator that checks if a number does not exceed a maximum value
|
|
53
|
+
* @param max - The maximum allowed value (inclusive)
|
|
54
|
+
* @returns A validator function that validates number maximum
|
|
55
|
+
* @example
|
|
56
|
+
* const validator = maxValidator(100);
|
|
57
|
+
*/
|
|
58
|
+
export declare function maxValidator(max: number): PropertyValidator<number>;
|
|
59
|
+
/**
|
|
60
|
+
* Creates a validator that checks if an array meets a minimum length requirement
|
|
61
|
+
* @param minLength - The minimum required array length
|
|
62
|
+
* @returns A validator function that validates array length
|
|
63
|
+
* @example
|
|
64
|
+
* const validator = arrayMinLengthValidator(1);
|
|
65
|
+
*/
|
|
66
|
+
export declare function arrayMinLengthValidator<T>(minLength: number): PropertyValidator<T[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a validator that checks if an array does not exceed a maximum length
|
|
69
|
+
* @param maxLength - The maximum allowed array length
|
|
70
|
+
* @returns A validator function that validates array length
|
|
71
|
+
* @example
|
|
72
|
+
* const validator = arrayMaxLengthValidator(10);
|
|
73
|
+
*/
|
|
74
|
+
export declare function arrayMaxLengthValidator<T>(maxLength: number): PropertyValidator<T[]>;
|
|
18
75
|
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/lib/validators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5D,QAAQ,EAAE,CAAC,GACV,iBAAiB,CAAC,MAAM,CAAC,CAa3B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAYxD"}
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/lib/validators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5D,QAAQ,EAAE,CAAC,GACV,iBAAiB,CAAC,MAAM,CAAC,CAa3B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAYxD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,GAChB,iBAAiB,CAAC,MAAM,CAAC,CAY3B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,GAChB,iBAAiB,CAAC,MAAM,CAAC,CAY3B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,CAAC,MAAM,CAAC,CAc3B;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAYnE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAYnE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,SAAS,EAAE,MAAM,GAChB,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAYxB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,SAAS,EAAE,MAAM,GAChB,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAYxB"}
|
package/dist/lib/validators.js
CHANGED
|
@@ -38,5 +38,139 @@ import { Problem } from './problem.js';
|
|
|
38
38
|
return [];
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a validator that checks if a string meets a minimum length requirement
|
|
43
|
+
* @param minLength - The minimum required length
|
|
44
|
+
* @returns A validator function that validates string length
|
|
45
|
+
* @example
|
|
46
|
+
* const validator = minLengthValidator(3);
|
|
47
|
+
*/ export function minLengthValidator(minLength) {
|
|
48
|
+
return ({ value })=>{
|
|
49
|
+
if (value.length < minLength) {
|
|
50
|
+
return [
|
|
51
|
+
new Problem({
|
|
52
|
+
property: '',
|
|
53
|
+
message: `Expected minimum length ${minLength} but received length ${value.length}`
|
|
54
|
+
})
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a validator that checks if a string does not exceed a maximum length
|
|
62
|
+
* @param maxLength - The maximum allowed length
|
|
63
|
+
* @returns A validator function that validates string length
|
|
64
|
+
* @example
|
|
65
|
+
* const validator = maxLengthValidator(100);
|
|
66
|
+
*/ export function maxLengthValidator(maxLength) {
|
|
67
|
+
return ({ value })=>{
|
|
68
|
+
if (value.length > maxLength) {
|
|
69
|
+
return [
|
|
70
|
+
new Problem({
|
|
71
|
+
property: '',
|
|
72
|
+
message: `Expected maximum length ${maxLength} but received length ${value.length}`
|
|
73
|
+
})
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates a validator that checks if a string matches a regular expression pattern
|
|
81
|
+
* @param pattern - The regular expression pattern to match
|
|
82
|
+
* @param message - Optional custom error message
|
|
83
|
+
* @returns A validator function that validates string pattern
|
|
84
|
+
* @example
|
|
85
|
+
* const validator = patternValidator(/^[a-z]+$/, 'Must contain only lowercase letters');
|
|
86
|
+
*/ export function patternValidator(pattern, message) {
|
|
87
|
+
return ({ value })=>{
|
|
88
|
+
if (!pattern.test(value)) {
|
|
89
|
+
return [
|
|
90
|
+
new Problem({
|
|
91
|
+
property: '',
|
|
92
|
+
message: message || `Expected value to match pattern ${pattern.toString()} but received "${value}"`
|
|
93
|
+
})
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
return [];
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a validator that checks if a number meets a minimum value requirement
|
|
101
|
+
* @param min - The minimum allowed value (inclusive)
|
|
102
|
+
* @returns A validator function that validates number minimum
|
|
103
|
+
* @example
|
|
104
|
+
* const validator = minValidator(0);
|
|
105
|
+
*/ export function minValidator(min) {
|
|
106
|
+
return ({ value })=>{
|
|
107
|
+
if (value < min) {
|
|
108
|
+
return [
|
|
109
|
+
new Problem({
|
|
110
|
+
property: '',
|
|
111
|
+
message: `Expected minimum value ${min} but received ${value}`
|
|
112
|
+
})
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
return [];
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Creates a validator that checks if a number does not exceed a maximum value
|
|
120
|
+
* @param max - The maximum allowed value (inclusive)
|
|
121
|
+
* @returns A validator function that validates number maximum
|
|
122
|
+
* @example
|
|
123
|
+
* const validator = maxValidator(100);
|
|
124
|
+
*/ export function maxValidator(max) {
|
|
125
|
+
return ({ value })=>{
|
|
126
|
+
if (value > max) {
|
|
127
|
+
return [
|
|
128
|
+
new Problem({
|
|
129
|
+
property: '',
|
|
130
|
+
message: `Expected maximum value ${max} but received ${value}`
|
|
131
|
+
})
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
return [];
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Creates a validator that checks if an array meets a minimum length requirement
|
|
139
|
+
* @param minLength - The minimum required array length
|
|
140
|
+
* @returns A validator function that validates array length
|
|
141
|
+
* @example
|
|
142
|
+
* const validator = arrayMinLengthValidator(1);
|
|
143
|
+
*/ export function arrayMinLengthValidator(minLength) {
|
|
144
|
+
return ({ value })=>{
|
|
145
|
+
if (value.length < minLength) {
|
|
146
|
+
return [
|
|
147
|
+
new Problem({
|
|
148
|
+
property: '',
|
|
149
|
+
message: `Expected minimum array length ${minLength} but received length ${value.length}`
|
|
150
|
+
})
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
return [];
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Creates a validator that checks if an array does not exceed a maximum length
|
|
158
|
+
* @param maxLength - The maximum allowed array length
|
|
159
|
+
* @returns A validator function that validates array length
|
|
160
|
+
* @example
|
|
161
|
+
* const validator = arrayMaxLengthValidator(10);
|
|
162
|
+
*/ export function arrayMaxLengthValidator(maxLength) {
|
|
163
|
+
return ({ value })=>{
|
|
164
|
+
if (value.length > maxLength) {
|
|
165
|
+
return [
|
|
166
|
+
new Problem({
|
|
167
|
+
property: '',
|
|
168
|
+
message: `Expected maximum array length ${maxLength} but received length ${value.length}`
|
|
169
|
+
})
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
return [];
|
|
173
|
+
};
|
|
174
|
+
}
|
|
41
175
|
|
|
42
176
|
//# sourceMappingURL=validators.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/validators.ts"],"sourcesContent":["import { Problem } from './problem.js';\nimport type { PropertyValidator } from './types.js';\n\n/**\n * Creates a validator that checks if a string value is one of the allowed enum values\n * @param enumType - The enum object containing valid values\n * @returns A validator function that validates enum values\n * @example\n * enum Status { Active = 'active', Inactive = 'inactive' }\n * const validator = enumValidator(Status);\n */\nexport function enumValidator<T extends Record<string, string>>(\n enumType: T,\n): PropertyValidator<string> {\n const validValues = Object.values(enumType);\n return ({ value }: { value: string }) => {\n if (!validValues.includes(value)) {\n return [\n new Problem({\n property: '',\n message: `Expected one of [${validValues.join(', ')}] but received \"${value}\"`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a number is an integer (no decimal places)\n * @returns A validator function that validates integer values\n * @example\n * const validator = intValidator();\n */\nexport function intValidator(): PropertyValidator<number> {\n return ({ value }: { value: number }) => {\n if (!Number.isInteger(value)) {\n return [\n new Problem({\n property: '',\n message: `Expected an integer but received ${value}`,\n }),\n ];\n }\n return [];\n };\n}\n"],"names":["Problem","enumValidator","enumType","validValues","Object","values","value","includes","property","message","join","intValidator","Number","isInteger"],"mappings":"AAAA,SAASA,OAAO,QAAQ,eAAe;AAGvC;;;;;;;CAOC,GACD,OAAO,SAASC,cACdC,QAAW;IAEX,MAAMC,cAAcC,OAAOC,MAAM,CAACH;IAClC,OAAO,CAAC,EAAEI,KAAK,EAAqB;QAClC,IAAI,CAACH,YAAYI,QAAQ,CAACD,QAAQ;YAChC,OAAO;gBACL,IAAIN,QAAQ;oBACVQ,UAAU;oBACVC,SAAS,CAAC,iBAAiB,EAAEN,YAAYO,IAAI,CAAC,MAAM,gBAAgB,EAAEJ,MAAM,CAAC,CAAC;gBAChF;aACD;QACH;QACA,OAAO,EAAE;IACX;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASK;IACd,OAAO,CAAC,EAAEL,KAAK,EAAqB;QAClC,IAAI,CAACM,OAAOC,SAAS,CAACP,QAAQ;YAC5B,OAAO;gBACL,IAAIN,QAAQ;oBACVQ,UAAU;oBACVC,SAAS,CAAC,iCAAiC,EAAEH,OAAO;gBACtD;aACD;QACH;QACA,OAAO,EAAE;IACX;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/validators.ts"],"sourcesContent":["import { Problem } from './problem.js';\nimport type { PropertyValidator } from './types.js';\n\n/**\n * Creates a validator that checks if a string value is one of the allowed enum values\n * @param enumType - The enum object containing valid values\n * @returns A validator function that validates enum values\n * @example\n * enum Status { Active = 'active', Inactive = 'inactive' }\n * const validator = enumValidator(Status);\n */\nexport function enumValidator<T extends Record<string, string>>(\n enumType: T,\n): PropertyValidator<string> {\n const validValues = Object.values(enumType);\n return ({ value }: { value: string }) => {\n if (!validValues.includes(value)) {\n return [\n new Problem({\n property: '',\n message: `Expected one of [${validValues.join(', ')}] but received \"${value}\"`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a number is an integer (no decimal places)\n * @returns A validator function that validates integer values\n * @example\n * const validator = intValidator();\n */\nexport function intValidator(): PropertyValidator<number> {\n return ({ value }: { value: number }) => {\n if (!Number.isInteger(value)) {\n return [\n new Problem({\n property: '',\n message: `Expected an integer but received ${value}`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a string meets a minimum length requirement\n * @param minLength - The minimum required length\n * @returns A validator function that validates string length\n * @example\n * const validator = minLengthValidator(3);\n */\nexport function minLengthValidator(\n minLength: number,\n): PropertyValidator<string> {\n return ({ value }: { value: string }) => {\n if (value.length < minLength) {\n return [\n new Problem({\n property: '',\n message: `Expected minimum length ${minLength} but received length ${value.length}`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a string does not exceed a maximum length\n * @param maxLength - The maximum allowed length\n * @returns A validator function that validates string length\n * @example\n * const validator = maxLengthValidator(100);\n */\nexport function maxLengthValidator(\n maxLength: number,\n): PropertyValidator<string> {\n return ({ value }: { value: string }) => {\n if (value.length > maxLength) {\n return [\n new Problem({\n property: '',\n message: `Expected maximum length ${maxLength} but received length ${value.length}`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a string matches a regular expression pattern\n * @param pattern - The regular expression pattern to match\n * @param message - Optional custom error message\n * @returns A validator function that validates string pattern\n * @example\n * const validator = patternValidator(/^[a-z]+$/, 'Must contain only lowercase letters');\n */\nexport function patternValidator(\n pattern: RegExp,\n message?: string,\n): PropertyValidator<string> {\n return ({ value }: { value: string }) => {\n if (!pattern.test(value)) {\n return [\n new Problem({\n property: '',\n message:\n message ||\n `Expected value to match pattern ${pattern.toString()} but received \"${value}\"`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a number meets a minimum value requirement\n * @param min - The minimum allowed value (inclusive)\n * @returns A validator function that validates number minimum\n * @example\n * const validator = minValidator(0);\n */\nexport function minValidator(min: number): PropertyValidator<number> {\n return ({ value }: { value: number }) => {\n if (value < min) {\n return [\n new Problem({\n property: '',\n message: `Expected minimum value ${min} but received ${value}`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if a number does not exceed a maximum value\n * @param max - The maximum allowed value (inclusive)\n * @returns A validator function that validates number maximum\n * @example\n * const validator = maxValidator(100);\n */\nexport function maxValidator(max: number): PropertyValidator<number> {\n return ({ value }: { value: number }) => {\n if (value > max) {\n return [\n new Problem({\n property: '',\n message: `Expected maximum value ${max} but received ${value}`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if an array meets a minimum length requirement\n * @param minLength - The minimum required array length\n * @returns A validator function that validates array length\n * @example\n * const validator = arrayMinLengthValidator(1);\n */\nexport function arrayMinLengthValidator<T>(\n minLength: number,\n): PropertyValidator<T[]> {\n return ({ value }: { value: T[] }) => {\n if (value.length < minLength) {\n return [\n new Problem({\n property: '',\n message: `Expected minimum array length ${minLength} but received length ${value.length}`,\n }),\n ];\n }\n return [];\n };\n}\n\n/**\n * Creates a validator that checks if an array does not exceed a maximum length\n * @param maxLength - The maximum allowed array length\n * @returns A validator function that validates array length\n * @example\n * const validator = arrayMaxLengthValidator(10);\n */\nexport function arrayMaxLengthValidator<T>(\n maxLength: number,\n): PropertyValidator<T[]> {\n return ({ value }: { value: T[] }) => {\n if (value.length > maxLength) {\n return [\n new Problem({\n property: '',\n message: `Expected maximum array length ${maxLength} but received length ${value.length}`,\n }),\n ];\n }\n return [];\n };\n}\n"],"names":["Problem","enumValidator","enumType","validValues","Object","values","value","includes","property","message","join","intValidator","Number","isInteger","minLengthValidator","minLength","length","maxLengthValidator","maxLength","patternValidator","pattern","test","toString","minValidator","min","maxValidator","max","arrayMinLengthValidator","arrayMaxLengthValidator"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { PropertyOptions, PropertyValidator } from './types.js';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Helper decorator for Zod schema properties.
|
|
5
|
+
* Validates values using a Zod schema during deserialization.
|
|
6
|
+
* Validation failures throw ValidationError (hard errors).
|
|
7
|
+
*
|
|
8
|
+
* When validation succeeds, applies any Zod transformations to the value.
|
|
9
|
+
*
|
|
10
|
+
* @param schema - The Zod schema to validate against
|
|
11
|
+
* @param options - Additional property options (optional, array, etc.)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { z } from 'zod';
|
|
16
|
+
*
|
|
17
|
+
* const UserSchema = z.object({
|
|
18
|
+
* name: z.string().min(3),
|
|
19
|
+
* age: z.number().int().min(0)
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* @Entity()
|
|
23
|
+
* class User {
|
|
24
|
+
* @ZodProperty(UserSchema)
|
|
25
|
+
* data!: z.infer<typeof UserSchema>;
|
|
26
|
+
*
|
|
27
|
+
* @ZodProperty(z.string().email(), { optional: true })
|
|
28
|
+
* email?: string;
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function ZodProperty<T = any>(schema: z.ZodTypeAny, options?: Omit<PropertyOptions<T, any>, 'type' | 'deserialize' | 'serialize' | 'passthrough' | 'validators'> & {
|
|
33
|
+
validators?: PropertyValidator<T>[];
|
|
34
|
+
}): PropertyDecorator;
|
|
35
|
+
//# sourceMappingURL=zod-property.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-property.d.ts","sourceRoot":"","sources":["../../src/lib/zod-property.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGrE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA6B7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,GAAG,EACjC,MAAM,EAAE,CAAC,CAAC,UAAU,EACpB,OAAO,CAAC,EAAE,IAAI,CACZ,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC,EACvB,MAAM,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,GAAG,YAAY,CACpE,GAAG;IACF,UAAU,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;CACrC,GACA,iBAAiB,CAiBnB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */ import { Property } from './property.js';
|
|
2
|
+
import { Problem } from './problem.js';
|
|
3
|
+
import { ValidationError } from './validation-error.js';
|
|
4
|
+
/**
|
|
5
|
+
* Converts Zod issues to Problem array
|
|
6
|
+
* @param issues - Zod validation issues
|
|
7
|
+
* @returns Array of Problems with proper property paths
|
|
8
|
+
*/ function zodIssuesToProblems(issues) {
|
|
9
|
+
return issues.map((issue)=>{
|
|
10
|
+
const path = issue.path || [];
|
|
11
|
+
let propertyPath = '';
|
|
12
|
+
for (const segment of path){
|
|
13
|
+
if (typeof segment === 'number') {
|
|
14
|
+
propertyPath += `[${segment}]`;
|
|
15
|
+
} else if (propertyPath === '') {
|
|
16
|
+
propertyPath = segment;
|
|
17
|
+
} else {
|
|
18
|
+
propertyPath += `.${segment}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return new Problem({
|
|
22
|
+
property: propertyPath,
|
|
23
|
+
message: issue.message || 'Validation failed'
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Helper decorator for Zod schema properties.
|
|
29
|
+
* Validates values using a Zod schema during deserialization.
|
|
30
|
+
* Validation failures throw ValidationError (hard errors).
|
|
31
|
+
*
|
|
32
|
+
* When validation succeeds, applies any Zod transformations to the value.
|
|
33
|
+
*
|
|
34
|
+
* @param schema - The Zod schema to validate against
|
|
35
|
+
* @param options - Additional property options (optional, array, etc.)
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import { z } from 'zod';
|
|
40
|
+
*
|
|
41
|
+
* const UserSchema = z.object({
|
|
42
|
+
* name: z.string().min(3),
|
|
43
|
+
* age: z.number().int().min(0)
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* @Entity()
|
|
47
|
+
* class User {
|
|
48
|
+
* @ZodProperty(UserSchema)
|
|
49
|
+
* data!: z.infer<typeof UserSchema>;
|
|
50
|
+
*
|
|
51
|
+
* @ZodProperty(z.string().email(), { optional: true })
|
|
52
|
+
* email?: string;
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/ export function ZodProperty(schema, options) {
|
|
56
|
+
return Property({
|
|
57
|
+
...options,
|
|
58
|
+
type: ()=>Object,
|
|
59
|
+
serialize: (value)=>value,
|
|
60
|
+
deserialize: (value)=>{
|
|
61
|
+
const result = schema.safeParse(value);
|
|
62
|
+
if (result.success) {
|
|
63
|
+
return result.data;
|
|
64
|
+
} else {
|
|
65
|
+
const problems = zodIssuesToProblems(result.error.issues);
|
|
66
|
+
throw new ValidationError(problems);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
validators: options?.validators
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=zod-property.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/zod-property.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Property } from './property.js';\nimport type { PropertyOptions, PropertyValidator } from './types.js';\nimport { Problem } from './problem.js';\nimport { ValidationError } from './validation-error.js';\nimport type { z } from 'zod';\n\n/**\n * Converts Zod issues to Problem array\n * @param issues - Zod validation issues\n * @returns Array of Problems with proper property paths\n */\nfunction zodIssuesToProblems(issues: z.core.$ZodIssue[]): Problem[] {\n return issues.map((issue: any) => {\n const path = issue.path || [];\n\n let propertyPath = '';\n for (const segment of path) {\n if (typeof segment === 'number') {\n propertyPath += `[${segment}]`;\n } else if (propertyPath === '') {\n propertyPath = segment;\n } else {\n propertyPath += `.${segment}`;\n }\n }\n\n return new Problem({\n property: propertyPath,\n message: issue.message || 'Validation failed',\n });\n });\n}\n\n/**\n * Helper decorator for Zod schema properties.\n * Validates values using a Zod schema during deserialization.\n * Validation failures throw ValidationError (hard errors).\n *\n * When validation succeeds, applies any Zod transformations to the value.\n *\n * @param schema - The Zod schema to validate against\n * @param options - Additional property options (optional, array, etc.)\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const UserSchema = z.object({\n * name: z.string().min(3),\n * age: z.number().int().min(0)\n * });\n *\n * @Entity()\n * class User {\n * @ZodProperty(UserSchema)\n * data!: z.infer<typeof UserSchema>;\n *\n * @ZodProperty(z.string().email(), { optional: true })\n * email?: string;\n * }\n * ```\n */\nexport function ZodProperty<T = any>(\n schema: z.ZodTypeAny,\n options?: Omit<\n PropertyOptions<T, any>,\n 'type' | 'deserialize' | 'serialize' | 'passthrough' | 'validators'\n > & {\n validators?: PropertyValidator<T>[];\n },\n): PropertyDecorator {\n return Property({\n ...options,\n type: () => Object,\n serialize: (value: any) => value,\n deserialize: (value: unknown): any => {\n const result = schema.safeParse(value);\n\n if (result.success) {\n return result.data as T;\n } else {\n const problems = zodIssuesToProblems(result.error.issues);\n throw new ValidationError(problems);\n }\n },\n validators: options?.validators as any,\n });\n}\n"],"names":["Property","Problem","ValidationError","zodIssuesToProblems","issues","map","issue","path","propertyPath","segment","property","message","ZodProperty","schema","options","type","Object","serialize","value","deserialize","result","safeParse","success","data","problems","error","validators"],"mappings":"AAAA,qDAAqD,GACrD,SAASA,QAAQ,QAAQ,gBAAgB;AAEzC,SAASC,OAAO,QAAQ,eAAe;AACvC,SAASC,eAAe,QAAQ,wBAAwB;AAGxD;;;;CAIC,GACD,SAASC,oBAAoBC,MAA0B;IACrD,OAAOA,OAAOC,GAAG,CAAC,CAACC;QACjB,MAAMC,OAAOD,MAAMC,IAAI,IAAI,EAAE;QAE7B,IAAIC,eAAe;QACnB,KAAK,MAAMC,WAAWF,KAAM;YAC1B,IAAI,OAAOE,YAAY,UAAU;gBAC/BD,gBAAgB,CAAC,CAAC,EAAEC,QAAQ,CAAC,CAAC;YAChC,OAAO,IAAID,iBAAiB,IAAI;gBAC9BA,eAAeC;YACjB,OAAO;gBACLD,gBAAgB,CAAC,CAAC,EAAEC,SAAS;YAC/B;QACF;QAEA,OAAO,IAAIR,QAAQ;YACjBS,UAAUF;YACVG,SAASL,MAAMK,OAAO,IAAI;QAC5B;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,SAASC,YACdC,MAAoB,EACpBC,OAKC;IAED,OAAOd,SAAS;QACd,GAAGc,OAAO;QACVC,MAAM,IAAMC;QACZC,WAAW,CAACC,QAAeA;QAC3BC,aAAa,CAACD;YACZ,MAAME,SAASP,OAAOQ,SAAS,CAACH;YAEhC,IAAIE,OAAOE,OAAO,EAAE;gBAClB,OAAOF,OAAOG,IAAI;YACpB,OAAO;gBACL,MAAMC,WAAWrB,oBAAoBiB,OAAOK,KAAK,CAACrB,MAAM;gBACxD,MAAM,IAAIF,gBAAgBsB;YAC5B;QACF;QACAE,YAAYZ,SAASY;IACvB;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtpaulino/entity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -52,9 +52,16 @@
|
|
|
52
52
|
"lodash-es": "^4.17.22"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@types/lodash-es": "^4.17.12"
|
|
55
|
+
"@types/lodash-es": "^4.17.12",
|
|
56
|
+
"zod": "^4.0.0"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"reflect-metadata": "^0.2.2"
|
|
59
|
+
"reflect-metadata": "^0.2.2",
|
|
60
|
+
"zod": "^4.0.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"zod": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
59
66
|
}
|
|
60
67
|
}
|