chore(eslint): fix 21 quick-win errors (Onda 5 PR 5.1)#109
Conversation
Reduces ESLint errors from 164 → 143 (-13%) by fixing low-risk categories:
**Deleted (1):**
- .eslintrc.tailwind.js — orphan file with TS syntax in .js extension,
not referenced anywhere (eslint.config.*, package.json, .github/).
Caused 'Parsing error: Unexpected token {' on every lint run.
**no-useless-escape (10):** removed unnecessary backslashes in regex char classes
- scripts/ds-config.ts:57 (negative lookahead)
- src/features/auth/components/PasswordStrengthMeter.tsx:22 (\[ \/)
- src/features/inbox/components/chat/MarkdownPreview.tsx:19 (\*)
- src/features/inbox/components/linkPreviewUtils.ts:2 (2x \+)
- src/features/inbox/hooks/useSipClient.ts:44 (\( \))
- src/lib/csvUtils.ts:154 (\.)
- src/lib/security.ts:115 (\-)
**prefer-const (3):** let → const where assignment happens once
- src/features/inbox/hooks/useScheduledMediaUpload.ts: removed dangling
let declaration, inlined const at point of assignment
- src/pages/admin/AdminStressTestPage.tsx: same pattern
- src/hooks/useActionFeedback.ts:52: kept as let with eslint-disable
comment (closure assigned after toast definition uses it — refactor
would need significant restructure)
**no-empty (4):** added /* intentionally empty */ comments
- src/components/security/RateLimitRealtimeAlerts.tsx:83
- src/features/connections/hooks/useConnectionsManager.ts:164
- src/hooks/useThemeAudit.ts:59
- src/lib/evolutionDirectClient.ts:18
**no-case-declarations (3):** wrapped case body in braces
- src/components/contacts/ContactsRichView.tsx:125 (case 'f')
- src/hooks/useCSAT.ts:31 (case 'week')
- src/hooks/useCSAT.ts:35 (case 'month')
Validations:
- bun run build → ✓ built in 60s
- bun run lint → 143 errors (was 164), 1330 warnings (was 1332)
Backlog remaining for Onda 5:
- PR 5.2: 14 no-explicit-any + 10 no-unused-expressions + ~6 outros
- PR 5.3: 108 no-restricted-imports (DDD architecture refactor)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughRemove ChangesLimpeza de Configuração e Padronização Técnica
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Reduz a contagem de erros do ESLint através de correções mecânicas de baixo risco (ex.: no-useless-escape, prefer-const, no-empty, no-case-declarations) e remove uma config Tailwind ESLint órfã que quebrava o parsing.
Changes:
- Remoção de
.eslintrc.tailwind.js(arquivo órfão com sintaxe TS em.js). - Simplificação de regex/char classes para resolver
no-useless-escapee pequenos ajustes de escopo (no-case-declarations) eprefer-const. - Marcação de
catch {}intencionalmente vazios com comentário para satisfazerno-empty.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/admin/AdminStressTestPage.tsx | prefer-const ao derivar messageId do resultado do envio |
| src/lib/security.ts | Ajuste em regex de validação de JID removendo escape desnecessário |
| src/lib/evolutionDirectClient.ts | Marca catch vazio como intencional (no-empty) |
| src/lib/csvUtils.ts | Ajuste em regex de sanitização de filename (atenção: risco de regex inválida) |
| src/hooks/useThemeAudit.ts | Marca catch vazio como intencional (no-empty) |
| src/hooks/useCSAT.ts | Adiciona escopo em case para evitar no-case-declarations |
| src/hooks/useActionFeedback.ts | Documenta exceção de prefer-const para variável atribuída após definição do toast |
| src/features/inbox/hooks/useSipClient.ts | Simplifica char class de regex para normalizar telefone (no-useless-escape) |
| src/features/inbox/hooks/useScheduledMediaUpload.ts | prefer-const ao obter signed URL (mediaUrl) |
| src/features/inbox/components/linkPreviewUtils.ts | Simplifica escapes em regex de URL (no-useless-escape) |
| src/features/inbox/components/chat/MarkdownPreview.tsx | Simplifica regex de negação em char class (no-useless-escape) |
| src/features/connections/hooks/useConnectionsManager.ts | Marca catch vazio como intencional (no-empty) |
| src/features/auth/components/PasswordStrengthMeter.tsx | Simplifica escapes em regex de caracteres especiais (no-useless-escape) |
| src/components/security/RateLimitRealtimeAlerts.tsx | Marca catch vazio como intencional (no-empty) |
| src/components/contacts/ContactsRichView.tsx | Adiciona bloco em case para evitar no-case-declarations |
| scripts/ds-config.ts | Remove escape desnecessário em lookahead (no-useless-escape) |
| .eslintrc.tailwind.js | Removido (config órfã que causava erro de parsing) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const parts = [prefix, suffix, date].filter(Boolean).join('-'); | ||
| // Strip unsafe filename chars | ||
| return parts.replace(/[^a-zA-Z0-9_\-\.]/g, '_') + '.csv'; | ||
| return parts.replace(/[^a-zA-Z0-9_\-.]/g, '_') + '.csv'; |
| return new Promise((resolve) => { | ||
| const { description, undoDuration = 5000, onUndo, onConfirm } = options; | ||
| let undone = false; | ||
| // eslint-disable-next-line prefer-const -- closure: assigned at line 57 after toast definition uses it |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/features/inbox/hooks/useScheduledMediaUpload.ts (1)
47-52:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFalha silenciosa ao gerar signed URL.
Se
createSignedUrlretornar erro, o código segue commediaUrlindefinida sem notificar o usuário. Isso quebra a promessa de tratamento explícito de erro e pode agendar mídia sem URL válida.💡 Sugestão de correção
- const { data: signedData , error } = await supabase.storage + const { data: signedData, error: signedUrlError } = await supabase.storage .from('whatsapp-media') .createSignedUrl(fileName, 604800); - const mediaUrl = signedData?.signedUrl; + if (signedUrlError || !signedData?.signedUrl) { + log.error('Scheduled media signed URL generation failed:', signedUrlError); + toast({ + title: 'Erro ao preparar anexo', + description: `Upload concluído, mas não foi possível gerar URL temporária para "${attachment.name}". A mensagem será agendada sem o anexo.`, + variant: 'destructive', + }); + return { mediaUrl: undefined, messageType: 'text' }; + } + + const mediaUrl = signedData.signedUrl;🤖 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/features/inbox/hooks/useScheduledMediaUpload.ts` around lines 47 - 52, The code in useScheduledMediaUpload calls supabase.storage.from('whatsapp-media').createSignedUrl and then proceeds using signedData.signedUrl without checking error or undefined, which can silently schedule media with no URL; update the logic in useScheduledMediaUpload to check the returned error and signedData (and mediaUrl) after createSignedUrl, handle failures by returning or throwing an explicit error (or invoking the existing user-notification/error handler), and prevent scheduling when mediaUrl is falsy; reference createSignedUrl, signedData, error, mediaUrl and the hook useScheduledMediaUpload when making the fix.
🤖 Prompt for all review comments with 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.
Inline comments:
In `@src/features/inbox/hooks/useSipClient.ts`:
- Around line 44-45: The phone sanitization in useSipClient.ts currently uses
phone.replace(/[\s\-()]/g, '') which still allows special characters into the
PostgREST .or() filter and can enable injection; change the cleaning to remove
all non-digits (usePhone -> create n from phone by stripping using a non-digit
regex) so the value passed to supabase.from('contacts').select(...).or(...) is
only digits, and fail/return early if result is empty; also fix the analogous
issue in useNewConversation.ts where searchQuery is interpolated into
.or(`name.ilike.%${searchQuery}%,phone.ilike.%${searchQuery}%`) by
validating/sanitizing searchQuery (either allow a safe subset of characters or
escape/normalize percent/quote characters, or reject unsafe input) before
building the query.
In `@src/hooks/useActionFeedback.ts`:
- Around line 52-53: The timeoutId variable in useActionFeedback is typed as
NodeJS.Timeout which couples to `@types/node`; change its type to
ReturnType<typeof setTimeout> to match the project's cross-runtime pattern (as
used in src/lib/debounce.ts and src/pages/VerifyEmail.tsx). Update the
declaration of timeoutId (and any related setTimeout/clearTimeout uses inside
the useActionFeedback hook) to use ReturnType<typeof setTimeout> and remove any
NodeJS-specific typing so the hook remains runtime-agnostic.
---
Outside diff comments:
In `@src/features/inbox/hooks/useScheduledMediaUpload.ts`:
- Around line 47-52: The code in useScheduledMediaUpload calls
supabase.storage.from('whatsapp-media').createSignedUrl and then proceeds using
signedData.signedUrl without checking error or undefined, which can silently
schedule media with no URL; update the logic in useScheduledMediaUpload to check
the returned error and signedData (and mediaUrl) after createSignedUrl, handle
failures by returning or throwing an explicit error (or invoking the existing
user-notification/error handler), and prevent scheduling when mediaUrl is falsy;
reference createSignedUrl, signedData, error, mediaUrl and the hook
useScheduledMediaUpload when making the fix.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a21aefb0-df2a-4842-9b9a-ae94783892a8
📒 Files selected for processing (17)
.eslintrc.tailwind.jsscripts/ds-config.tssrc/components/contacts/ContactsRichView.tsxsrc/components/security/RateLimitRealtimeAlerts.tsxsrc/features/auth/components/PasswordStrengthMeter.tsxsrc/features/connections/hooks/useConnectionsManager.tssrc/features/inbox/components/chat/MarkdownPreview.tsxsrc/features/inbox/components/linkPreviewUtils.tssrc/features/inbox/hooks/useScheduledMediaUpload.tssrc/features/inbox/hooks/useSipClient.tssrc/hooks/useActionFeedback.tssrc/hooks/useCSAT.tssrc/hooks/useThemeAudit.tssrc/lib/csvUtils.tssrc/lib/evolutionDirectClient.tssrc/lib/security.tssrc/pages/admin/AdminStressTestPage.tsx
💤 Files with no reviewable changes (1)
- .eslintrc.tailwind.js
| const n = phone.replace(/[\s\-()]/g, ''); | ||
| const { data, error } = await supabase.from('contacts').select('id').or(`phone.eq.${n},phone.eq.+${n},phone.ilike.%${n.slice(-8)}%`).limit(1).maybeSingle(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verifica onde a string .or(...) está sendo montada com interpolação direta
rg -nP --type=ts -C3 '\.or\(`phone\.eq\.\$\{n\},phone\.eq\.\+\$\{n\},phone\.ilike\.\%\$\{n\.slice\(-8\)\}\%`\)'
rg -nP --type=ts -C3 '\bfindContactByPhone\s*\('Repository: adm01-debug/zapp-web
Length of output: 1480
🏁 Script executed:
# Lê o arquivo completo para entender o contexto
cat -n src/features/inbox/hooks/useSipClient.ts | head -80Repository: adm01-debug/zapp-web
Length of output: 4749
🏁 Script executed:
# Procura por outras instâncias de phone normalization ou queries diretas ao Supabase
rg -n 'phone\s*=' src/features/inbox/hooks/useSipClient.ts -A 2Repository: adm01-debug/zapp-web
Length of output: 46
🏁 Script executed:
# Verifica se há migrations ou schema que defina o tipo do campo phone
fd -e sql -e ts | xargs rg -l 'contacts.*phone' | head -5Repository: adm01-debug/zapp-web
Length of output: 315
🏁 Script executed:
# Procura por outros usos de .or() com interpolação de strings
rg -n '\.or\(`' src/features/inbox/hooks/ -A 1Repository: adm01-debug/zapp-web
Length of output: 1226
🏁 Script executed:
# Procura por migrations que definem o campo phone
fd -e sql | xargs grep -l 'phone' 2>/dev/null | head -10Repository: adm01-debug/zapp-web
Length of output: 824
🏁 Script executed:
# Verifica policies de RLS para contacts
fd -e sql | xargs grep -A 3 'policy.*contacts' 2>/dev/null | head -20Repository: adm01-debug/zapp-web
Length of output: 2142
🏁 Script executed:
# Verifica se há sanitização em useNewConversation que também tem o mesmo padrão
sed -n '40,60p' src/features/inbox/hooks/useNewConversation.tsRepository: adm01-debug/zapp-web
Length of output: 765
🏁 Script executed:
# Procura pela definição mais recente do campo phone nas migrations
rg -A 5 'CREATE TABLE.*contacts|ALTER TABLE.*contacts' supabase/migrations/ | head -40Repository: adm01-debug/zapp-web
Length of output: 4262
🏁 Script executed:
# Verifica o tipo do campo phone
rg 'phone\s+(text|varchar|character|numeric|bigint)' supabase/migrations/Repository: adm01-debug/zapp-web
Length of output: 1293
🏁 Script executed:
# Verifica se há função ou trigger que normaliza phone
rg -i 'normalize.*phone|phone.*format' supabase/Repository: adm01-debug/zapp-web
Length of output: 5370
Sanitização insuficiente permite injeção de filtro PostgREST
O regex atual /[\s\-()]/g deixa passar caracteres especiais (vírgula, aspas, ponto, etc.) que quebram a sintaxe do filtro .or(). Uma entrada como 123",phone.eq.456,phone.ilike.% passa intacta, corrompendo a query.
Use /\D/g para manter apenas dígitos (números de telefone só precisam disso):
- const n = phone.replace(/[\s\-()]/g, '');
+ const n = phone.replace(/\D/g, '');
+ if (!n) return null;
const { data, error } = await supabase.from('contacts').select('id').or(`phone.eq.${n},phone.eq.+${n},phone.ilike.%${n.slice(-8)}%`).limit(1).maybeSingle();useNewConversation.ts:51: searchQuery não tem sanitização e passa direto para .or(\name.ilike.%${searchQuery}%,phone.ilike.%${searchQuery}%`)`. Adicione validação lá também.
📝 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.
| const n = phone.replace(/[\s\-()]/g, ''); | |
| const { data, error } = await supabase.from('contacts').select('id').or(`phone.eq.${n},phone.eq.+${n},phone.ilike.%${n.slice(-8)}%`).limit(1).maybeSingle(); | |
| const n = phone.replace(/\D/g, ''); | |
| if (!n) return null; | |
| const { data, error } = await supabase.from('contacts').select('id').or(`phone.eq.${n},phone.eq.+${n},phone.ilike.%${n.slice(-8)}%`).limit(1).maybeSingle(); |
🤖 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/features/inbox/hooks/useSipClient.ts` around lines 44 - 45, The phone
sanitization in useSipClient.ts currently uses phone.replace(/[\s\-()]/g, '')
which still allows special characters into the PostgREST .or() filter and can
enable injection; change the cleaning to remove all non-digits (usePhone ->
create n from phone by stripping using a non-digit regex) so the value passed to
supabase.from('contacts').select(...).or(...) is only digits, and fail/return
early if result is empty; also fix the analogous issue in useNewConversation.ts
where searchQuery is interpolated into
.or(`name.ilike.%${searchQuery}%,phone.ilike.%${searchQuery}%`) by
validating/sanitizing searchQuery (either allow a safe subset of characters or
escape/normalize percent/quote characters, or reject unsafe input) before
building the query.
| // eslint-disable-next-line prefer-const -- closure: assigned at line 57 after toast definition uses it | ||
| let timeoutId: NodeJS.Timeout; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verifica se o projeto usa NodeJS.Timeout em código de frontend e como o TS está configurado.
echo "== Procurando tsconfig* =="
fd -i '^tsconfig.*\.json$'
echo
echo "== libs/types nos tsconfig* =="
fd -i '^tsconfig.*\.json$' -x sh -c 'echo "--- $1"; sed -n "1,220p" "$1" | rg -n "\"lib\"|\"types\"|dom|node" -n -C2' sh {}
echo
echo "== Ocorrências de NodeJS.Timeout em src =="
rg -n --type ts --type tsx '\bNodeJS\.Timeout\b' src
echo
echo "== Padrão recomendado já existente (ReturnType<typeof setTimeout>) =="
rg -n --type ts --type tsx 'ReturnType<\s*typeof\s+setTimeout\s*>' srcRepository: adm01-debug/zapp-web
Length of output: 856
🏁 Script executed:
cat -n src/hooks/useActionFeedback.ts | sed -n '48,62p'Repository: adm01-debug/zapp-web
Length of output: 1153
🏁 Script executed:
#!/bin/bash
echo "== Buscando setTimeout/setInterval/clearTimeout em src =="
rg '(setTimeout|setInterval|clearTimeout|clearInterval)' src -B2 -A2 --no-heading | head -100
echo
echo "== Procurando tipagens de timeout em src =="
rg 'Timeout|ReturnType.*setTimeout' src -n --no-heading | head -50Repository: adm01-debug/zapp-web
Length of output: 10241
Padronize tipagem de timeout com ReturnType<typeof setTimeout> (linha 53)
O projeto estabelece padrão em src/lib/debounce.ts e src/pages/VerifyEmail.tsx usando ReturnType<typeof setTimeout>. Alinhando useActionFeedback.ts mantém consistência de tipagem cross-runtime sem acoplamento a @types/node em ambiente web.
- let timeoutId: NodeJS.Timeout;
+ let timeoutId: ReturnType<typeof setTimeout>;📝 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.
| // eslint-disable-next-line prefer-const -- closure: assigned at line 57 after toast definition uses it | |
| let timeoutId: NodeJS.Timeout; | |
| // eslint-disable-next-line prefer-const -- closure: assigned at line 57 after toast definition uses it | |
| let timeoutId: ReturnType<typeof setTimeout>; |
🤖 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/hooks/useActionFeedback.ts` around lines 52 - 53, The timeoutId variable
in useActionFeedback is typed as NodeJS.Timeout which couples to `@types/node`;
change its type to ReturnType<typeof setTimeout> to match the project's
cross-runtime pattern (as used in src/lib/debounce.ts and
src/pages/VerifyEmail.tsx). Update the declaration of timeoutId (and any related
setTimeout/clearTimeout uses inside the useActionFeedback hook) to use
ReturnType<typeof setTimeout> and remove any NodeJS-specific typing so the hook
remains runtime-agnostic.
Two micro-fixes after PR review (no scope creep):
1. csvUtils.ts:154 — normalize regex char class
- Before: /[^a-zA-Z0-9_\-.]/g (dash escaped between _ and .)
- After: /[^a-zA-Z0-9_.-]/g (dash at end, unambiguous)
- Validated: still produces same output for all test cases.
- Copilot flagged this as potential SyntaxError risk; runtime test
confirmed escaped form was valid, but the unescaped/end-position
form is the cleaner convention.
2. useActionFeedback.ts:52 — eslint-disable comment
- Removed hardcoded 'line 57' reference (drifts as file changes).
- New comment describes intent: 'timeoutId is referenced inside
toast action.onClick (declared above) and only assigned later
by setTimeout'.
Skipped review feedback (out of scope for Onda 5 PR 5.1):
- useSipClient.ts: phone sanitization PostgREST injection risk
→ Backlog Onda 5 / security pass (escape rule was the only thing
this PR was supposed to touch on this file).
- useScheduledMediaUpload.ts: createSignedUrl error not handled
→ Pre-existing bug, outside scope. Backlog as separate fix.
- useActionFeedback.ts: NodeJS.Timeout typing
→ Tangential, can ride a future PR.
Validations:
- bun run lint → 143 errors, 1330 warnings (unchanged from previous
commit, both fixes were within already-touched lines)
- regex tested in isolation: produces identical output
|
@coderabbitai full review Address comments aplicados em commit
Skipped feedbacks documentados em /workspace/notes/backlog-pr109-review-feedback.md (out of scope: useSipClient phone injection é pre-existing, useScheduledMediaUpload error handling é pre-existing bug, NodeJS.Timeout é nit cosmético). |
|
Fixes confirmados:
Itens de backlog registrados (fora do escopo deste PR):
Iniciando revisão completa agora... ✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/security.ts (1)
102-102:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPipeline bloqueado por controle de caracteres no regex.
O ESLint está falhando com
no-control-regexna linha 102 porque o regex inclui explicitamente caracteres de controle (\u0000-\u001F). Como o objetivo da função é intencionalmente remover esses caracteres (conforme comentário linha 100), o correto é adicionar uma supressão ESLint com justificativa.🛠️ Supressão sugerida
// Remove zero-width characters, control chars, and direction overrides + // eslint-disable-next-line no-control-regex -- intentionally matching control chars to remove them const cleaned = name .replace(/[\u200B-\u200F\u202A-\u202E\uFEFF\u0000-\u001F\u007F-\u009F]/g, '') .trim();🤖 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/security.ts` at line 102, Add an ESLint suppression for the no-control-regex rule immediately above the .replace(/[\u200B-\u200F\u202A-\u202E\uFEFF\u0000-\u001F\u007F-\u009F]/g, '') call to acknowledge the intentional removal of control characters (per the comment around line 100); use an inline eslint-disable-next-line no-control-regex with a short justification referencing the deliberate stripping of control characters so the regex stays unchanged and the linter no longer fails.
♻️ Duplicate comments (1)
src/features/inbox/hooks/useSipClient.ts (1)
44-45:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winSanitização ainda permite manipular o filtro
.or()(issue já reportada).Em Line 44, remover só espaço/hífen/parênteses não impede caracteres que quebram/alteram o filtro interpolado em Line 45. Restrinja para dígitos e retorne cedo se vazio.
Patch mínimo sugerido
- const n = phone.replace(/[\s\-()]/g, ''); + const n = phone.replace(/\D/g, ''); + if (!n) return null; const { data, error } = await supabase.from('contacts').select('id').or(`phone.eq.${n},phone.eq.+${n},phone.ilike.%${n.slice(-8)}%`).limit(1).maybeSingle();#!/bin/bash set -euo pipefail # Verifica a sanitização atual e a interpolação no filtro do Supabase rg -n --type=ts -C2 'const n = phone\.replace\(' src/features/inbox/hooks/useSipClient.ts rg -n --type=ts -C2 '\.or\(`phone\.eq\.\$\{n\}' src/features/inbox/hooks/useSipClient.ts # Resultado esperado após correção: # - Sanitização com /\D/g # - Guard clause: if (!n) return null;🤖 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/features/inbox/hooks/useSipClient.ts` around lines 44 - 45, The current sanitization for the phone variable (const n = phone.replace(/[\s\-()]/g, '')) still allows non-digit chars that can tamper with the interpolated .or(...) filter; change the sanitization to strip all non-digits (use phone.replace(/\D/g, '')) and add a guard clause (e.g., if (!n) return null or handle early) before calling supabase.from('contacts').select(...).maybeSingle() so the .or(...) interpolation only receives a purely numeric, non-empty string.
🤖 Prompt for all review comments with 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.
Outside diff comments:
In `@src/lib/security.ts`:
- Line 102: Add an ESLint suppression for the no-control-regex rule immediately
above the
.replace(/[\u200B-\u200F\u202A-\u202E\uFEFF\u0000-\u001F\u007F-\u009F]/g, '')
call to acknowledge the intentional removal of control characters (per the
comment around line 100); use an inline eslint-disable-next-line
no-control-regex with a short justification referencing the deliberate stripping
of control characters so the regex stays unchanged and the linter no longer
fails.
---
Duplicate comments:
In `@src/features/inbox/hooks/useSipClient.ts`:
- Around line 44-45: The current sanitization for the phone variable (const n =
phone.replace(/[\s\-()]/g, '')) still allows non-digit chars that can tamper
with the interpolated .or(...) filter; change the sanitization to strip all
non-digits (use phone.replace(/\D/g, '')) and add a guard clause (e.g., if (!n)
return null or handle early) before calling
supabase.from('contacts').select(...).maybeSingle() so the .or(...)
interpolation only receives a purely numeric, non-empty string.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 074d6104-fbd9-4243-9b3a-b35c8617bea2
📒 Files selected for processing (17)
.eslintrc.tailwind.jsscripts/ds-config.tssrc/components/contacts/ContactsRichView.tsxsrc/components/security/RateLimitRealtimeAlerts.tsxsrc/features/auth/components/PasswordStrengthMeter.tsxsrc/features/connections/hooks/useConnectionsManager.tssrc/features/inbox/components/chat/MarkdownPreview.tsxsrc/features/inbox/components/linkPreviewUtils.tssrc/features/inbox/hooks/useScheduledMediaUpload.tssrc/features/inbox/hooks/useSipClient.tssrc/hooks/useActionFeedback.tssrc/hooks/useCSAT.tssrc/hooks/useThemeAudit.tssrc/lib/csvUtils.tssrc/lib/evolutionDirectClient.tssrc/lib/security.tssrc/pages/admin/AdminStressTestPage.tsx
💤 Files with no reviewable changes (1)
- .eslintrc.tailwind.js
CodeRabbit's 2nd review caught a pre-existing no-control-regex error on src/lib/security.ts:102 (outside diff but blocks CI). The regex INTENTIONALLY matches control characters (U+0000-001F, U+007F-009F) to strip them from display names — exactly what's documented in the comment on line 100. Adding ESLint suppression with justification is the correct fix. Now 142 errors (was 143), no-control-regex zeroed.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with 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.
Inline comments:
In `@src/lib/security.ts`:
- Line 116: A constante jidPattern aceita strings sem dígitos porque permite
sequências de hífens; atualize jidPattern (a constante jidPattern) para exigir
explicitamente uma sequência inicial de um ou mais dígitos, opcionalmente
seguida de um hífen e outra sequência de um ou mais dígitos (ou seja, "digits"
ou "digits-hyphen-digits"), antes do @ e do sufixo (s.whatsapp.net | g.us | lid
| newsletter), para impedir entradas como "---@s.whatsapp.net".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 22eb459e-972b-4602-a990-7151b94422b2
📒 Files selected for processing (1)
src/lib/security.ts
| // Individual: 5511999999999@s.whatsapp.net | ||
| // Group: 120363XXXX@g.us | ||
| const jidPattern = /^[\d\-]+@(s\.whatsapp\.net|g\.us|lid|newsletter)$/; | ||
| const jidPattern = /^[\d-]+@(s\.whatsapp\.net|g\.us|lid|newsletter)$/; |
There was a problem hiding this comment.
Regex de JID permite entradas inválidas sem dígitos.
Com ^[\d-]+...$, valores como ---@s.whatsapp.net passam na validação, embora o formato esperado no próprio docstring exija dígitos (ou digits-timestamp para grupos). Isso pode deixar JID inválido seguir no fluxo.
Diff sugerido
- const jidPattern = /^[\d-]+@(s\.whatsapp\.net|g\.us|lid|newsletter)$/;
+ const jidPattern = /^(?:\d+@s\.whatsapp\.net|\d+-\d+@g\.us|\d+@(?:lid|newsletter))$/;📝 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.
| const jidPattern = /^[\d-]+@(s\.whatsapp\.net|g\.us|lid|newsletter)$/; | |
| const jidPattern = /^(?:\d+@s\.whatsapp\.net|\d+-\d+@g\.us|\d+@(?:lid|newsletter))$/; |
🤖 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/security.ts` at line 116, A constante jidPattern aceita strings sem
dígitos porque permite sequências de hífens; atualize jidPattern (a constante
jidPattern) para exigir explicitamente uma sequência inicial de um ou mais
dígitos, opcionalmente seguida de um hífen e outra sequência de um ou mais
dígitos (ou seja, "digits" ou "digits-hyphen-digits"), antes do @ e do sufixo
(s.whatsapp.net | g.us | lid | newsletter), para impedir entradas como
"---@s.whatsapp.net".
🎉 Fecha Onda 5: 108 → 0 errors (-108). ESLint TOTAL: 164 → 0 errors em 4 PRs (#109, #110, #111, este). ## Estratégia DDD Regra `no-restricted-imports` (eslint.config.js) bloqueia: 1. Imports cross-feature deep (`@/features/X/...`) — força entry points 2. Imports relativos profundos (`../../...`) 3. INTRA-feature via alias (`@/features/X/Y` quando arquivo está em X) ## Batch A — INTRA-feature (106 fixes em 42 arquivos) Pattern: `@/features/X/Y/Z` → caminho relativo via path.relative() Ex (src/features/inbox/components/chat/ChatHeader.tsx): - ANTES: `@/features/inbox/components/ai-tools/VisionIcon` - DEPOIS: `../ai-tools/VisionIcon` Distribuição: - 92 inbox (52 chat, 12 contact-details, 8 components, 5 hooks, 3 services, 3 virtualized, 3 conversation-list, 2 realtime, 2 templates, 2 search, 1 monitoring, 1 mocks) - 7 auth (5 components, 1 hooks, 1 context) - 3 admin (2 hooks, 1 services) - 3 connections (2 hooks, 1 services) - 1 sla (1 components) Script: /tmp/fix-intra.mjs (Node + path.relative) - 106/106 sucesso, zero falhas - 1 read/write por arquivo (eficiente) - Ordem desc por linha (não invalida índices) ## Batch B — CROSS-feature (2 fixes) Imports de outra feature DEVEM usar entry point (`@/features/X`), não path interno (`@/features/X/hooks/...`). Os barrels já existiam (`auth/index.ts` e `sla/index.ts` fazem `export * from './hooks'`), só não estavam sendo usados: - src/features/inbox/components/ChatPanel.tsx: `@/features/auth/hooks/useUserRole` → `@/features/auth` - src/features/inbox/components/RealtimeInboxView.tsx: `@/features/sla/hooks/useSLAAlerts` → `@/features/sla` ## Validação - bunx eslint . → 0 errors, 1330 warnings (Onda 5 ZERADA) - bunx tsc --noEmit → ✓ sem erros (typecheck pristine) - bun run build → ✓ built in 58.09s (zero regressões) - 44 files changed, 108 insertions(+), 108 deletions(-) ## Stress-test (10 cenários) ✓ path.relative gera `../foo` ou `./foo` corretamente ✓ Sem imports circulares (auth/sla não importam inbox) ✓ Comments inline preservados (replace só no string interno) ✓ BOM/CRLF tratados pelo Node ✓ index.ts implícito resolvido pelo TS path mapping ✓ Build não quebrou — tsconfig path mapping continua funcionando ✓ Typecheck sem regressão ✓ Sem sessão paralela (verificado via git reflog) ✓ Barrels já existiam, useUserRole/useSLAAlerts já re-exportados ✓ Eslint --fix NÃO usado (só explicit replace, sem side effects) ## Pós-Onda 5 Próximo do roadmap pré-deploy: - PR #108 (preserve/faxina) — decidir mergear ou fechar - 8 dependabots — aplicar recomendação A (fechar com TODO pós-deploy) - Fase B10 — patch 9 vulns npm - Fase B5 — ENV_SETUP doc - Fase C — Dockerfile + deploy zapp.atomicabr.com.br
🎉 Fecha Onda 5: 108 → 0 errors (-108). ESLint TOTAL: 164 → 0 errors em 4 PRs (#109, #110, #111, este). ## Estratégia DDD Regra `no-restricted-imports` (eslint.config.js) bloqueia: 1. Imports cross-feature deep (`@/features/X/...`) — força entry points 2. Imports relativos profundos (`../../...`) 3. INTRA-feature via alias (`@/features/X/Y` quando arquivo está em X) ## Batch A — INTRA-feature (106 fixes em 42 arquivos) Pattern: `@/features/X/Y/Z` → caminho relativo via path.relative() Ex (src/features/inbox/components/chat/ChatHeader.tsx): - ANTES: `@/features/inbox/components/ai-tools/VisionIcon` - DEPOIS: `../ai-tools/VisionIcon` Distribuição: - 92 inbox (52 chat, 12 contact-details, 8 components, 5 hooks, 3 services, 3 virtualized, 3 conversation-list, 2 realtime, 2 templates, 2 search, 1 monitoring, 1 mocks) - 7 auth (5 components, 1 hooks, 1 context) - 3 admin (2 hooks, 1 services) - 3 connections (2 hooks, 1 services) - 1 sla (1 components) Script: /tmp/fix-intra.mjs (Node + path.relative) - 106/106 sucesso, zero falhas - 1 read/write por arquivo (eficiente) - Ordem desc por linha (não invalida índices) ## Batch B — CROSS-feature (2 fixes) Imports de outra feature DEVEM usar entry point (`@/features/X`), não path interno (`@/features/X/hooks/...`). Os barrels já existiam (`auth/index.ts` e `sla/index.ts` fazem `export * from './hooks'`), só não estavam sendo usados: - src/features/inbox/components/ChatPanel.tsx: `@/features/auth/hooks/useUserRole` → `@/features/auth` - src/features/inbox/components/RealtimeInboxView.tsx: `@/features/sla/hooks/useSLAAlerts` → `@/features/sla` ## Validação - bunx eslint . → 0 errors, 1330 warnings (Onda 5 ZERADA) - bunx tsc --noEmit → ✓ sem erros (typecheck pristine) - bun run build → ✓ built in 58.09s (zero regressões) - 44 files changed, 108 insertions(+), 108 deletions(-) ## Stress-test (10 cenários) ✓ path.relative gera `../foo` ou `./foo` corretamente ✓ Sem imports circulares (auth/sla não importam inbox) ✓ Comments inline preservados (replace só no string interno) ✓ BOM/CRLF tratados pelo Node ✓ index.ts implícito resolvido pelo TS path mapping ✓ Build não quebrou — tsconfig path mapping continua funcionando ✓ Typecheck sem regressão ✓ Sem sessão paralela (verificado via git reflog) ✓ Barrels já existiam, useUserRole/useSLAAlerts já re-exportados ✓ Eslint --fix NÃO usado (só explicit replace, sem side effects) ## Pós-Onda 5 Próximo do roadmap pré-deploy: - PR #108 (preserve/faxina) — decidir mergear ou fechar - 8 dependabots — aplicar recomendação A (fechar com TODO pós-deploy) - Fase B10 — patch 9 vulns npm - Fase B5 — ENV_SETUP doc - Fase C — Dockerfile + deploy zapp.atomicabr.com.br
…09) (#113) Documenta o estado FINAL após: - Onda 5 completa (164 → 0 errors em 4 PRs: #109, #110, #111, #112) - 8 dependabots resolvidos (3 mergeados, 5 fechados, 1 preservation closed) Estado atual: - HEAD main: f1b3eab - ESLint: 0 errors, 1330 warnings - Branches remotas: 2 (main + preserve) - PRs OPEN: 0 Conteúdo do handoff (718 linhas): - TL;DR + estado atual com métricas - Quem é Joaquim e workflow - Histórico cronológico de TODOS os PRs (Schemas, Faxina pré-Onda, Ondas 1, 2, 5) - Faxina de branches (491 → 2) - Resolução técnica dos 8 dependabots - Decisões técnicas-chave (React 18, Vite reject, Sentry accept, etc) - 20 lições aprendidas - Catálogo de notes em /workspace/notes/ - Pendências pré-deploy (Backup Supabase, RabbitMQ 69GB, Drift, Deploy VPS, FX-DEP-02) - Backlogs de review skipped - Como retomar (instruções pra próximo Claude) - Apêndices: comandos úteis, infra VPS, Vercel, workflow Este doc serve como ponto de retomada pra próxima sessão Claude sem perder contexto.
Resumo
Reduz ESLint errors de 164 → 143 (-21, -13%) corrigindo as categorias de baixo risco.
Mudanças
🗑️ Deletado:
.eslintrc.tailwind.jsArquivo órfão com sintaxe TypeScript (
import type,Linter.Config,export default) em extensão.js. Não é referenciado emeslint.config.*,package.json, nem.github/. CausavaParsing error: Unexpected token {em todo lint run.🔧 no-useless-escape (10)
Removidas barras invertidas desnecessárias em regex char classes (onde
-,(,),*,+,.,[,/são literais):scripts/ds-config.ts:57(negative lookahead)src/features/auth/components/PasswordStrengthMeter.tsx:22(\[\/)src/features/inbox/components/chat/MarkdownPreview.tsx:19(\*)src/features/inbox/components/linkPreviewUtils.ts:2(2x\+)src/features/inbox/hooks/useSipClient.ts:44(\(\))src/lib/csvUtils.ts:154(\.)src/lib/security.ts:115(\-)🔧 prefer-const (3)
let→constquando há apenas uma atribuição:src/features/inbox/hooks/useScheduledMediaUpload.ts: removida declaraçãolet mediaUrlsolta, inlinedconst mediaUrl = signedData?.signedUrlsrc/pages/admin/AdminStressTestPage.tsx: mesmo padrão pramessageIdsrc/hooks/useActionFeedback.ts:52: mantido comoletcom// eslint-disable-next-line prefer-const— closure assigned depois da definição do toast que usa a variável, refactor exigiria restruturação significativa🔧 no-empty (4)
Adicionado
/* intentionally empty */em catch blocks vazios intencionalmente:src/components/security/RateLimitRealtimeAlerts.tsx:83src/features/connections/hooks/useConnectionsManager.ts:164src/hooks/useThemeAudit.ts:59src/lib/evolutionDirectClient.ts:18🔧 no-case-declarations (3)
case 'X':envolvido emcase 'X': { ... }para criar escopo léxico:src/components/contacts/ContactsRichView.tsx:125(case'f')src/hooks/useCSAT.ts:31(case'week')src/hooks/useCSAT.ts:35(case'month')Validação
bun run build→ built in 60sbun run lint→ 143 errors (era 164)Backlog Onda 5 (próximos PRs)
no-explicit-any+ 10no-unused-expressions+ 6 outros (~30 errors)no-restricted-imports(DDD: 106 INTRA-feature codemod + 2 cross-feature barrel)Governance
Refs: backlog Onda 5 em /workspace/notes/
Summary by CodeRabbit
Chores
Refactor