Repository navigation
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughSe agrega Zod como dependencia, se introduce middleware de validación, se añaden múltiples DTOs Zod y mapeadores de respuesta, se añade/ajusta configuración de Prisma (nuevo prisma.config.js) y se refina el manejo de errores en el controlador de reseñas de producto. Changes
Sequence Diagram(s)(Skip) Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/modules/commerce/commerces/store.routes.js (1)
20-20:⚠️ Potential issue | 🟠 MajorFalta aplicar validación del body en la creación de stores.
En Line 20 se hace
POST /sinvalidate(CreateStoreDTO, "body"), dejando la entrada sin validar.🧩 Ajuste propuesto
-router.post("/", authenticate, createStore); +router.post("/", authenticate, validate(CreateStoreDTO, "body"), createStore);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/commerce/commerces/store.routes.js` at line 20, La ruta POST para crear stores usa router.post("/", authenticate, createStore) sin validar el body; añade el middleware de validación entre authenticate y createStore usando validate(CreateStoreDTO, "body") para que la entrada pase por la validación antes de ejecutar createStore y referencia la función validate y el DTO CreateStoreDTO al actualizar router.post.
🧹 Nitpick comments (8)
src/modules/global/dtos/products/product.response.dto.ts (1)
52-55: Posible acceso a propiedad undefined en el mapeo de tags.Si algún elemento de
product_tag_relationsno contieneproduct_tag, se pasaráundefinedal constructor deProductTagNestedDTO, lo que podría causar valores inesperados.♻️ Sugerencia para agregar validación defensiva
this.tags = - data.product_tag_relations?.map( - (r: any) => new ProductTagNestedDTO(r.product_tag) - ) ?? []; + data.product_tag_relations + ?.filter((r: any) => r.product_tag) + .map((r: any) => new ProductTagNestedDTO(r.product_tag)) ?? [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/products/product.response.dto.ts` around lines 52 - 55, The mapping of this.tags uses data.product_tag_relations?.map((r: any) => new ProductTagNestedDTO(r.product_tag)) which may pass undefined into ProductTagNestedDTO if a relation lacks product_tag; update the assignment to defensively filter out missing entries (e.g., filter relations where r?.product_tag is truthy) before mapping, or map with a guard that skips/ignores undefined product_tag values so only valid product_tag objects are passed to the ProductTagNestedDTO constructor.src/modules/global/dtos/stores/store.request.dto.ts (1)
25-28: Considerar agregar validación de formato paraphone.La validación actual solo verifica que
phonesea un string no vacío de máximo 20 caracteres, pero no valida el formato del número telefónico. Dependiendo de los requisitos del negocio, podría ser útil agregar una validación con regex para formatos de teléfono válidos.♻️ Ejemplo de validación de formato (opcional)
phone: z .string({ error: "phone es requerido" }) .min(1, "phone no puede estar vacío") - .max(20, "phone no puede superar 20 caracteres"), + .max(20, "phone no puede superar 20 caracteres") + .regex(/^[+]?[\d\s\-()]+$/, "phone debe tener un formato válido"),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/stores/store.request.dto.ts` around lines 25 - 28, La propiedad phone en el DTO (archivo con la definición de store.request.dto) solo valida longitud; añade una validación de formato usando la API de Zod para imponer un patrón de teléfono (p. ej. .regex(..., "phone formato inválido")) a la cadena `phone` para asegurar que cumpla el formato esperado; localiza la propiedad `phone` (definida con z.string(...).min(...).max(...)) y encadena `.regex(...)` con la expresión regular apropiada y un mensaje de error claro.src/modules/global/dtos/addresses/address.dto.ts (2)
41-65: Misma observación paraaddressenUpdateAddressDTO.También falta el límite de longitud en el campo
addressdel DTO de actualización.♻️ Agregar límite de longitud
- address: z.string().min(1, "address no puede estar vacío").optional(), + address: z + .string() + .min(1, "address no puede estar vacío") + .max(500, "address no puede superar 500 caracteres") + .optional(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/addresses/address.dto.ts` around lines 41 - 65, UpdateAddressDTO's address field lacks a maximum length constraint; update the address schema inside UpdateAddressDTO to include a .max(...) validation (e.g., match your other DTOs' conventions, such as .min(1, "address no puede estar vacío").max(200, "address no puede superar 200 caracteres").optional()) so the address field enforces an upper length limit consistent with city/region validations.
18-20: Falta restricción de longitud máxima enaddress.Los campos
cityyregiontienen validación.max(100), peroaddresssolo tiene.min(1)sin límite superior. Esto podría permitir direcciones extremadamente largas. Considerar agregar una restricción de longitud máxima.♻️ Agregar límite de longitud
address: z .string({ error: "address es requerido" }) - .min(1, "address no puede estar vacío"), + .min(1, "address no puede estar vacío") + .max(500, "address no puede superar 500 caracteres"),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/addresses/address.dto.ts` around lines 18 - 20, The address schema in address.dto.ts uses z.string().min(1) but lacks an upper length limit; update the address validator (the address field's z.string chain) to include a .max(...) constraint (e.g., .max(200, "address no puede exceder 200 caracteres") or match the existing city/region limit) so extremely long addresses are rejected and provide a clear error message.src/modules/global/dtos/products/product.request.dto.ts (1)
32-44: El manejo de errores en el transform detagspodría mejorar.Lanzar
new Error("tag inválido")dentro deltransformfunciona, pero Zod convertirá esto en un error genérico sin el path específico del tag inválido. Considerar usarz.preprocesso manejar el caso de forma que produzca mejores mensajes de error.♻️ Alternativa con mejor manejo de errores
tags: z .union([ z.array(z.number().int().positive("Cada tag debe ser un ID válido")), - z.string().transform((val) => - val.split(",").map((v) => { - const n = Number(v.trim()); - if (!Number.isInteger(n) || n <= 0) throw new Error("tag inválido"); - return n; - }) - ) + z.string().transform((val, ctx) => { + const result: number[] = []; + for (const v of val.split(",")) { + const n = Number(v.trim()); + if (!Number.isInteger(n) || n <= 0) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `Tag inválido: "${v.trim()}"`, + }); + return z.NEVER; + } + result.push(n); + } + return result; + }) ]) .optional() .default([]),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/products/product.request.dto.ts` around lines 32 - 44, El transform actual en la propiedad tags lanza new Error dentro de la transformación y produce errores genéricos sin path; reemplaza el transform por un z.preprocess que convierta la cadena CSV en un array de strings/números y luego deje que Zod valide ese array con z.array(z.number().int().positive()) para que los errores incluyan la ruta exacta (usa los mismos símbolos tags, z.union, z.preprocess y z.array(z.number().int().positive())). Asegúrate de mapear/parsear cada elemento en el preprocess y devolver undefined o el array parseado para invalidar correctamente la entrada, de modo que Zod genere mensajes de error detallados por índice en lugar de lanzar manualmente un Error.src/modules/global/dtos/orders/order.dto.ts (1)
43-46: Extraéorder_statusa una constante compartida para evitar drift.El enum está duplicado en dos lugares; centralizarlo reduce riesgo de inconsistencias futuras.
♻️ Refactor propuesto
+const OrderStatusEnum = z.enum( + ["PENDING", "PROCESSING", "SHIPPED", "DELIVERED", "CANCELLED"], + { error: "order_status debe ser PENDING, PROCESSING, SHIPPED, DELIVERED o CANCELLED" } +); export const UpdateOrderDTO = z .object({ - order_status: z - .enum(["PENDING", "PROCESSING", "SHIPPED", "DELIVERED", "CANCELLED"], { - error: "order_status debe ser PENDING, PROCESSING, SHIPPED, DELIVERED o CANCELLED" - }) - .optional(), + order_status: OrderStatusEnum.optional(), @@ export const FilterOrderDTO = z.object({ @@ - order_status: z - .enum(["PENDING", "PROCESSING", "SHIPPED", "DELIVERED", "CANCELLED"], { - error: "order_status debe ser PENDING, PROCESSING, SHIPPED, DELIVERED o CANCELLED" - }) - .optional(), + order_status: OrderStatusEnum.optional(),Also applies to: 75-77
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/orders/order.dto.ts` around lines 43 - 46, Extract the inline enum array used by order_status into a single exported constant (e.g., ORDER_STATUS_VALUES or OrderStatus) in a shared module and import it into this DTO; replace the inline z.enum([...], { error: ... }) usage in the order_status validator with z.enum(ORDER_STATUS_VALUES, { error: ... }) (and do the same replacement for the duplicate occurrence referenced at lines 75-77) so both validators reference the same source of truth and avoid drift.src/modules/global/dtos/users/user.response.dto.ts (1)
11-32: Tipá el input del DTO para no depender deany.Con un tipo explícito para el payload evitás errores silenciosos y mejorás autocompletado.
🧪 Refactor sugerido
+type UserRow = { + id_user: number; + name: string; + email: string; + phone?: string | null; + role: string; + created_at: Date; + updated_at: Date; +}; + export class UserResponseDTO extends BaseResponseDTO { @@ - constructor(data: any) { + constructor(data: UserRow) { @@ - static map(data: any): UserResponseDTO { + static map(data: UserRow): UserResponseDTO { return new UserResponseDTO(data); } - static mapList(data: any[]): UserResponseDTO[] { + static mapList(data: UserRow[]): UserResponseDTO[] { return data.map(UserResponseDTO.map); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/users/user.response.dto.ts` around lines 11 - 32, The DTO currently uses any for input; define a specific input type (e.g., interface UserPayload or type UserEntity) describing id_user, created_at, updated_at, name, email, phone?: string|null, role, etc., then change the constructor signature to constructor(data: UserPayload) and update static map(data: UserPayload): UserResponseDTO and mapList(data: UserPayload[]): UserResponseDTO[] to use that type; ensure the property types on UserResponseDTO match the payload (e.g., phone optional or nullable) so callers get proper type checking and autocompletion when using UserResponseDTO, constructor, map, and mapList.src/modules/global/dtos/wishlists/wishlist.dto.ts (1)
28-42: Conviene reutilizar un schema común de paginación.
page/limitse repite en varios DTOs globales; extraerlo evita mantenimiento duplicado.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/wishlists/wishlist.dto.ts` around lines 28 - 42, Extract the repeated page/limit logic into a shared PaginationDTO (or PaginationSchema) and replace the inline definitions inside FilterWishlistDTO with a .merge or .extend using that shared schema; specifically, create a reusable zod object (e.g., PaginationDTO with page: z.string().transform(Number)...default(1) and limit: z.string().transform(Number)...default(10)) and then refactor FilterWishlistDTO to import and merge/extend that PaginationDTO so page and limit are not duplicated across DTOs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 42: Actualiza la dependencia "prisma" en package.json de ^6.19.2 a la
versión major 7 que coincide con `@prisma/client` y `@prisma/adapter-pg` (como
^7.4.1 o ^7.5.0) para evitar incompatibilidades en comandos como `prisma
generate` y `prisma migrate`; después de cambiar la versión de "prisma", ejecuta
npm install (o yarn) y vuelve a correr `npx prisma generate` y los checks de
migración para asegurar que la CLI y el cliente estén alineados con
`@prisma/client` y `@prisma/adapter-pg`.
In `@prisma.config.js`:
- Around line 10-12: La configuración actual usa datasource.url =
process.env["DIRECT_URL"] || "" y silencia la ausencia de la variable; reemplazá
ese fallback por una validación que falle temprano: comprobá si
process.env["DIRECT_URL"] está definida y, si no, lanzá un Error (o llamá
process.exit(1) con un mensaje claro) antes de exportar la configuración para
que el proceso termine inmediatamente indicando la variable faltante; modificá
el bloque datasource (propiedad url) en prisma.config.js para leer la variable
obligatoria y arrojar/terminar con un mensaje explicativo en caso de ausencia.
In `@src/modules/commerce/commerces/dtos/create-store.dto.ts`:
- Around line 4-21: Replace the Zod option key "required_error" with "error" for
the DTO fields to match project convention: update fk_user, fk_store_category,
name and email in create-store.dto (change required_error -> error in the
z.number/z.string validators) so each validator uses error: "..." instead of
required_error.
- Around line 30-33: The URL fields (logo, website_url, instagram_url,
tiktok_url) currently use z.string().url() which allows non-http(s) protocols;
change their validators to restrict to http/https by using z.string().httpUrl()
(or apply a .refine that checks new URL(value).protocol is "http:" or "https:")
and keep the existing .max(500). Preserve .nullable()/.optional() as before and
update the error messages to indicate the URL must use http or https.
In `@src/modules/commerce/commerces/dtos/index.ts`:
- Line 1: The dtos/index.ts currently imports CreateStoreDTO but does not
re-export it, causing imports from store.routes.js to fail; update dtos/index.ts
to re-export the DTO (e.g., export CreateStoreDTO or use a direct re-export like
export { CreateStoreDTO } from "./create-store.dto") so store.routes.js can
import CreateStoreDTO properly.
In `@src/modules/global/dtos/base/base.response.dto.ts`:
- Around line 30-31: Valida que data.size > 0 antes de usar Math.ceil para
calcular this.total_pages: si data.size es mayor que 0 haz this.total_pages =
Math.ceil(data.total_elements / data.size) y asigna this.size = data.size; si
no, asigna this.total_pages = 0 (o el valor por defecto adecuado) y this.size =
0 para evitar dividir por cero o producir Infinity; aplica este cambio en el
mismo bloque donde se usan this.total_pages, this.size, data.total_elements y
data.size (por ejemplo en el constructor o método de BaseResponseDto).
In `@src/modules/global/dtos/product-reviews/product-review.dto.ts`:
- Around line 42-79: Agregar una validación cruzada en el esquema
FilterProductReviewDTO para garantizar coherencia entre minRating y maxRating:
usar superRefine (o refine) sobre FilterProductReviewDTO para comprobar que si
ambos existen entonces Number(minRating) <= Number(maxRating), y en caso
contrario añadir un error con ctx.addIssue asociado a la propiedad maxRating (o
mensaje general) indicando que minRating no puede ser mayor que maxRating;
actualizar los mensajes de error para que sean claros y mantener las
transformaciones existentes en minRating y maxRating.
In `@src/modules/global/dtos/product-tags/product-tag.dto.ts`:
- Around line 22-24: La validación .refine actualmente solo comprueba claves y
permite objetos como { name: undefined }; cambia la condición a comprobar
valores definidos: reemplaza la función en la llamada .refine(...) del DTO de
actualización (el product-tag update DTO en product-tag.dto.ts) por algo como
Object.values(data).some(v => v !== undefined) (o v !== undefined && v !== null
si también quieres excluir null), conservando el mensaje existente para que
falle cuando no haya ningún campo con valor definido.
In `@src/modules/global/dtos/store-categories/store-category.dto.ts`:
- Around line 22-24: El refine en store-category.dto.ts actualmente usa
Object.keys(data).length > 0 y solo verifica la presencia de claves; actualízalo
en la llamada .refine(...) (la expresión que define la validación) para
comprobar que al menos un valor no sea undefined, por ejemplo usando
Object.values(data).some(v => v !== undefined) como predicado, de modo que la
validación requiera cambios efectivos en los valores enviados.
In `@src/modules/global/dtos/users/user.request.dto.ts`:
- Around line 5-8: The name field currently allows strings with only spaces
because .min(1) runs on raw input; update the Zod schema for the name property
(the 'name' schema in user.request.dto.ts) to trim whitespace before length
checks—use a transformation like .transform(s => s.trim()) (or Zod's trim helper
if available) and then apply .min(1, ...) and .max(100, ...). Apply the same
trim-then-validate change to the other name occurrences noted (the other 'name'
schemas around lines 39-43 and 71) so create/update/filter all reject values
consisting only of spaces.
---
Outside diff comments:
In `@src/modules/commerce/commerces/store.routes.js`:
- Line 20: La ruta POST para crear stores usa router.post("/", authenticate,
createStore) sin validar el body; añade el middleware de validación entre
authenticate y createStore usando validate(CreateStoreDTO, "body") para que la
entrada pase por la validación antes de ejecutar createStore y referencia la
función validate y el DTO CreateStoreDTO al actualizar router.post.
---
Nitpick comments:
In `@src/modules/global/dtos/addresses/address.dto.ts`:
- Around line 41-65: UpdateAddressDTO's address field lacks a maximum length
constraint; update the address schema inside UpdateAddressDTO to include a
.max(...) validation (e.g., match your other DTOs' conventions, such as .min(1,
"address no puede estar vacío").max(200, "address no puede superar 200
caracteres").optional()) so the address field enforces an upper length limit
consistent with city/region validations.
- Around line 18-20: The address schema in address.dto.ts uses z.string().min(1)
but lacks an upper length limit; update the address validator (the address
field's z.string chain) to include a .max(...) constraint (e.g., .max(200,
"address no puede exceder 200 caracteres") or match the existing city/region
limit) so extremely long addresses are rejected and provide a clear error
message.
In `@src/modules/global/dtos/orders/order.dto.ts`:
- Around line 43-46: Extract the inline enum array used by order_status into a
single exported constant (e.g., ORDER_STATUS_VALUES or OrderStatus) in a shared
module and import it into this DTO; replace the inline z.enum([...], { error:
... }) usage in the order_status validator with z.enum(ORDER_STATUS_VALUES, {
error: ... }) (and do the same replacement for the duplicate occurrence
referenced at lines 75-77) so both validators reference the same source of truth
and avoid drift.
In `@src/modules/global/dtos/products/product.request.dto.ts`:
- Around line 32-44: El transform actual en la propiedad tags lanza new Error
dentro de la transformación y produce errores genéricos sin path; reemplaza el
transform por un z.preprocess que convierta la cadena CSV en un array de
strings/números y luego deje que Zod valide ese array con
z.array(z.number().int().positive()) para que los errores incluyan la ruta
exacta (usa los mismos símbolos tags, z.union, z.preprocess y
z.array(z.number().int().positive())). Asegúrate de mapear/parsear cada elemento
en el preprocess y devolver undefined o el array parseado para invalidar
correctamente la entrada, de modo que Zod genere mensajes de error detallados
por índice en lugar de lanzar manualmente un Error.
In `@src/modules/global/dtos/products/product.response.dto.ts`:
- Around line 52-55: The mapping of this.tags uses
data.product_tag_relations?.map((r: any) => new
ProductTagNestedDTO(r.product_tag)) which may pass undefined into
ProductTagNestedDTO if a relation lacks product_tag; update the assignment to
defensively filter out missing entries (e.g., filter relations where
r?.product_tag is truthy) before mapping, or map with a guard that skips/ignores
undefined product_tag values so only valid product_tag objects are passed to the
ProductTagNestedDTO constructor.
In `@src/modules/global/dtos/stores/store.request.dto.ts`:
- Around line 25-28: La propiedad phone en el DTO (archivo con la definición de
store.request.dto) solo valida longitud; añade una validación de formato usando
la API de Zod para imponer un patrón de teléfono (p. ej. .regex(..., "phone
formato inválido")) a la cadena `phone` para asegurar que cumpla el formato
esperado; localiza la propiedad `phone` (definida con
z.string(...).min(...).max(...)) y encadena `.regex(...)` con la expresión
regular apropiada y un mensaje de error claro.
In `@src/modules/global/dtos/users/user.response.dto.ts`:
- Around line 11-32: The DTO currently uses any for input; define a specific
input type (e.g., interface UserPayload or type UserEntity) describing id_user,
created_at, updated_at, name, email, phone?: string|null, role, etc., then
change the constructor signature to constructor(data: UserPayload) and update
static map(data: UserPayload): UserResponseDTO and mapList(data: UserPayload[]):
UserResponseDTO[] to use that type; ensure the property types on UserResponseDTO
match the payload (e.g., phone optional or nullable) so callers get proper type
checking and autocompletion when using UserResponseDTO, constructor, map, and
mapList.
In `@src/modules/global/dtos/wishlists/wishlist.dto.ts`:
- Around line 28-42: Extract the repeated page/limit logic into a shared
PaginationDTO (or PaginationSchema) and replace the inline definitions inside
FilterWishlistDTO with a .merge or .extend using that shared schema;
specifically, create a reusable zod object (e.g., PaginationDTO with page:
z.string().transform(Number)...default(1) and limit:
z.string().transform(Number)...default(10)) and then refactor FilterWishlistDTO
to import and merge/extend that PaginationDTO so page and limit are not
duplicated across DTOs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 98338248-fb66-4d02-bd9d-c25b60f897a6
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (26)
package.jsonprisma.config.jsprisma.config.tssrc/middlewares/validate.middleware.tssrc/modules/commerce/commerces/.gitkeepsrc/modules/commerce/commerces/dtos/create-store.dto.tssrc/modules/commerce/commerces/dtos/index.tssrc/modules/commerce/commerces/store.routes.jssrc/modules/commerce/product-reviews/product-review.routes.jssrc/modules/global/dtos/addresses/address.dto.tssrc/modules/global/dtos/base/base.response.dto.tssrc/modules/global/dtos/collections/collection.dto.tssrc/modules/global/dtos/notifications/notification.dto.tssrc/modules/global/dtos/orders/order.dto.tssrc/modules/global/dtos/product-categories/product-category.dto.tssrc/modules/global/dtos/product-reviews/product-review.dto.tssrc/modules/global/dtos/product-tags/product-tag.dto.tssrc/modules/global/dtos/products/product.request.dto.tssrc/modules/global/dtos/products/product.response.dto.tssrc/modules/global/dtos/shipping-zones/shipping-zone.dto.tssrc/modules/global/dtos/store-categories/store-category.dto.tssrc/modules/global/dtos/stores/store.request.dto.tssrc/modules/global/dtos/stores/store.response.dto.tssrc/modules/global/dtos/users/user.request.dto.tssrc/modules/global/dtos/users/user.response.dto.tssrc/modules/global/dtos/wishlists/wishlist.dto.ts
| fk_user: z | ||
| .number({ required_error: "fk_user es requerido" }) | ||
| .int() | ||
| .positive(), | ||
|
|
||
| fk_store_category: z | ||
| .number({ required_error: "fk_store_category es requerido" }) | ||
| .int() | ||
| .positive(), | ||
|
|
||
| name: z | ||
| .string({ required_error: "name es requerido" }) | ||
| .min(1) | ||
| .max(100), | ||
|
|
||
| email: z | ||
| .string({ required_error: "email es requerido" }) | ||
| .email("email no tiene formato válido") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Uso de APIs de error de Zod en DTOs:"
rg -n --type ts 'required_error|error\s*:' src/modulesRepository: CrisNAC/BackendMarketplace
Length of output: 5397
Unificá la convención de mensajes de error de Zod con el resto del proyecto.
Este archivo usá required_error, pero todos los demás DTOs del proyecto usan error. Para mantener consistencia, reemplazá required_error por error en las líneas 5, 10, 15, 20 y 25.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/commerce/commerces/dtos/create-store.dto.ts` around lines 4 - 21,
Replace the Zod option key "required_error" with "error" for the DTO fields to
match project convention: update fk_user, fk_store_category, name and email in
create-store.dto (change required_error -> error in the z.number/z.string
validators) so each validator uses error: "..." instead of required_error.
| logo: z.string().url("logo debe ser una URL válida").max(500).nullable().optional(), | ||
| website_url: z.string().url().max(500).nullable().optional(), | ||
| instagram_url: z.string().url().max(500).nullable().optional(), | ||
| tiktok_url: z.string().url().max(500).nullable().optional() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "create-store.dto.ts" | head -20Repository: CrisNAC/BackendMarketplace
Length of output: 127
🏁 Script executed:
# Check the file size and read it
wc -l src/modules/commerce/commerces/dtos/create-store.dto.tsRepository: CrisNAC/BackendMarketplace
Length of output: 128
🏁 Script executed:
# Read the entire DTO file
cat -n src/modules/commerce/commerces/dtos/create-store.dto.tsRepository: CrisNAC/BackendMarketplace
Length of output: 1400
🌐 Web query:
Zod z.string().url() validation allowed protocols security
💡 Result:
z.string().url() (and z.url() in Zod v4) validates “is parseable as a URL” and does not restrict schemes/protocols to http/https by default—so strings like ftp://…, file://…, mailto:…, and even javascript:… can still be “valid URLs” depending on the platform URL parser. This is because Zod’s URL check is based on the JS URL parser (new URL(...)). [1] (stackoverflow.com)
Security impact
If you accept arbitrary “valid URLs” and later place them into navigation contexts (e.g., <a href>, redirects), you can accidentally allow dangerous schemes like javascript: (which can execute code when clicked/navigated). [2] (developer.mozilla.org)
Safer options
- If you only want web URLs: use
z.httpUrl()(http/https only). [3] (zod.dev) - If you want HTTPS-only: add a refinement/transform check on
new URL(val).protocol === "https:"(or equivalent) after basic URL parsing (and optionally also restrict hostnames/ports).
References
- Stack Overflow: Zod
url()usesnew URL()(stackoverflow.com) - MDN:
javascript:URLs and their behavior (developer.mozilla.org) - Zod docs:
z.url()andz.httpUrl()string formats (zod.dev)
Citations:
- 1: https://stackoverflow.com/questions/77944022/custom-eslint-rule-to-ban-z-string-url-from-zod?utm_source=openai
- 2: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript?utm_source=openai
- 3: https://zod.dev/api?id=promises&utm_source=openai
- 4: https://stackoverflow.com/questions/77944022/custom-eslint-rule-to-ban-z-string-url-from-zod?utm_source=openai
- 5: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript?utm_source=openai
- 6: https://zod.dev/api?id=promises&utm_source=openai
🌐 Web query:
javascript: protocol XSS URL validation security issue
💡 Result:
Allowing untrusted URLs into navigation sinks (e.g., <a href>, iframe src, location.href, window.open) can become XSS if an attacker can supply a scriptable scheme like javascript: (and often data: / vbscript: in legacy contexts). OWASP explicitly recommends allow-listing only http and https for untrusted URLs used in href/src. (cheatsheetseries.owasp.org)
Why simple checks fail
Attackers can bypass naïve validation (e.g., startsWith("javascript:"), regexes, or checks done before canonicalization) using:
- Case changes, encoding, and entity tricks (e.g.,
java	script:style transformations in some contexts). (cheatsheetseries.owasp.org) - Control characters / whitespace / newlines that alter how parsers interpret the value (filter vs. browser parsing differences). (resources.docs.salesforce.com)
- Alternate script-capable vectors if you don’t restrict schemes (e.g.,
data:). (cheatsheetseries.owasp.org)
Safer URL validation pattern (JavaScript)
Rule of thumb: parse → normalize → enforce an allow-list of schemes (and optionally hosts) → only then use it.
function toSafeHttpUrl(input, base = window.location.origin) {
const s = String(input);
// Parse using the platform URL parser (handles canonicalization).
const u = new URL(s, base);
// Allow-list schemes only.
if (u.protocol !== "http:" && u.protocol !== "https:") return null;
// Optional: restrict host(s) to prevent open redirects / exfil.
// if (u.hostname !== "example.com") return null;
return u.toString();
}Also treat this as defense-in-depth: a strict CSP helps because browsers block common XSS vectors including javascript: URLs. (developer.mozilla.org)
Citations:
- 1: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
- 2: https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
- 3: https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/secure_coding.pdf
- 4: https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
- 5: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS
🌐 Web query:
Zod documentation URL validation HTTP HTTPS restriction
💡 Result:
In Zod, z.string().url() validates that a string is a URL, but it does not restrict the protocol (scheme) to HTTP/HTTPS by itself. [1]
To allow only http: and https:, add a refinement:
import { z } from "zod";
export const httpHttpsUrl = z.string().url().refine((s) => {
const protocol = new URL(s).protocol; // "https:" | "http:" | etc.
return protocol === "http:" || protocol === "https:";
}, { message: "URL must use http or https" });Zod’s URL validation is based on the platform’s standard URL API, so this also requires globalThis.URL to exist in your runtime. [2]
Alternative (simple but less robust):
const httpHttpsUrl = z.string().url().regex(/^https?:\/\//, "URL must start with http:// or https://");Sources: [1] [2]
Restringe URLs a http/https para prevenir inyección de protocolos maliciosos (XSS/open-redirect).
z.string().url() valida únicamente el formato URL, pero acepta cualquier protocolo (javascript:, data:, ftp:, etc.). Cuando estas URLs se usan en atributos href, src o redirecciones en frontend, pueden ejecutar código malicioso.
Zod ofrece z.httpUrl() que restringe automáticamente a http: y https::
🔧 Propuesta de ajuste
- logo: z.string().url("logo debe ser una URL válida").max(500).nullable().optional(),
- website_url: z.string().url().max(500).nullable().optional(),
- instagram_url: z.string().url().max(500).nullable().optional(),
- tiktok_url: z.string().url().max(500).nullable().optional()
+ logo: z.string().url("logo debe ser una URL válida").max(500).nullable().optional(),
+ website_url: z.httpUrl().max(500).nullable().optional(),
+ instagram_url: z.httpUrl().max(500).nullable().optional(),
+ tiktok_url: z.httpUrl().max(500).nullable().optional()O si prefieres personalizarla:
const HttpUrl = z
.string()
.url("Debe ser una URL válida")
.refine((value) => {
const protocol = new URL(value).protocol;
return protocol === "http:" || protocol === "https:";
}, "La URL debe usar http o https")
.max(500);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| logo: z.string().url("logo debe ser una URL válida").max(500).nullable().optional(), | |
| website_url: z.string().url().max(500).nullable().optional(), | |
| instagram_url: z.string().url().max(500).nullable().optional(), | |
| tiktok_url: z.string().url().max(500).nullable().optional() | |
| logo: z.string().url("logo debe ser una URL válida").max(500).nullable().optional(), | |
| website_url: z.httpUrl().max(500).nullable().optional(), | |
| instagram_url: z.httpUrl().max(500).nullable().optional(), | |
| tiktok_url: z.httpUrl().max(500).nullable().optional() |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/commerce/commerces/dtos/create-store.dto.ts` around lines 30 -
33, The URL fields (logo, website_url, instagram_url, tiktok_url) currently use
z.string().url() which allows non-http(s) protocols; change their validators to
restrict to http/https by using z.string().httpUrl() (or apply a .refine that
checks new URL(value).protocol is "http:" or "https:") and keep the existing
.max(500). Preserve .nullable()/.optional() as before and update the error
messages to indicate the URL must use http or https.
| @@ -0,0 +1 @@ | |||
| import { CreateStoreDTO } from "./create-store.dto"; No newline at end of file | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Uso del símbolo =="
rg -n "CreateStoreDTO" src/modules/commerce/commerces/dtos/index.ts src/modules/commerce/commerces/store.routes.js
echo
echo "== Export en barrel =="
rg -n "export\s+\{\s*CreateStoreDTO\s*\}\s+from" src/modules/commerce/commerces/dtos/index.ts || trueRepository: CrisNAC/BackendMarketplace
Length of output: 285
🏁 Script executed:
rg -B 5 -A 2 "CreateStoreDTO" src/modules/commerce/commerces/store.routes.js | head -20Repository: CrisNAC/BackendMarketplace
Length of output: 295
El barrel no exporta CreateStoreDTO (rompe el import desde store.routes.js).
El archivo dtos/index.ts solo importa el DTO sin re-exportarlo, lo que causa que store.routes.js falle al intentar importar desde "./dtos/index".
Fix requerido
-import { CreateStoreDTO } from "./create-store.dto";
+export { CreateStoreDTO } from "./create-store.dto";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { CreateStoreDTO } from "./create-store.dto"; | |
| export { CreateStoreDTO } from "./create-store.dto"; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/commerce/commerces/dtos/index.ts` at line 1, The dtos/index.ts
currently imports CreateStoreDTO but does not re-export it, causing imports from
store.routes.js to fail; update dtos/index.ts to re-export the DTO (e.g., export
CreateStoreDTO or use a direct re-export like export { CreateStoreDTO } from
"./create-store.dto") so store.routes.js can import CreateStoreDTO properly.
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (8)
src/modules/global/dtos/product-categories/product-category.dto.ts (1)
49-70: Considerar agregar tipado al parámetrodatapara mayor seguridad de tipos.El uso de
anyen el constructor y métodos estáticos pierde la seguridad de tipos de TypeScript. Definir una interfaz para el input esperado mejoraría la mantenibilidad y detectaría errores en tiempo de compilación.♻️ Refactor sugerido
+interface ProductCategoryRecord { + id_product_category: number; + name: string; + created_at: Date; + updated_at: Date; +} + export class ProductCategoryResponseDTO extends BaseResponseDTO { id_product_category: number; name: string; - constructor(data: any) { + constructor(data: ProductCategoryRecord) { super({ id: data.id_product_category, created_at: data.created_at, updated_at: data.updated_at }); this.id_product_category = data.id_product_category; this.name = data.name; } - static map(data: any): ProductCategoryResponseDTO { + static map(data: ProductCategoryRecord): ProductCategoryResponseDTO { return new ProductCategoryResponseDTO(data); } - static mapList(data: any[]): ProductCategoryResponseDTO[] { + static mapList(data: ProductCategoryRecord[]): ProductCategoryResponseDTO[] { return data.map(ProductCategoryResponseDTO.map); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/product-categories/product-category.dto.ts` around lines 49 - 70, Create a typed interface (e.g., ProductCategoryInput) describing the expected shape { id_product_category: number; name: string; created_at?: string | Date; updated_at?: string | Date } and replace all uses of any in the ProductCategoryResponseDTO constructor and static methods (constructor(data: ProductCategoryInput), static map(data: ProductCategoryInput), static mapList(data: ProductCategoryInput[])) so the class (ProductCategoryResponseDTO) enforces input types and gains compile-time safety when mapping input data.src/modules/global/dtos/wishlists/wishlist.dto.ts (1)
81-85: Reducir uso deanyen DTOs de respuesta para mejorar type-safety.En las líneas 81-85 y 94-112, el uso de
anyen constructores y mappers oculta errores de shape y debilita el autocompletado y las refactorizaciones. Este patrón se repite en 38+ instancias en toda la capa global de DTOs.♻️ Refactor sugerido con tipos explícitos
+type WishlistItemModel = { + id_wishlist_item: number; + fk_product: number; + quantity: number; +}; + +type WishlistModel = { + id_wishlist: number; + fk_user: number; + name: string; + created_at: Date | string; + updated_at: Date | string; + wishlist_items?: WishlistItemModel[]; +}; export class WishlistItemResponseDTO { id_wishlist_item: number; fk_product: number; quantity: number; - constructor(data: any) { + constructor(data: WishlistItemModel) { this.id_wishlist_item = data.id_wishlist_item; this.fk_product = data.fk_product; this.quantity = data.quantity; } + + static map(data: WishlistItemModel): WishlistItemResponseDTO { + return new WishlistItemResponseDTO(data); + } } export class WishlistResponseDTO extends BaseResponseDTO { @@ - constructor(data: any) { + constructor(data: WishlistModel) { @@ - this.items = data.wishlist_items?.map((i: any) => new WishlistItemResponseDTO(i)) ?? []; + this.items = (data.wishlist_items ?? []).map(WishlistItemResponseDTO.map); } - static map(data: any): WishlistResponseDTO { + static map(data: WishlistModel): WishlistResponseDTO { return new WishlistResponseDTO(data); } - static mapList(data: any[]): WishlistResponseDTO[] { + static mapList(data: WishlistModel[]): WishlistResponseDTO[] { return data.map(WishlistResponseDTO.map); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/wishlists/wishlist.dto.ts` around lines 81 - 85, The constructor currently accepts data: any (constructor(data: any)) and the mapper methods in the same file (lines ~94-112) also use any; replace these with an explicit typed input (e.g., define an interface WishlistItemData { id_wishlist_item: number; fk_product: number; quantity: number } or appropriate types) and change the constructor signature to constructor(data: WishlistItemData) and update the mapper signatures to accept/return that type; ensure assignments use the typed fields and adjust any callers to pass the correctly typed object (or map unknown input into WishlistItemData before calling) to restore type-safety and improve autocompletion for the class and its mappers.src/modules/commerce/product-reviews/product-review.controller.js (1)
7-13: Considerar loguear errores 5xx para debugging.El manejo de errores es correcto: valida el status y oculta mensajes internos en 5xx. Sin embargo, sin logging, los errores del servidor serán difíciles de diagnosticar.
♻️ Ajuste sugerido
} catch (error) { const status = Number.isInteger(error?.status) && error.status >= 400 && error.status <= 599 ? error.status : 500; + if (status >= 500) { + console.error("Error en getProductReviews:", error); + } return res.status(status).json({ message: status < 500 ? error.message : "Error interno del servidor" });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/commerce/product-reviews/product-review.controller.js` around lines 7 - 13, Agregar logging para errores de servidor en el bloque catch donde se calcula la variable status y se responde con res.status(...).json(...): cuando status sea >=500, registrar el objeto error (por ejemplo con processLogger.error or console.error) incluyendo contexto (mensaje y stack) antes de enviar la respuesta; conserva la lógica actual que oculta el mensaje al cliente para 5xx y solo añade la llamada de log que referencie la variable error en el controlador (el catch que contiene la comprobación Number.isInteger(error?.status) y la respuesta res.status(status).json(...)).src/modules/global/dtos/products/product.request.dto.ts (2)
35-40: Usarthrowconz.ZodErroroctx.addIssuepara errores en transforms.Lanzar
Error("tag inválido")funciona pero no produce errores Zod estructurados. Considerar usarsuperRefineo devolver errores Zod para consistencia con el resto de validaciones.♻️ Alternativa con preprocess
tags: z - .union([ - z.array(z.number().int().positive()), - z.string().transform((val) => - val.split(",").map((v) => { - const n = Number(v.trim()); - if (!Number.isInteger(n) || n <= 0) throw new Error("tag inválido"); - return n; - }) - ) - ]) + .preprocess( + (val) => (typeof val === "string" ? val.split(",").map((v) => Number(v.trim())) : val), + z.array(z.number().int().positive("tag debe ser un ID válido")) + ) .optional() .default([]),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/products/product.request.dto.ts` around lines 35 - 40, The transform currently throws a raw Error inside the z.string().transform callback (the z.string().transform(...) block) which produces non-Zod errors; change this to produce Zod-friendly errors by either using a preprocess + z.array(z.number().int().positive()) or by replacing the transform with a z.string().superRefine/transform that uses ctx.addIssue to report invalid tag values (or throw a z.ZodError) instead of throw new Error; locate the z.string().transform(...) in product.request.dto.ts and refactor to emit Zod issues via ctx.addIssue (or use preprocess to parse CSV -> number array and then validate with zod schemas) so validation errors are structured.
5-8: Aplicar.trim()anamepara rechazar valores de solo espacios.Con
.min(1)sin trim previo, un string como" "pasa la validación. Esto introduce datos inconsistentes.✂️ Ajuste sugerido
name: z .string({ error: "name es requerido" }) + .trim() .min(1, "name no puede estar vacío") .max(100, "name no puede superar 100 caracteres"),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/products/product.request.dto.ts` around lines 5 - 8, La validación del campo name en product.request.dto.ts usa z.string().min(1) pero no aplica .trim(), por lo que valores solo con espacios pasan; update el schema que define name (la cadena construida con z.string(...).min(...).max(...)) para encadenar .trim() antes de .min(1) (es decir: z.string(...).trim().min(1)...), de modo que los inputs compuestos únicamente por espacios sean rechazados manteniendo los mensajes de error actuales.src/modules/global/dtos/addresses/address.dto.ts (1)
106-119: Considerar tipar el parámetrodatadel constructor.Usar
anypierde beneficios de type safety. Definir una interface para la estructura esperada de los datos del ORM mejoraría la mantenibilidad.♻️ Ejemplo de tipado
interface AddressData { id_address: number; fk_user: number; fk_store?: number | null; address: string; city: string; region: string; postal_code?: string | null; created_at: Date; updated_at: Date; } constructor(data: AddressData) { ... }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/addresses/address.dto.ts` around lines 106 - 119, Define a strongly-typed input for the Address DTO by creating an interface (e.g. AddressData) that lists id_address, fk_user, fk_store?: number|null, address, city, region, postal_code?: string|null, created_at: Date, updated_at: Date and change the constructor signature from constructor(data: any) to constructor(data: AddressData); update the constructor body in the class (the constructor method shown) to use that type (keeping the same property assignments and null coalescing for fk_store and postal_code) and fix any call sites constructing this DTO to pass objects matching AddressData so TypeScript type checks will catch shape errors.src/modules/global/dtos/orders/order.dto.ts (2)
104-109: Manejar caso deNaNal convertirsubtotal.
Number(data.subtotal)puede producirNaNsi el valor no es numérico. Considerar validar o usar un fallback.♻️ Ajuste defensivo
- this.subtotal = Number(data.subtotal); + this.subtotal = Number(data.subtotal) || 0;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/orders/order.dto.ts` around lines 104 - 109, En el constructor de la clase (constructor) la asignación this.subtotal = Number(data.subtotal) puede resultar en NaN; valida la conversión y aplica un valor por defecto: convierte con Number o parseFloat, luego comprueba con Number.isFinite/Number.isNaN (o isNaN) y si no es un número válido asigna un fallback (ej. 0) para que this.subtotal siempre sea un número válido; ajusta junto a las otras asignaciones (id_order_item, fk_product, quantity) para mantener comportamiento consistente.
128-136: Mismo patrón de conversión entotal.Aplicar el mismo manejo defensivo para
Number(data.total)para evitarNaNen la respuesta.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/orders/order.dto.ts` around lines 128 - 136, The conversion for total can produce NaN; update the assignment of this.total to defensively parse data.total and fall back to a safe value (e.g., 0) when parsing fails. Locate the constructor where this.total is currently set from data.total and replace the direct Number(data.total) with a guarded parse (check isFinite/Number.isFinite or use parseFloat + fallback) so this.total is always a numeric value; keep the rest of the constructor (including this.order_items mapping to OrderItemResponseDTO) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/modules/global/dtos/addresses/address.dto.ts`:
- Line 43: La propiedad address dentro de UpdateAddressDTO solo tiene
.min(1)...optional(); actualiza su esquema para aplicar el mismo límite de
longitud máximo que usa el DTO de creación (CreateAddressDTO) para garantizar
consistencia. Modifica la definición de address en UpdateAddressDTO para añadir
el .max(...) con el mismo valor y mensaje de error que en CreateAddressDTO,
manteniendo .min(1) y .optional(); busca las constantes o la validación en
CreateAddressDTO para replicar exactamente el límite y texto.
- Around line 18-20: The address DTO's `address` Zod schema (the `address:`
field in address.dto.ts) is missing a maximum length constraint; update the
chain on `address` to include `.max(100, "address no puede tener más de 100
caracteres")` (matching `city`/`region` limits) so arbitrarily long strings are
rejected and the error message is consistent and localized.
In `@src/modules/global/dtos/product-categories/product-category.dto.ts`:
- Around line 5-10: The current CreateProductCategoryDTO uses z.string({ error:
"name es requerido" }) which mislabels type errors as missing-field errors;
update the z.string() call in CreateProductCategoryDTO to either remove the
error param and rely on the object-level required semantics, or provide a
conditional error function that distinguishes missing vs wrong-type (e.g., an
error: (issue) => issue.input === undefined ? "name es requerido" : "name debe
ser un texto") so that "name es requerido" is only returned when the field is
absent and a separate message is used for non-string inputs; keep the existing
.min and .max constraints.
In `@src/modules/global/dtos/users/user.request.dto.ts`:
- Around line 57-62: The Update user password schema currently calls .trim() on
the password (password: z.string().trim()...), which can alter the stored
password and cause login mismatches; update the password validator in this DTO
to match CreateUserDTO by removing .trim() (leave
.string().min(8).max(255).optional()) so passwords are validated but not
trimmed, keeping behavior consistent with CreateUserDTO and the authentication
flow.
- Around line 17-21: The password Zod schema currently calls .trim() on the
password field which will strip intentional leading/trailing spaces and cause
mismatches on login; update the DTO by removing .trim() from the password schema
(the password property in the Zod schema in user.request.dto.ts) so
stored/validated values preserve user-entered whitespace, and keep the existing
.min(8) and .max(255) validations unchanged.
In `@src/modules/global/dtos/wishlists/wishlist.dto.ts`:
- Around line 6-9: The z.string validators for the wishlist fields currently
allow input of only whitespace (e.g., " "); update the validators by inserting
.trim() before .min(1) so whitespace-only values are rejected — apply this
change to the z.string chain for the "name" field and the other z.string
validator in the same file (the one around lines 16-19) so both perform
.string().trim().min(1, ...) (keeping their existing .max(...) and error
messages).
---
Nitpick comments:
In `@src/modules/commerce/product-reviews/product-review.controller.js`:
- Around line 7-13: Agregar logging para errores de servidor en el bloque catch
donde se calcula la variable status y se responde con res.status(...).json(...):
cuando status sea >=500, registrar el objeto error (por ejemplo con
processLogger.error or console.error) incluyendo contexto (mensaje y stack)
antes de enviar la respuesta; conserva la lógica actual que oculta el mensaje al
cliente para 5xx y solo añade la llamada de log que referencie la variable error
en el controlador (el catch que contiene la comprobación
Number.isInteger(error?.status) y la respuesta res.status(status).json(...)).
In `@src/modules/global/dtos/addresses/address.dto.ts`:
- Around line 106-119: Define a strongly-typed input for the Address DTO by
creating an interface (e.g. AddressData) that lists id_address, fk_user,
fk_store?: number|null, address, city, region, postal_code?: string|null,
created_at: Date, updated_at: Date and change the constructor signature from
constructor(data: any) to constructor(data: AddressData); update the constructor
body in the class (the constructor method shown) to use that type (keeping the
same property assignments and null coalescing for fk_store and postal_code) and
fix any call sites constructing this DTO to pass objects matching AddressData so
TypeScript type checks will catch shape errors.
In `@src/modules/global/dtos/orders/order.dto.ts`:
- Around line 104-109: En el constructor de la clase (constructor) la asignación
this.subtotal = Number(data.subtotal) puede resultar en NaN; valida la
conversión y aplica un valor por defecto: convierte con Number o parseFloat,
luego comprueba con Number.isFinite/Number.isNaN (o isNaN) y si no es un número
válido asigna un fallback (ej. 0) para que this.subtotal siempre sea un número
válido; ajusta junto a las otras asignaciones (id_order_item, fk_product,
quantity) para mantener comportamiento consistente.
- Around line 128-136: The conversion for total can produce NaN; update the
assignment of this.total to defensively parse data.total and fall back to a safe
value (e.g., 0) when parsing fails. Locate the constructor where this.total is
currently set from data.total and replace the direct Number(data.total) with a
guarded parse (check isFinite/Number.isFinite or use parseFloat + fallback) so
this.total is always a numeric value; keep the rest of the constructor
(including this.order_items mapping to OrderItemResponseDTO) unchanged.
In `@src/modules/global/dtos/product-categories/product-category.dto.ts`:
- Around line 49-70: Create a typed interface (e.g., ProductCategoryInput)
describing the expected shape { id_product_category: number; name: string;
created_at?: string | Date; updated_at?: string | Date } and replace all uses of
any in the ProductCategoryResponseDTO constructor and static methods
(constructor(data: ProductCategoryInput), static map(data:
ProductCategoryInput), static mapList(data: ProductCategoryInput[])) so the
class (ProductCategoryResponseDTO) enforces input types and gains compile-time
safety when mapping input data.
In `@src/modules/global/dtos/products/product.request.dto.ts`:
- Around line 35-40: The transform currently throws a raw Error inside the
z.string().transform callback (the z.string().transform(...) block) which
produces non-Zod errors; change this to produce Zod-friendly errors by either
using a preprocess + z.array(z.number().int().positive()) or by replacing the
transform with a z.string().superRefine/transform that uses ctx.addIssue to
report invalid tag values (or throw a z.ZodError) instead of throw new Error;
locate the z.string().transform(...) in product.request.dto.ts and refactor to
emit Zod issues via ctx.addIssue (or use preprocess to parse CSV -> number array
and then validate with zod schemas) so validation errors are structured.
- Around line 5-8: La validación del campo name en product.request.dto.ts usa
z.string().min(1) pero no aplica .trim(), por lo que valores solo con espacios
pasan; update el schema que define name (la cadena construida con
z.string(...).min(...).max(...)) para encadenar .trim() antes de .min(1) (es
decir: z.string(...).trim().min(1)...), de modo que los inputs compuestos
únicamente por espacios sean rechazados manteniendo los mensajes de error
actuales.
In `@src/modules/global/dtos/wishlists/wishlist.dto.ts`:
- Around line 81-85: The constructor currently accepts data: any
(constructor(data: any)) and the mapper methods in the same file (lines ~94-112)
also use any; replace these with an explicit typed input (e.g., define an
interface WishlistItemData { id_wishlist_item: number; fk_product: number;
quantity: number } or appropriate types) and change the constructor signature to
constructor(data: WishlistItemData) and update the mapper signatures to
accept/return that type; ensure assignments use the typed fields and adjust any
callers to pass the correctly typed object (or map unknown input into
WishlistItemData before calling) to restore type-safety and improve
autocompletion for the class and its mappers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 09f7fe12-b1d1-4d46-8130-7ec25b7c3972
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (20)
package.jsonprisma.config.jssrc/middlewares/validate.middleware.tssrc/modules/commerce/collection/.gitkeepsrc/modules/commerce/product-reviews/product-review.controller.jssrc/modules/commerce/product-reviews/product-review.routes.jssrc/modules/global/dtos/addresses/address.dto.tssrc/modules/global/dtos/base/base.response.dto.tssrc/modules/global/dtos/collections/collection.dto.tssrc/modules/global/dtos/notifications/notification.dto.tssrc/modules/global/dtos/orders/order.dto.tssrc/modules/global/dtos/product-categories/product-category.dto.tssrc/modules/global/dtos/product-reviews/product-review.dto.tssrc/modules/global/dtos/product-tags/product-tag.dto.tssrc/modules/global/dtos/products/product.request.dto.tssrc/modules/global/dtos/shipping-zones/shipping-zone.dto.tssrc/modules/global/dtos/store-categories/store-category.dto.tssrc/modules/global/dtos/stores/store.request.dto.tssrc/modules/global/dtos/users/user.request.dto.tssrc/modules/global/dtos/wishlists/wishlist.dto.ts
✅ Files skipped from review due to trivial changes (3)
- package.json
- src/modules/commerce/product-reviews/product-review.routes.js
- src/modules/global/dtos/base/base.response.dto.ts
🚧 Files skipped from review as they are similar to previous changes (8)
- prisma.config.js
- src/middlewares/validate.middleware.ts
- src/modules/global/dtos/product-tags/product-tag.dto.ts
- src/modules/global/dtos/shipping-zones/shipping-zone.dto.ts
- src/modules/global/dtos/stores/store.request.dto.ts
- src/modules/global/dtos/notifications/notification.dto.ts
- src/modules/global/dtos/collections/collection.dto.ts
- src/modules/global/dtos/store-categories/store-category.dto.ts
| address: z | ||
| .string({ error: "address es requerido" }) | ||
| .min(1, "address no puede estar vacío"), |
There was a problem hiding this comment.
Falta restricción de longitud máxima en address.
A diferencia de city y region (max 100), el campo address no tiene límite, permitiendo strings arbitrariamente largos.
✂️ Ajuste sugerido
address: z
.string({ error: "address es requerido" })
- .min(1, "address no puede estar vacío"),
+ .min(1, "address no puede estar vacío")
+ .max(500, "address no puede superar 500 caracteres"),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| address: z | |
| .string({ error: "address es requerido" }) | |
| .min(1, "address no puede estar vacío"), | |
| address: z | |
| .string({ error: "address es requerido" }) | |
| .min(1, "address no puede estar vacío") | |
| .max(500, "address no puede superar 500 caracteres"), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/global/dtos/addresses/address.dto.ts` around lines 18 - 20, The
address DTO's `address` Zod schema (the `address:` field in address.dto.ts) is
missing a maximum length constraint; update the chain on `address` to include
`.max(100, "address no puede tener más de 100 caracteres")` (matching
`city`/`region` limits) so arbitrarily long strings are rejected and the error
message is consistent and localized.
| name: z | ||
| .string({ error: "name es requerido" }) | ||
| .min(1, "name no puede estar vacío") | ||
| .max(50, "name no puede superar 50 caracteres") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/modules/global/dtos/wishlists/wishlist.dto.tsRepository: CrisNAC/BackendMarketplace
Length of output: 4074
🏁 Script executed:
# Buscar archivos de prueba relacionados con wishlists DTO
find . -type f \( -name "*wishlist*test*" -o -name "*wishlist*.spec.*" -o -name "*wishlist*.test.*" \) 2>/dev/null | head -20Repository: CrisNAC/BackendMarketplace
Length of output: 52
🏁 Script executed:
# Buscar si hay ejemplos de .trim() en el código
rg "\.trim\(\)" --type ts --type js -n | head -20Repository: CrisNAC/BackendMarketplace
Length of output: 1897
🌐 Web query:
Zod string trim validation whitespace min
💡 Result:
To reject whitespace-only strings in Zod, trim first, then apply a minimum length:
import { z } from "zod";
const RequiredTrimmed = z.string().trim().min(1, { message: "Required" });trim()removes leading/trailing whitespace before validation. [1]z.string()by itself allows"", so the common pattern for “required (non-empty) after trimming” is.trim().min(1)(or piping to that schema). [2]- Note:
trim()does not remove whitespace inside the string (e.g.,"a b"stays"a b"). [3]
Sources: [1] [2] [3]
Agregar .trim() para validar nombres sin espacios en blanco únicamente.
En las líneas 6-9 y 16-19, .min(1) permite valores como " " (solo espacios), que se guardan como datos inválidos. El patrón .trim() seguido de .min(1) ya se usa en otros DTOs del proyecto (ej: user.request.dto.ts).
💡 Ajuste propuesto
export const CreateWishlistDTO = z.object({
name: z
.string({ error: "name es requerido" })
+ .trim()
.min(1, "name no puede estar vacío")
.max(50, "name no puede superar 50 caracteres")
});
export const UpdateWishlistDTO = z
.object({
name: z
.string()
+ .trim()
.min(1, "name no puede estar vacío")
.max(50, "name no puede superar 50 caracteres")
.optional()
})Also applies to: 16-19
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/global/dtos/wishlists/wishlist.dto.ts` around lines 6 - 9, The
z.string validators for the wishlist fields currently allow input of only
whitespace (e.g., " "); update the validators by inserting .trim() before
.min(1) so whitespace-only values are rejected — apply this change to the
z.string chain for the "name" field and the other z.string validator in the same
file (the one around lines 16-19) so both perform .string().trim().min(1, ...)
(keeping their existing .max(...) and error messages).
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/modules/global/dtos/wishlists/wishlist.dto.ts (1)
22-31:⚠️ Potential issue | 🟡 MinorFalta
.trim()para validar nombres sin solo espacios en blanco.A diferencia de
CreateWishlistDTO, este esquema no incluye.trim()antes de.min(1), lo que permite guardar valores como" ". Esto genera una inconsistencia en la validación entre crear y actualizar.🛡️ Corrección propuesta
export const UpdateWishlistDTO = z .object({ name: z .string({ error: (issue) => issue.input === undefined ? "name es requerido" : "name debe ser un texto" }) + .trim() .min(1, "name no puede estar vacío") .max(50, "name no puede superar 50 caracteres") .optional() })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/wishlists/wishlist.dto.ts` around lines 22 - 31, The name string schema in the wishlist DTO currently lacks .trim(), allowing values of only whitespace to pass; update the schema for the name field (the z.string(...).min(1).max(50).optional() definition) to call .trim() before .min(1) so it behaves the same as CreateWishlistDTO and rejects names consisting solely of spaces while keeping the existing error messages and optional() behavior.
🧹 Nitpick comments (3)
src/modules/global/dtos/wishlists/wishlist.dto.ts (1)
39-53: El uso de.optional()antes de.default()es redundante.En Zod 4,
.default(value)ya maneja el casoundefinedy proporciona el valor por defecto. Usar.optional().default()funciona pero es innecesario.♻️ Simplificación propuesta
export const FilterWishlistDTO = z.object({ page: z .string() .transform(Number) .pipe(z.number().int().positive("page debe ser mayor a 0")) - .optional() .default(1), limit: z .string() .transform(Number) .pipe(z.number().int().min(1).max(100, "limit no puede superar 100")) - .optional() .default(10) });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/wishlists/wishlist.dto.ts` around lines 39 - 53, The FilterWishlistDTO schema uses .optional() before .default() on the page and limit fields which is redundant; update the FilterWishlistDTO definition by removing the .optional() calls on the page and limit chains so each field relies on .default(...) to provide a value (locate the page and limit chains inside the FilterWishlistDTO constant).src/modules/global/dtos/product-categories/product-category.dto.ts (2)
63-78: Considerar reemplazaranycon un tipo específico.El uso de
anyreduce la seguridad de tipos de TypeScript. Definir una interfaz para el parámetrodatamejoraría la mantenibilidad y detectaría errores en tiempo de compilación.♻️ Sugerencia de tipado
+interface ProductCategoryData { + id_product_category: number; + name: string; + created_at: Date; + updated_at: Date; +} + export class ProductCategoryResponseDTO extends BaseResponseDTO { id_product_category: number; name: string; - constructor(data: any) { + constructor(data: ProductCategoryData) { super({ id: data.id_product_category, created_at: data.created_at, updated_at: data.updated_at }); this.id_product_category = data.id_product_category; this.name = data.name; } - static map(data: any): ProductCategoryResponseDTO { + static map(data: ProductCategoryData): ProductCategoryResponseDTO { return new ProductCategoryResponseDTO(data); } - static mapList(data: any[]): ProductCategoryResponseDTO[] { + static mapList(data: ProductCategoryData[]): ProductCategoryResponseDTO[] { return data.map(ProductCategoryResponseDTO.map); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/product-categories/product-category.dto.ts` around lines 63 - 78, Reemplaza los usos de any en ProductCategoryResponseDTO para recuperar tipado fuerte: define una interfaz (por ejemplo ProductCategoryInput o ProductCategoryRow) con las propiedades id_product_category, name, created_at y updated_at, luego cambia la firma del constructor de ProductCategoryResponseDTO(data: any) a constructor(data: ProductCategoryInput), cambia static map(data: any) a static map(data: ProductCategoryInput) y cambia static mapList(data: any[]) a static mapList(data: ProductCategoryInput[]); ajusta llamadas externas si es necesario para cumplir el nuevo tipo.
5-15: Considerar agregar.trim()para evitar valores con solo espacios en blanco.Sin
.trim(), un valor como" "(solo espacios) pasaría la validación.min(1)pero sería efectivamente vacío. Agregar.trim()antes de.min()aseguraría que los espacios no cuenten como contenido válido.♻️ Sugerencia de mejora
export const CreateProductCategoryDTO = z.object({ name: z .string({ error: (issue) => issue.input === undefined ? "name es requerido" : "name debe ser un texto" }) + .trim() .min(1, "name no puede estar vacío") .max(100, "name no puede superar 100 caracteres") });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/dtos/product-categories/product-category.dto.ts` around lines 5 - 15, The name schema in CreateProductCategoryDTO allows values made only of spaces to pass because it lacks trimming; update the zod chain on the name field to call .trim() before .min(1, ...) (e.g., z.string({...}).trim().min(1, "name no puede estar vacío").max(...)) so whitespace-only strings are rejected while preserving the existing custom error behavior and max length check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/modules/global/dtos/addresses/address.dto.ts`:
- Around line 30-33: The region zod schema currently allows strings of only
spaces; update the region field's z.string chain in address.dto.ts to call
.trim() before .min/.max (matching how address and city are normalized) so
values like " " are rejected; make the same .trim() change for the region
field in the corresponding create and update DTO schemas (the region
z.string(...) definitions around the shown snippet and the one at lines ~59-63).
---
Duplicate comments:
In `@src/modules/global/dtos/wishlists/wishlist.dto.ts`:
- Around line 22-31: The name string schema in the wishlist DTO currently lacks
.trim(), allowing values of only whitespace to pass; update the schema for the
name field (the z.string(...).min(1).max(50).optional() definition) to call
.trim() before .min(1) so it behaves the same as CreateWishlistDTO and rejects
names consisting solely of spaces while keeping the existing error messages and
optional() behavior.
---
Nitpick comments:
In `@src/modules/global/dtos/product-categories/product-category.dto.ts`:
- Around line 63-78: Reemplaza los usos de any en ProductCategoryResponseDTO
para recuperar tipado fuerte: define una interfaz (por ejemplo
ProductCategoryInput o ProductCategoryRow) con las propiedades
id_product_category, name, created_at y updated_at, luego cambia la firma del
constructor de ProductCategoryResponseDTO(data: any) a constructor(data:
ProductCategoryInput), cambia static map(data: any) a static map(data:
ProductCategoryInput) y cambia static mapList(data: any[]) a static
mapList(data: ProductCategoryInput[]); ajusta llamadas externas si es necesario
para cumplir el nuevo tipo.
- Around line 5-15: The name schema in CreateProductCategoryDTO allows values
made only of spaces to pass because it lacks trimming; update the zod chain on
the name field to call .trim() before .min(1, ...) (e.g.,
z.string({...}).trim().min(1, "name no puede estar vacío").max(...)) so
whitespace-only strings are rejected while preserving the existing custom error
behavior and max length check.
In `@src/modules/global/dtos/wishlists/wishlist.dto.ts`:
- Around line 39-53: The FilterWishlistDTO schema uses .optional() before
.default() on the page and limit fields which is redundant; update the
FilterWishlistDTO definition by removing the .optional() calls on the page and
limit chains so each field relies on .default(...) to provide a value (locate
the page and limit chains inside the FilterWishlistDTO constant).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2ade5dc1-c870-4021-bb9c-46a80f9f564f
📒 Files selected for processing (4)
src/modules/global/dtos/addresses/address.dto.tssrc/modules/global/dtos/product-categories/product-category.dto.tssrc/modules/global/dtos/users/user.request.dto.tssrc/modules/global/dtos/wishlists/wishlist.dto.ts
✅ Files skipped from review due to trivial changes (1)
- src/modules/global/dtos/users/user.request.dto.ts
Summary by CodeRabbit
Nuevas Características
Correcciones de Errores
Chores