@fiado/type-kit 3.28.0 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,8 @@ const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const DeliveryChannelEnum_1 = require("../enums/DeliveryChannelEnum");
16
16
  const ContentTypeEnum_1 = require("../enums/ContentTypeEnum");
17
+ const IsDestinationByChannel_1 = require("../validators/IsDestinationByChannel");
18
+ const IsNotBlank_1 = require("../validators/IsNotBlank");
17
19
  class MessageDeliveryRequest {
18
20
  }
19
21
  exports.MessageDeliveryRequest = MessageDeliveryRequest;
@@ -24,21 +26,18 @@ __decorate([
24
26
  ], MessageDeliveryRequest.prototype, "deliveryChannel", void 0);
25
27
  __decorate([
26
28
  (0, class_transformer_1.Expose)(),
27
- (0, class_validator_1.IsString)(),
28
- (0, class_validator_1.IsNotEmpty)(),
29
+ (0, class_validator_1.Validate)(IsDestinationByChannel_1.IsDestinationByChannel),
29
30
  __metadata("design:type", String)
30
31
  ], MessageDeliveryRequest.prototype, "destination", void 0);
31
32
  __decorate([
32
33
  (0, class_transformer_1.Expose)(),
33
34
  (0, class_validator_1.ValidateIf)(o => o.deliveryChannel === DeliveryChannelEnum_1.DeliveryChannelEnum.MAIL),
34
- (0, class_validator_1.IsString)(),
35
- (0, class_validator_1.IsNotEmpty)(),
35
+ (0, class_validator_1.Validate)(IsNotBlank_1.IsNotBlank),
36
36
  __metadata("design:type", String)
37
37
  ], MessageDeliveryRequest.prototype, "subject", void 0);
38
38
  __decorate([
39
39
  (0, class_transformer_1.Expose)(),
40
- (0, class_validator_1.IsString)(),
41
- (0, class_validator_1.IsNotEmpty)(),
40
+ (0, class_validator_1.Validate)(IsNotBlank_1.IsNotBlank),
42
41
  __metadata("design:type", String)
43
42
  ], MessageDeliveryRequest.prototype, "message", void 0);
