PR (C+A): escrita falha LOUD + caminho REST nativo de escrita (Plano A)#531
Conversation
Rebase limpo sobre main atual (inclui #526 telemetria, #527 RLS, #530). - bridge.ts: WRITE_OPERATIONS + isWriteOperation() + WriteUnavailableError; escrita com bridge OFF / kill-switch / CORS agora LANCA em vez de retornar vazio. Leitura mantem vazio silencioso. Telemetria do #526 preservada (recordBridgeCall/recordKillSwitchHit) — recordCall roda ANTES do throw. - index.ts: exporta isWriteOperation + WriteUnavailableError. Apenas estes 2 arquivos (rest-native writes + testes ficam no #525).
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
WalkthroughA ponte de banco de dados externo agora classifica explicitamente operações de escrita e lança ChangesPrevenção de falhas silenciosas em escrita
Sequence Diagram(s)Nenhum diagrama necessário nesta seção. 🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
There was a problem hiding this comment.
Pull request overview
Converts silent no-op writes through the External DB bridge into loud, typed failures. With the kill-switch OFF (REST-native covers reads only), invokeExternalDb/invokeExternalDbDelete previously returned {records:[],count:0} for insert/update/delete/upsert/batch_insert, causing the UI to show success toasts without any persistence. This PR makes those paths throw a new WriteUnavailableError so callers surface real errors.
Changes:
- Adds
WriteUnavailableErrorclass andisWriteOperation()helper inbridge.ts. - Throws
WriteUnavailableErrorininvokeExternalDbfor write ops when bridge is OFF, onKillSwitchActiveError, and on CORS/network errors (telemetryrecordCall(false,...)still emitted before the throw). Reads keep their silent-empty behavior. - Replaces silent
returnininvokeExternalDbDeletewith the same loud throw for kill-switch and CORS/network paths. - Re-exports the new symbols from
src/lib/external-db/index.ts.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/lib/external-db/bridge.ts |
Adds WriteUnavailableError, isWriteOperation(), and throws on write-path no-ops in invokeExternalDb and invokeExternalDbDelete; telemetry preserved before throw. |
src/lib/external-db/index.ts |
Re-exports isWriteOperation and WriteUnavailableError from the bridge module. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…estes das 6 guardas + remap + LOUD
… remap EN→PT, fail LOUD; preserva telemetria #526
…-exato ao artefato validado)
…te-exato 32a30c5c)
…Write) + corrige bridge.test.ts (evita REST path no mock) — 23/23 verde
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.Mudanças
C — falha LOUD (
bridge.ts+index.ts):WriteUnavailableError(tipado) + helperisWriteOperation().invokeExternalDb: escrita com bridge OFF / kill-switch / CORS agora lança em vez de retornar vazio. SELECT mantém vazio silencioso (REST nativo é o caminho real).invokeExternalDbDelete: lança em vez dereturnsilencioso.index.tsexporta os novos símbolos.A — escrita REST nativo (
rest-native.ts+bridge.tsfast-path):executeRestNativeWrite/tryExecuteRestNativeWrite/isRestNativeWriteEligiblecom 6 guardas: (A1) auth delegada ao RLS; (A2)update/deletesem filtro/id proibido (anti-mutação em massa); (A3) sempre tabela BASE (WRITE_TABLE_ALIASES), nunca viewv_*_public; (A4).select()de volta (select-back vazio por RLS ainda é sucesso); (A5) remap EN→PT no payload e filtros (tecnicas_gravacao). Sem retry em escrita; erros (RLS negada/validação) PROPAGAM LOUD.invokeExternalDb/invokeExternalDbDeletechamam o fast-path de escrita antes da bridge; tabela/op não-elegível cai noWriteUnavailableErrorhonesto.Preserva a telemetria do #526 (
recordBridgeCall/recordKillSwitchHit, comrecordCallANTES do throw) e convive com #527 (RLS, já nomain) e #530.Validação local
tsc --noEmit: 0 erros (apenas 2 pré-existentes emnode_modules/@vitejs/plugin-react-swc, fora de escopo).vitest: 23/23 verde — 12 derest-native-write.test.ts(Plano A) + 11 debridge.test.ts(inclui os 2 antes falhando).git merge-treevsmainatual: 0 conflitos.Série
Este PR (C+A) → #527 (RLS) já mergeado → #528 (aposentar bridge, gated, só após soak).
Ordem de merge recomendada: #531 primeiro; #528 permanece gated (não-destrutivo). Atenção ao fail-open do kill-switch (deletar a linha re-habilita a bridge): rollback do Plano A =
UPDATE public.system_kill_switches SET enabled=true WHERE switch_name='edge_external_db_bridge'.