Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions .tsc-baseline.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"generatedAt": "2026-05-09T23:55:23.566Z",
"totalErrors": 960,
"generatedAt": "2026-05-10T00:32:16.986Z",
"totalErrors": 924,
"counts": {
"src/components/admin/DiscountApprovalQueue.tsx": {
"TS18048": 1
Expand Down Expand Up @@ -695,12 +695,6 @@
"TS2322": 1,
"TS2353": 3
},
"src/lib/external-db/products.ts": {
"TS2345": 13,
"TS2322": 20,
"TS2339": 2,
"TS7053": 1
},
"src/lib/external-db/techniques.ts": {
"TS2322": 19
},
Expand Down
43 changes: 30 additions & 13 deletions src/lib/external-db/products.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { type ExternalProduct } from "@/types/external-db";
/**
* Fetch products with full enrichment (colors, images, variants, suppliers).
*/
Expand All @@ -12,6 +11,24 @@ import {
shouldFallbackSelect,
} from './product-types';

// Row shapes for external_db_bridge results (untyped at runtime; assertions below).
type VariantRow = {
id: string; product_id: string; sku?: string | null;
color_id?: string | null; color_name?: string | null; color_code?: string | null;
color_hex?: string | null; stock_quantity?: number | null;
selected_thumbnail?: string | null; images?: string[] | null;
};
Comment on lines +16 to +21
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

VariantRow.id obrigatório está inconsistente com o payload consultado.

VariantRow exige id: string, mas a query de product_variants não seleciona id. Isso mascara erro de contrato e pode quebrar a associação por variant.id na montagem das imagens.

🔧 Ajuste sugerido
- select: 'product_id, color_name, color_hex, color_code, color_id, sku, stock_quantity, images, selected_thumbnail',
+ select: 'id, product_id, color_name, color_hex, color_code, color_id, sku, stock_quantity, images, selected_thumbnail',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/external-db/products.ts` around lines 16 - 21, VariantRow currently
declares id: string but the product_variants query doesn't select id, causing a
contract mismatch when assembling images (variant.id). Fix by either updating
the query that reads product_variants to include the id column, or change the
VariantRow type to match the payload (e.g., id?: string | null); prefer adding
id to the product_variants select so VariantRow.id remains required and
variant.id can be reliably used when assembling images.

type ImageRow = {
Comment on lines +16 to +21
product_id: string; variant_id: string | null;
url_cdn: string; url_original: string | null; filename: string | null;
image_type: string; is_primary: boolean; is_og_image: boolean | null;
applies_to_color: boolean | null; display_order: number;
supplier_code: string | null; alt_text: string | null; title_text: string | null;
};
type SupplierRow = { id: string; name: string; code: string };
type ColorVariationRow = { id: string; name: string; slug: string; group_id: string };
type ColorGroupRow = { id: string; name: string; slug: string };
Comment on lines +29 to +30

export async function fetchPromobrindProducts(options?: {
search?: string;
limit?: number;
Expand Down Expand Up @@ -231,31 +248,31 @@ async function enrichProducts(
}

// Extract results
const variantsRecords: Record<string, unknown>[] = [];
const variantsRecords: VariantRow[] = [];
for (const idx of queryMap.variants) {
const r = batchResults[idx];
if (r?.success && r.data?.records) variantsRecords.push(...r.data.records);
if (r?.success && r.data?.records) variantsRecords.push(...(r.data.records as VariantRow[]));
}
const imagesRecords: Record<string, unknown>[] = [];
const imagesRecords: ImageRow[] = [];
for (const idx of queryMap.images) {
const r = batchResults[idx];
if (r?.success && r.data?.records) imagesRecords.push(...r.data.records);
if (r?.success && r.data?.records) imagesRecords.push(...(r.data.records as ImageRow[]));
}
const suppliersRecords: { id: string; name: string; code: string }[] = [];
const suppliersRecords: SupplierRow[] = [];
for (const idx of queryMap.suppliers) {
const r = batchResults[idx];
if (r?.success && r.data?.records) suppliersRecords.push(...(r.data.records as ExternalProduct[]));
if (r?.success && r.data?.records) suppliersRecords.push(...(r.data.records as SupplierRow[]));
}

let colorVariationsRecords: Record<string, unknown>[] = [];
let colorVariationsRecords: ColorVariationRow[] = [];
for (const idx of queryMap.colorVariations) {
const r = batchResults[idx];
if (r?.success && r.data?.records) colorVariationsRecords = r.data.records as unknown[];
if (r?.success && r.data?.records) colorVariationsRecords = r.data.records as ColorVariationRow[];
}
let colorGroupsRecords: Record<string, unknown>[] = [];
let colorGroupsRecords: ColorGroupRow[] = [];
for (const idx of queryMap.colorGroups) {
const r = batchResults[idx];
if (r?.success && r.data?.records) colorGroupsRecords = r.data.records as unknown[];
if (r?.success && r.data?.records) colorGroupsRecords = r.data.records as ColorGroupRow[];
Comment on lines +252 to +276
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verifica pontos com cast direto de records sem narrowing e o tipo de BatchResult no bridge.
rg -n -C2 'r\.data\.records as [A-Za-z]+Row\[\]' src/lib/external-db/products.ts
fd -i 'bridge.ts' src/lib/external-db --exec sed -n '1,260p' {}

Repository: adm01-debug/Promo_Gifts

Length of output: 9157


🏁 Script executed:

# Verify the Row type definitions exist to understand what narrowing should check
rg -n 'type.*Row\s*=|interface.*Row\s*{' src/lib/external-db/products.ts | head -20

Repository: adm01-debug/Promo_Gifts

Length of output: 195


🏁 Script executed:

# Check Row type definitions
rg -n 'type.*Row' src/lib/external-db/products.ts | head -20

Repository: adm01-debug/Promo_Gifts

Length of output: 335


🏁 Script executed:

# Get the full Row type definitions
sed -n '16,31p' src/lib/external-db/products.ts

Repository: adm01-debug/Promo_Gifts

Length of output: 927


Casts de records sem narrowing propagam dados não-validados.

Essas linhas fazem cast direto de unknown[] para [RowType][] sem validação de shape. Se o bridge retornar registros incompletos ou mal-formados (campos obrigatórios faltando ou tipos errados), você propaga dados inconsistentes para maps/enriquecimento sem proteção.

Implemente narrowing mínimo antes de usar os dados:

Exemplo de fix
+const isSupplierRow = (v: unknown): v is SupplierRow => {
+  if (!v || typeof v !== 'object') return false;
+  const r = v as Record<string, unknown>;
+  return typeof r.id === 'string' && typeof r.name === 'string' && typeof r.code === 'string';
+};

 const suppliersRecords: SupplierRow[] = [];
 for (const idx of queryMap.suppliers) {
   const r = batchResults[idx];
-  if (r?.success && r.data?.records) suppliersRecords.push(...(r.data.records as SupplierRow[]));
+  if (r?.success && Array.isArray(r.data?.records)) {
+    suppliersRecords.push(...r.data.records.filter(isSupplierRow));
+  }
 }

Repita para variants, images, colorVariations e colorGroups (linhas 255, 260, 271, 276).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/external-db/products.ts` around lines 252 - 276, As linhas que
preenchem variantsRecords, imagesRecords, suppliersRecords,
colorVariationsRecords e colorGroupsRecords fazem casts diretos de
r.data.records (unknown[]) para tipos específicos sem checagem; ajuste o código
para validar/narrowear os registros antes de usá‑los: quando iterar queryMap.* e
ler batchResults[idx], verifique Array.isArray(r.data?.records) e
filtre/normalize cada item usando type guards ou checagens de campos
obrigatórios (por exemplo, verifica presença e tipos de id, product_id, url,
etc.) antes de push/assign em variantsRecords, imagesRecords, suppliersRecords,
colorVariationsRecords e colorGroupsRecords, assim evitando propagação de dados
malformados para os passos seguintes.

}

const suppliersMap = new Map(suppliersRecords.map(s => [s.id, s.name]));
Expand All @@ -266,8 +283,8 @@ async function enrichProducts(
if (s?.id && s?.name) putInCacheSafe('suppliers', { id: s.id, name: s.name, code: s.code });
}
} catch { /* cache populate is best-effort */ }
const colorVariationMap = new Map(colorVariationsRecords.map((v) => [v.id as string, { name: v.name as string, slug: v.slug as string, group_id: v.group_id as string }]));
const colorGroupMap = new Map(colorGroupsRecords.map((g) => [g.id as string, { name: g.name as string, slug: g.slug as string }]));
const colorVariationMap = new Map(colorVariationsRecords.map((v) => [v.id, { name: v.name, slug: v.slug, group_id: v.group_id }]));
const colorGroupMap = new Map(colorGroupsRecords.map((g) => [g.id, { name: g.name, slug: g.slug }]));

// Build image map
const productIdSet = new Set(productIds);
Expand Down
Loading