44
43
  __decorate([
@@ -1,4 +1,6 @@
1
1
  export * from './enums/DeliveryChannelEnum';
2
2
  export * from './enums/ContentTypeEnum';
3
+ export * from './validators/IsDestinationByChannel';
4
+ export * from './validators/IsNotBlank';
3
5
  export * from './dtos/MessageDeliveryRequest';
4
6
  export * from './dtos/MessageDeliveryResponse';
@@ -16,5 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./enums/DeliveryChannelEnum"), exports);
18
18
  __exportStar(require("./enums/ContentTypeEnum"), exports);
19
+ __exportStar(require("./validators/IsDestinationByChannel"), exports);
20
+ __exportStar(require("./validators/IsNotBlank"), exports);
19
21
  __exportStar(require("./dtos/MessageDeliveryRequest"), exports);
20
22
  __exportStar(require("./dtos/MessageDeliveryResponse"), exports);
@@ -0,0 +1,5 @@
1
+ import { ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
2
+ export declare class IsDestinationByChannel implements ValidatorConstraintInterface {
3
+ validate(value: unknown, args: ValidationArguments): boolean;
4
+ defaultMessage(args: ValidationArguments): string;
5
+ }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.IsDestinationByChannel = void 0;
10
+ const class_validator_1 = require("class-validator");
11
+ const DeliveryChannelEnum_1 = require("../enums/DeliveryChannelEnum");
12
+ /**
13
+ * Valida que `destination` matchee el formato correcto segun el `deliveryChannel`
14
+ * declarado en el mismo DTO.
15
+ *
16
+ * Por que un custom validator en lugar de dos `@ValidateIf` + `@IsEmail`/`@Matches`:
17
+ * - `@ValidateIf` afecta a **toda la propiedad**, no solo a los decoradores que
18
+ * vienen despues. Cuando se ponen dos `@ValidateIf` consecutivos sobre la
19
+ * misma propiedad, el segundo sobrescribe al primero y class-validator
20
+ * ignora silenciosamente los validators afectados.
21
+ * - Originalmente descubierto en `sns-ses-connector/tests/RUN_LOG.md` T7/T10/T11
22
+ * (2026-05-21) — la version anterior del DTO dejaba pasar
23
+ * `destination: "12345"` con canal SMS y `destination: "no-es-email"` con
24
+ * canal MAIL. Validator migrado del connector al type-kit en 3.29.0 para
25
+ * eliminar el DTO_LOCAL del connector y exponer la regla a todos los
26
+ * callers de `MessageDeliveryRequest`.
27
+ *
28
+ * Reglas:
29
+ * - Canal `SMS` → `destination` debe matchear E.164: `^\+\d{10,15}$`
30
+ * - Canal `MAIL` → `destination` debe matchear formato email
31
+ * - Canal invalido o ausente → falla (el `@IsEnum` del DTO genera su propio mensaje aparte)
32
+ * - String vacio o whitespace → falla
33
+ *
34
+ * Uso tipico:
35
+ * ```ts
36
+ * @Validate(IsDestinationByChannel)
37
+ * destination: string;
38
+ * ```
39
+ */
40
+ const E164_REGEX = /^\+\d{10,15}$/;
41
+ const EMAIL_REGEX = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
42
+ let IsDestinationByChannel = class IsDestinationByChannel {
43
+ validate(value, args) {
44
+ if (typeof value !== 'string' || value.trim().length === 0)
45
+ return false;
46
+ const obj = args.object;
47
+ const channel = obj.deliveryChannel;
48
+ if (channel === DeliveryChannelEnum_1.DeliveryChannelEnum.SMS)
49
+ return E164_REGEX.test(value);
50
+ if (channel === DeliveryChannelEnum_1.DeliveryChannelEnum.MAIL)
51
+ return EMAIL_REGEX.test(value);
52
+ return false;
53
+ }
54
+ defaultMessage(args) {
55
+ const obj = args.object;
56
+ const channel = obj.deliveryChannel;
57
+ if (channel === DeliveryChannelEnum_1.DeliveryChannelEnum.SMS) {
58
+ return 'destination debe ser un telefono en formato E.164 (ej. +5215512345678) para canal SMS';
59
+ }
60
+ if (channel === DeliveryChannelEnum_1.DeliveryChannelEnum.MAIL) {
61
+ return 'destination debe ser un email valido para canal MAIL';
62
+ }
63
+ return 'destination requerido y debe corresponder al deliveryChannel';
64
+ }
65
+ };
66
+ exports.IsDestinationByChannel = IsDestinationByChannel;
67
+ exports.IsDestinationByChannel = IsDestinationByChannel = __decorate([
68
+ (0, class_validator_1.ValidatorConstraint)({ name: 'IsDestinationByChannel', async: false })
69
+ ], IsDestinationByChannel);
@@ -0,0 +1,24 @@
1
+ import { ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
2
+ /**
3
+ * Valida que el valor sea un string con al menos un caracter no-whitespace.
4
+ *
5
+ * Por que no usar `@IsNotEmpty()` de class-validator:
6
+ * - `@IsNotEmpty()` solo rechaza `""`, `null`, `undefined`, `false`, `0`.
7
+ * - **No** rechaza strings de solo whitespace (`" "`).
8
+ *
9
+ * En el sns-ses-connector eso era un bug latente: un `message: " "` pasaba la
10
+ * validacion, llegaba al SDK de SNS/SES, y se enviaba un SMS o email con
11
+ * contenido vacio al usuario final (y AWS cobraba). Originalmente descubierto
12
+ * en `sns-ses-connector/tests/RUN_LOG.md` T8b (2026-05-21). Validator migrado
13
+ * al type-kit en 3.29.0 para eliminar el DTO_LOCAL del connector.
14
+ *
15
+ * Uso tipico:
16
+ * ```ts
17
+ * @Validate(IsNotBlank)
18
+ * message: string;
19
+ * ```
20
+ */
21
+ export declare class IsNotBlank implements ValidatorConstraintInterface {
22
+ validate(value: unknown, _args: ValidationArguments): boolean;
23
+ defaultMessage(args: ValidationArguments): string;
24
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.IsNotBlank = void 0;
10
+ const class_validator_1 = require("class-validator");
11
+ /**
12
+ * Valida que el valor sea un string con al menos un caracter no-whitespace.
13
+ *
14
+ * Por que no usar `@IsNotEmpty()` de class-validator:
15
+ * - `@IsNotEmpty()` solo rechaza `""`, `null`, `undefined`, `false`, `0`.
16
+ * - **No** rechaza strings de solo whitespace (`" "`).
17
+ *
18
+ * En el sns-ses-connector eso era un bug latente: un `message: " "` pasaba la
19
+ * validacion, llegaba al SDK de SNS/SES, y se enviaba un SMS o email con
20
+ * contenido vacio al usuario final (y AWS cobraba). Originalmente descubierto
21
+ * en `sns-ses-connector/tests/RUN_LOG.md` T8b (2026-05-21). Validator migrado
22
+ * al type-kit en 3.29.0 para eliminar el DTO_LOCAL del connector.
23
+ *
24
+ * Uso tipico:
25
+ * ```ts
26
+ * @Validate(IsNotBlank)
27
+ * message: string;
28
+ * ```
29
+ */
30
+ let IsNotBlank = class IsNotBlank {
31
+ validate(value, _args) {
32
+ return typeof value === 'string' && value.trim().length > 0;
33
+ }
34
+ defaultMessage(args) {
35
+ return `${args.property} no puede ser vacio ni solo espacios en blanco`;
36
+ }
37
+ };
38
+ exports.IsNotBlank = IsNotBlank;
39
+ exports.IsNotBlank = IsNotBlank = __decorate([
40
+ (0, class_validator_1.ValidatorConstraint)({ name: 'IsNotBlank', async: false })
41
+ ], IsNotBlank);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.28.0",
3
+ "version": "3.29.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -1,7 +1,9 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
2
+ import { IsEnum, Validate, ValidateIf } from 'class-validator';
3
3
  import { DeliveryChannelEnum } from '../enums/DeliveryChannelEnum';
4
4
  import { ContentTypeEnum } from '../enums/ContentTypeEnum';
5
+ import { IsDestinationByChannel } from '../validators/IsDestinationByChannel';
6
+ import { IsNotBlank } from '../validators/IsNotBlank';
5
7
 
6
8
  export class MessageDeliveryRequest {
7
9
  @Expose()
@@ -9,19 +11,16 @@ export class MessageDeliveryRequest {
9
11
  deliveryChannel: DeliveryChannelEnum;
10
12
 
11
13
  @Expose()
12
- @IsString()
13
- @IsNotEmpty()
14
+ @Validate(IsDestinationByChannel)
14
15
  destination: string;
15
16
 
16
17
  @Expose()
17
18
  @ValidateIf(o => o.deliveryChannel === DeliveryChannelEnum.MAIL)
18
- @IsString()
19
- @IsNotEmpty()
19
+ @Validate(IsNotBlank)
20
20
  subject: string;
21
21
 
22
22
  @Expose()
23
- @IsString()
24
- @IsNotEmpty()
23
+ @Validate(IsNotBlank)
25
24
  message: string;
26
25
 
27
26
  @Expose()
@@ -1,4 +1,6 @@
1
- export * from './enums/DeliveryChannelEnum';
2
- export * from './enums/ContentTypeEnum';
3
- export * from './dtos/MessageDeliveryRequest';
4
- export * from './dtos/MessageDeliveryResponse';
1
+ export * from './enums/DeliveryChannelEnum';
2
+ export * from './enums/ContentTypeEnum';
3
+ export * from './validators/IsDestinationByChannel';
4
+ export * from './validators/IsNotBlank';
5
+ export * from './dtos/MessageDeliveryRequest';
6
+ export * from './dtos/MessageDeliveryResponse';
@@ -0,0 +1,61 @@
1
+ import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
2
+ import { DeliveryChannelEnum } from '../enums/DeliveryChannelEnum';
3
+
4
+ /**
5
+ * Valida que `destination` matchee el formato correcto segun el `deliveryChannel`
6
+ * declarado en el mismo DTO.
7
+ *
8
+ * Por que un custom validator en lugar de dos `@ValidateIf` + `@IsEmail`/`@Matches`:
9
+ * - `@ValidateIf` afecta a **toda la propiedad**, no solo a los decoradores que
10
+ * vienen despues. Cuando se ponen dos `@ValidateIf` consecutivos sobre la
11
+ * misma propiedad, el segundo sobrescribe al primero y class-validator
12
+ * ignora silenciosamente los validators afectados.
13
+ * - Originalmente descubierto en `sns-ses-connector/tests/RUN_LOG.md` T7/T10/T11
14
+ * (2026-05-21) — la version anterior del DTO dejaba pasar
15
+ * `destination: "12345"` con canal SMS y `destination: "no-es-email"` con
16
+ * canal MAIL. Validator migrado del connector al type-kit en 3.29.0 para
17
+ * eliminar el DTO_LOCAL del connector y exponer la regla a todos los
18
+ * callers de `MessageDeliveryRequest`.
19
+ *
20
+ * Reglas:
21
+ * - Canal `SMS` → `destination` debe matchear E.164: `^\+\d{10,15}$`
22
+ * - Canal `MAIL` → `destination` debe matchear formato email
23
+ * - Canal invalido o ausente → falla (el `@IsEnum` del DTO genera su propio mensaje aparte)
24
+ * - String vacio o whitespace → falla
25
+ *
26
+ * Uso tipico:
27
+ * ```ts
28
+ * @Validate(IsDestinationByChannel)
29
+ * destination: string;
30
+ * ```
31
+ */
32
+ const E164_REGEX = /^\+\d{10,15}$/;
33
+ const EMAIL_REGEX = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
34
+
35
+ @ValidatorConstraint({ name: 'IsDestinationByChannel', async: false })
36
+ export class IsDestinationByChannel implements ValidatorConstraintInterface {
37
+ validate(value: unknown, args: ValidationArguments): boolean {
38
+ if (typeof value !== 'string' || value.trim().length === 0) return false;
39
+
40
+ const obj = args.object as { deliveryChannel?: unknown };
41
+ const channel = obj.deliveryChannel;
42
+
43
+ if (channel === DeliveryChannelEnum.SMS) return E164_REGEX.test(value);
44
+ if (channel === DeliveryChannelEnum.MAIL) return EMAIL_REGEX.test(value);
45
+
46
+ return false;
47
+ }
48
+
49
+ defaultMessage(args: ValidationArguments): string {
50
+ const obj = args.object as { deliveryChannel?: unknown };
51
+ const channel = obj.deliveryChannel;
52
+
53
+ if (channel === DeliveryChannelEnum.SMS) {
54
+ return 'destination debe ser un telefono en formato E.164 (ej. +5215512345678) para canal SMS';
55
+ }
56
+ if (channel === DeliveryChannelEnum.MAIL) {
57
+ return 'destination debe ser un email valido para canal MAIL';
58
+ }
59
+ return 'destination requerido y debe corresponder al deliveryChannel';
60
+ }
61
+ }
@@ -0,0 +1,31 @@
1
+ import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
2
+
3
+ /**
4
+ * Valida que el valor sea un string con al menos un caracter no-whitespace.
5
+ *
6
+ * Por que no usar `@IsNotEmpty()` de class-validator:
7
+ * - `@IsNotEmpty()` solo rechaza `""`, `null`, `undefined`, `false`, `0`.
8
+ * - **No** rechaza strings de solo whitespace (`" "`).
9
+ *
10
+ * En el sns-ses-connector eso era un bug latente: un `message: " "` pasaba la
11
+ * validacion, llegaba al SDK de SNS/SES, y se enviaba un SMS o email con
12
+ * contenido vacio al usuario final (y AWS cobraba). Originalmente descubierto
13
+ * en `sns-ses-connector/tests/RUN_LOG.md` T8b (2026-05-21). Validator migrado
14
+ * al type-kit en 3.29.0 para eliminar el DTO_LOCAL del connector.
15
+ *
16
+ * Uso tipico:
17
+ * ```ts
18
+ * @Validate(IsNotBlank)
19
+ * message: string;
20
+ * ```
21
+ */
22
+ @ValidatorConstraint({ name: 'IsNotBlank', async: false })
23
+ export class IsNotBlank implements ValidatorConstraintInterface {
24
+ validate(value: unknown, _args: ValidationArguments): boolean {
25
+ return typeof value === 'string' && value.trim().length > 0;
26
+ }
27
+
28
+ defaultMessage(args: ValidationArguments): string {
29
+ return `${args.property} no puede ser vacio ni solo espacios en blanco`;
30
+ }
31
+ }