PR#1 (C): escrita falha LOUD em vez de no-op silencioso#524
PR#1 (C): escrita falha LOUD em vez de no-op silencioso#524adm01-debug wants to merge 3 commits into
Conversation
…cioso
Com a bridge OFF (REST nativo 100%) e o REST nativo cobrindo apenas SELECT,
toda operação de ESCRITA (insert/update/delete/upsert/batch_insert) roteada por
invokeExternalDb/invokeExternalDbSingle/invokeExternalDbDelete retornava vazio
silenciosamente — a UI reportava "salvo/excluído com sucesso" sem persistir nada
(corrupção silenciosa).
Mudanças:
- Novo WriteUnavailableError (tipado) + helper isWriteOperation().
- invokeExternalDb: escrita com bridge OFF ou erro CORS/kill-switch agora LANÇA
WriteUnavailableError em vez de retornar {records:[],count:0}. Leitura mantém
o retorno vazio silencioso (REST nativo é o caminho real).
- invokeExternalDbDelete: idem — lança em vez de return silencioso.
- Telemetria reportSilentEmpty('write_bridge_off') preservada antes do throw.
- Barrel index.ts exporta WriteUnavailableError e isWriteOperation.
Auditoria: os 12 callers de escrita estão todos em try/catch/useMutation com
toast.error(error.message) — o throw vira erro honesto e visível, sem rejeições
não-tratadas. Type-check (tsc --noEmit) limpo nos arquivos tocados.
Stopgap reversível enquanto PR#2/#3 (A: escrita REST nativo + RLS) reintroduzem
o caminho de escrita real.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 40 minutes and 31 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
* feat(external-db): PR#2 (A) — caminho de ESCRITA via PostgREST nativo Estende o rest-native para insert/update/delete/upsert/batch_insert, com as 6 guardas do Plano A: - A1 sessão autenticada → delegada ao RLS (supabase-js anexa o JWT). - A2 update/delete SEM filtro/id → proibido (proteção contra mutação em massa). - A3 escrita SEMPRE na tabela BASE (WRITE_TABLE_ALIASES), nunca na view v_*_public read-only — NÃO reusa TABLE_ALIASES de leitura. - A4 `.select()` de volta; insert OK com select-back vazio (RLS de SELECT) ainda é sucesso. - A5 remap EN→PT no payload e filtros (COLUMN_ALIASES_BY_TABLE), evita 400. - Sem retry em escrita (evita insert/upsert duplicado); erro real PROPAGA LOUD. Novos exports: isRestNativeWriteEligible, executeRestNativeWrite, tryExecuteRestNativeWrite. Whitelist de escrita = as 13 tabelas base usadas pelos 12 hooks de admin. Testes: src/lib/external-db/__tests__/rest-native-write.test.ts — 12 casos cobrindo todas as 6 guardas + elegibilidade + propagação LOUD + batch array remap. * feat(external-db): PR#2 (A) — fia o write fast-path no invokeExternalDb + delete - invokeExternalDb: novo WRITE fast-path (isRestNativeWriteEligible → tryExecuteRestNativeWrite) logo após o READ fast-path. Sucesso retorna; erro de RLS/validação PROPAGA LOUD; tabela/op não-elegível cai no WriteUnavailableError. - invokeExternalDbDelete: roteia REST nativo (RLS) primeiro; fallback bridge; senão LOUD. - bridge.test.ts: os 3 testes de contrato da bridge passam a usar tabela NÃO-whitelisted (audit_logs) — onde a bridge continua sendo o caminho — preservando a intenção (o caminho REST nativo é coberto por rest-native-write.test.ts). Também corrige 2 falhas pré-existentes no arquivo (string de acento + chave auth duplicada). tsc --noEmit limpo; vitest: bridge.test.ts + __tests__/ = 32/32 verdes. Depende do PR#3 (RLS WITH CHECK) para a escrita efetivamente persistir; sem ele, a escrita falha LOUD (honesto), nunca silenciosa.
There was a problem hiding this comment.
Pull request overview
This PR changes the external DB bridge write behavior so writes no longer degrade to silent empty/no-op results when the bridge is disabled or unreachable, while preserving read fallback behavior.
Changes:
- Adds
isWriteOperation()and typedWriteUnavailableError. - Throws
WriteUnavailableErrorfor write operations when bridge is OFF, kill-switched, or affected by CORS/network failures. - Re-exports the new helper/error from the external-db barrel.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/lib/external-db/bridge.ts |
Adds write-operation detection, typed write-unavailable errors, and loud failure paths for writes/deletes. |
src/lib/external-db/index.ts |
Exports the new bridge helper and error type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| `Escrita indisponível: '${operation}' em '${table}' não pôde ser persistida ` + | ||
| `(bridge OFF ou inacessível). Nenhum dado foi alterado.`, |
| * tenha sido persistido (corrupção silenciosa). Os callers (mutations / try-catch) | ||
| * já exibem `toast.error` a partir de `error.message`, então este erro tipado | ||
| * transforma o no-op silencioso em falha honesta e visível. |
|
|
||
| if (!bridgeEnabled) { | ||
| if (options.operation !== 'select') { | ||
| if (isWriteOperation(options.operation)) { |
…to do #524 O #526 (telemetria recordBridgeCall no caminho vivo) entrou no main e tocou bridge.ts nas mesmas regiões do PR#1. Este commit reescreve bridge.ts/index.ts com o conteúdo do main atual (toda a telemetria do #526 intacta) + os edits do PR#1 por cima, em regiões distintas: - WRITE_OPERATIONS + isWriteOperation + WriteUnavailableError (novos). - invokeExternalDb: escrita com bridge OFF agora THROW (após reportSilentEmpty + recordCall + recordKillSwitchHit do #526). Leitura segue vazia. - ramos KillSwitchActiveError/CORS: escrita THROW (telemetria preservada). - invokeExternalDbDelete: THROW em vez de no-op silencioso. - index.ts: exporta WriteUnavailableError e isWriteOperation. Resultado: as regiões do #526 ficam idênticas ao main → merge 3-way sem conflito. tsc --noEmit limpo. (As 2 falhas de bridge.test.ts são pré-existentes no main — mock sem logger.debug e asserção com acento — e são corrigidas no PR#2.)
…atual (#524) Rebase sobre o main atual (inclui #526, telemetria recordBridgeCall) para eliminar o conflito do #524. Branch recriada a partir do main + este \u00fanico commit. - WRITE_OPERATIONS + isWriteOperation + WriteUnavailableError (novos).\n- invokeExternalDb: escrita com bridge OFF/kill-switch/CORS agora THROW (ap\u00f3s reportSilentEmpty + recordCall + recordKillSwitchHit do #526); leitura segue retornando vazio.\n- invokeExternalDbDelete: THROW em vez de no-op silencioso.\n- index.ts: exporta WriteUnavailableError e isWriteOperation. Toda a telemetria do #526 preservada (7x recordCall). tsc --noEmit limpo. Escopo do PR#1 = apenas bridge.ts + index.ts (rest-native writes + testes ficam no PR#2/#525).
…E + testes Re-land do PR#2 (estava órfão após o rebase do #524/#529). rest-native.ts = main atual (telemetria #526 intacta) + seção WRITE (Plano A) anexada: executeRestNativeWrite/tryExecuteRestNativeWrite/isRestNativeWriteEligible com as 6 guardas (A1 RLS, A2 anti-mutação-em-massa, A3 tabela base nunca view, A4 select-back vazio=sucesso, A5 remap EN→PT, sem retry/LOUD). + rest-native-write.test.ts (12 casos).
Contexto
Bridge OFF (
edge_external_db_bridge:enabled=false, rollout=100→ REST nativo 100%) +rest-nativecobrindo só SELECT = toda ESCRITA (insert/update/delete/upsert/batch_insert) porinvokeExternalDb/invokeExternalDbSingle/invokeExternalDbDeleteretornava{records:[],count:0}silenciosamente. A UI dizia "salvo/excluído com sucesso" sem persistir — corrupção silenciosa.Simulação (73.728 cenários): estado ATUAL = 34.560 perdas silenciosas; com este PR (C) = 0, sem quebrar leitura (INV3=0) nem brecha de segurança (INV2=0).
Mudanças
WriteUnavailableError(tipado) + helperisWriteOperation().invokeExternalDb: escrita com bridge OFF / kill-switch / CORS agora lança em vez de retornar vazio. Leitura mantém vazio silencioso (REST nativo é o caminho real).invokeExternalDbDelete: lança em vez dereturnsilencioso.reportSilentEmpty('write_bridge_off')preservado antes do throw.index.tsexporta os novos símbolos.Auditoria
Os 12 callers de escrita estão todos em
try/catch/useMutationcomtoast.error(error.message)→ throw vira erro honesto, sem rejeições não-tratadas.Validação local
tsc --noEmit: 0 erros nos arquivos tocados. Draft proposital — aguardando CI (build Vite + testes).Série: #1 (C) → #2/#3 (A: escrita REST nativo + RLS) → #4 (aposentar bridge).
Summary by cubic
Escritas passam a usar REST nativo (PostgREST + RLS) nas tabelas whitelisted e, quando não for possível, falham em voz alta em vez de no-op silencioso — eliminando “salvo/excluído com sucesso” sem persistência. Leituras continuam retornando vazio quando o caminho real é REST nativo.
New Features
invokeExternalDbeinvokeExternalDbDelete; se a tabela/op não for elegível, cai para a bridge; sem retry em escrita. Novos exports:isRestNativeWriteEligible,executeRestNativeWrite,tryExecuteRestNativeWrite. Testes cobrindo o Plano A.mainpreserva a telemetria do caminho vivo (recordBridgeCall) e registra hits de kill-switch (recordKillSwitchHit).Bug Fixes
WriteUnavailableErrore helperisWriteOperation.WriteUnavailableErrorem vez de retornar vazio;selectmantém retorno vazio.invokeExternalDbDeletedeixa de no-op silencioso e também lança. ExportaWriteUnavailableError/isWriteOperationemindex.ts.Written for commit 6637d48. Summary will update on new commits.