@luanpdd/kit-mcp 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +168 -168
- package/gates/agent-no-recursive-dispatch.md +82 -82
- package/kit/COMANDOS.md +138 -138
- package/kit/README.md +76 -76
- package/kit/agents/advisor-researcher.md +106 -106
- package/kit/agents/assumptions-analyzer.md +107 -107
- package/kit/agents/audit-log-implementer.md +313 -313
- package/kit/agents/auditor-consistencia-isolamento.md +413 -413
- package/kit/agents/b2b-saas-architect.md +156 -156
- package/kit/agents/cascading-failures-auditor.md +298 -298
- package/kit/agents/codebase-mapper.md +768 -768
- package/kit/agents/crm-pipeline-implementer.md +256 -256
- package/kit/agents/debugger.md +813 -813
- package/kit/agents/detector-tenant-quente.md +337 -337
- package/kit/agents/evolution-go-integrator.md +200 -200
- package/kit/agents/example-reviewer.md +21 -21
- package/kit/agents/executor.md +564 -564
- package/kit/agents/integration-checker.md +200 -200
- package/kit/agents/invite-flow-implementer.md +189 -189
- package/kit/agents/legacy-characterizer.md +368 -368
- package/kit/agents/lgpd-compliance-auditor.md +295 -295
- package/kit/agents/multi-tenant-isolation-auditor.md +253 -253
- package/kit/agents/multi-tenant-rls-writer.md +340 -340
- package/kit/agents/nyquist-auditor.md +178 -178
- package/kit/agents/observability-coverage-auditor.md +315 -315
- package/kit/agents/org-onboarding-implementer.md +223 -223
- package/kit/agents/payload-capture-instrumenter.md +273 -273
- package/kit/agents/phase-researcher.md +696 -696
- package/kit/agents/plan-checker.md +272 -272
- package/kit/agents/planner.md +922 -922
- package/kit/agents/project-researcher.md +652 -652
- package/kit/agents/refactor-safety-auditor.md +404 -404
- package/kit/agents/research-synthesizer.md +245 -245
- package/kit/agents/roadmapper.md +677 -677
- package/kit/agents/seam-finder.md +359 -359
- package/kit/agents/shotgun-surgery-detector.md +349 -349
- package/kit/agents/supabase-branching-architect.md +562 -562
- package/kit/agents/supabase-cicd-pipeline-implementer.md +777 -777
- package/kit/agents/supabase-column-privileges-writer.md +399 -399
- package/kit/agents/supabase-edge-fn-tester.md +287 -0
- package/kit/agents/supabase-edge-fn-writer.md +239 -210
- package/kit/agents/supabase-migration-writer.md +385 -385
- package/kit/agents/supabase-rbac-implementer.md +392 -392
- package/kit/agents/supabase-realtime-implementer.md +363 -267
- package/kit/agents/supabase-rls-hardener.md +521 -521
- package/kit/agents/supabase-rls-writer.md +323 -323
- package/kit/agents/supabase-roles-implementer.md +355 -355
- package/kit/agents/super-admin-implementer.md +281 -281
- package/kit/agents/ui-auditor.md +437 -437
- package/kit/agents/ui-checker.md +302 -302
- package/kit/agents/ui-researcher.md +355 -355
- package/kit/agents/user-profiler.md +175 -175
- package/kit/agents/validador-evolucao-schema.md +335 -335
- package/kit/agents/verifier.md +728 -728
- package/kit/commands/adicionar-backlog.md +75 -75
- package/kit/commands/adicionar-fase.md +42 -42
- package/kit/commands/adicionar-tarefa.md +45 -45
- package/kit/commands/adicionar-testes.md +41 -41
- package/kit/commands/ajuda.md +21 -21
- package/kit/commands/atualizar.md +37 -37
- package/kit/commands/auditar-cascading.md +111 -111
- package/kit/commands/auditar-marco.md +179 -179
- package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
- package/kit/commands/auditar-refactor.md +219 -219
- package/kit/commands/auditar-release.md +109 -109
- package/kit/commands/auditar-uat.md +23 -23
- package/kit/commands/autonomo.md +40 -40
- package/kit/commands/branch-pr.md +24 -24
- package/kit/commands/burn-rate-status.md +408 -408
- package/kit/commands/capturar-payloads.md +193 -193
- package/kit/commands/caracterizar.md +212 -212
- package/kit/commands/concluir-marco.md +247 -247
- package/kit/commands/configuracoes.md +36 -36
- package/kit/commands/dados-distribuidos.md +188 -188
- package/kit/commands/definir-perfil.md +10 -10
- package/kit/commands/depurar.md +190 -190
- package/kit/commands/detectar-duplicacao.md +197 -197
- package/kit/commands/discutir-fase.md +131 -131
- package/kit/commands/encontrar-seams.md +136 -136
- package/kit/commands/entrar-discord.md +17 -17
- package/kit/commands/estatisticas.md +18 -18
- package/kit/commands/example-greeting.md +33 -33
- package/kit/commands/executar-fase.md +58 -58
- package/kit/commands/expresso.md +56 -56
- package/kit/commands/fase-ui.md +34 -34
- package/kit/commands/fazer.md +57 -57
- package/kit/commands/fio.md +125 -125
- package/kit/commands/fluxos-trabalho.md +64 -64
- package/kit/commands/forense.md +176 -176
- package/kit/commands/gerenciador.md +38 -38
- package/kit/commands/inserir-fase.md +31 -31
- package/kit/commands/legacy.md +263 -263
- package/kit/commands/limpeza.md +17 -17
- package/kit/commands/listar-hipoteses-fase.md +45 -45
- package/kit/commands/listar-workspaces.md +18 -18
- package/kit/commands/load-shedding.md +117 -117
- package/kit/commands/mapear-codebase.md +70 -70
- package/kit/commands/multi-tenant.md +163 -163
- package/kit/commands/nota.md +33 -33
- package/kit/commands/novo-marco.md +43 -43
- package/kit/commands/novo-projeto.md +41 -41
- package/kit/commands/novo-workspace.md +43 -43
- package/kit/commands/pausar-trabalho.md +37 -37
- package/kit/commands/perfil-usuario.md +45 -45
- package/kit/commands/pesquisar-fase.md +195 -195
- package/kit/commands/planejar-fase.md +67 -67
- package/kit/commands/planejar-lacunas.md +33 -33
- package/kit/commands/plantar-ideia.md +25 -25
- package/kit/commands/progresso.md +24 -24
- package/kit/commands/proximo.md +30 -30
- package/kit/commands/publicar.md +490 -490
- package/kit/commands/rapido.md +35 -35
- package/kit/commands/reaplicar-patches.md +124 -124
- package/kit/commands/refactor-seguro.md +321 -321
- package/kit/commands/relatorio-sessao.md +19 -19
- package/kit/commands/remover-fase.md +31 -31
- package/kit/commands/remover-workspace.md +26 -26
- package/kit/commands/resumo-marco.md +50 -50
- package/kit/commands/retomar-trabalho.md +40 -40
- package/kit/commands/revisar-backlog.md +60 -60
- package/kit/commands/revisar-ui.md +32 -32
- package/kit/commands/revisar.md +37 -37
- package/kit/commands/saude.md +21 -21
- package/kit/commands/setup-notion.md +93 -93
- package/kit/commands/storytelling.md +179 -179
- package/kit/commands/supabase.md +30 -7
- package/kit/commands/sync-main.md +68 -68
- package/kit/commands/validar-fase.md +35 -35
- package/kit/commands/verificar-tarefas.md +44 -44
- package/kit/commands/verificar-trabalho.md +64 -64
- package/kit/file-manifest.json +14 -8
- package/kit/framework/bin/lib/commands.cjs +959 -959
- package/kit/framework/bin/lib/config.cjs +442 -442
- package/kit/framework/bin/lib/core.cjs +1230 -1230
- package/kit/framework/bin/lib/frontmatter.cjs +336 -336
- package/kit/framework/bin/lib/init.cjs +1442 -1442
- package/kit/framework/bin/lib/milestone.cjs +252 -252
- package/kit/framework/bin/lib/model-profiles.cjs +68 -68
- package/kit/framework/bin/lib/phase.cjs +888 -888
- package/kit/framework/bin/lib/profile-output.cjs +952 -952
- package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
- package/kit/framework/bin/lib/roadmap.cjs +329 -329
- package/kit/framework/bin/lib/security.cjs +382 -382
- package/kit/framework/bin/lib/state.cjs +1031 -1031
- package/kit/framework/bin/lib/template.cjs +222 -222
- package/kit/framework/bin/lib/uat.cjs +282 -282
- package/kit/framework/bin/lib/verify.cjs +888 -888
- package/kit/framework/bin/lib/workstream.cjs +491 -491
- package/kit/framework/bin/tools.cjs +918 -918
- package/kit/framework/commands/workstreams.md +63 -63
- package/kit/framework/references/checkpoints.md +778 -778
- package/kit/framework/references/continuation-format.md +249 -249
- package/kit/framework/references/decimal-phase-calculation.md +64 -64
- package/kit/framework/references/git-integration.md +295 -295
- package/kit/framework/references/git-planning-commit.md +38 -38
- package/kit/framework/references/model-profile-resolution.md +36 -36
- package/kit/framework/references/model-profiles.md +139 -139
- package/kit/framework/references/phase-argument-parsing.md +61 -61
- package/kit/framework/references/planning-config.md +202 -202
- package/kit/framework/references/questioning.md +162 -162
- package/kit/framework/references/tdd.md +263 -263
- package/kit/framework/references/ui-brand.md +160 -160
- package/kit/framework/references/user-profiling.md +657 -657
- package/kit/framework/references/verification-patterns.md +612 -612
- package/kit/framework/references/workstream-flag.md +58 -58
- package/kit/framework/templates/DEBUG.md +164 -164
- package/kit/framework/templates/UAT.md +265 -265
- package/kit/framework/templates/UI-SPEC.md +100 -100
- package/kit/framework/templates/VALIDATION.md +76 -76
- package/kit/framework/templates/claude-md.md +122 -122
- package/kit/framework/templates/codebase/architecture.md +185 -185
- package/kit/framework/templates/codebase/concerns.md +205 -205
- package/kit/framework/templates/codebase/conventions.md +204 -204
- package/kit/framework/templates/codebase/integrations.md +192 -192
- package/kit/framework/templates/codebase/stack.md +158 -158
- package/kit/framework/templates/codebase/structure.md +199 -199
- package/kit/framework/templates/codebase/testing.md +301 -301
- package/kit/framework/templates/config.json +44 -44
- package/kit/framework/templates/context.md +352 -352
- package/kit/framework/templates/continue-here.md +78 -78
- package/kit/framework/templates/copilot-instructions.md +7 -7
- package/kit/framework/templates/debug-subagent-prompt.md +91 -91
- package/kit/framework/templates/dev-preferences.md +20 -20
- package/kit/framework/templates/discovery.md +146 -146
- package/kit/framework/templates/discussion-log.md +63 -63
- package/kit/framework/templates/milestone-archive.md +123 -123
- package/kit/framework/templates/milestone.md +115 -115
- package/kit/framework/templates/phase-prompt.md +610 -610
- package/kit/framework/templates/planner-subagent-prompt.md +117 -117
- package/kit/framework/templates/project.md +186 -186
- package/kit/framework/templates/requirements.md +231 -231
- package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
- package/kit/framework/templates/research-project/FEATURES.md +147 -147
- package/kit/framework/templates/research-project/PITFALLS.md +200 -200
- package/kit/framework/templates/research-project/STACK.md +120 -120
- package/kit/framework/templates/research-project/SUMMARY.md +170 -170
- package/kit/framework/templates/research.md +419 -419
- package/kit/framework/templates/retrospective.md +54 -54
- package/kit/framework/templates/roadmap.md +202 -202
- package/kit/framework/templates/state.md +176 -176
- package/kit/framework/templates/summary-complex.md +59 -59
- package/kit/framework/templates/summary-minimal.md +41 -41
- package/kit/framework/templates/summary-standard.md +48 -48
- package/kit/framework/templates/summary.md +209 -209
- package/kit/framework/templates/user-profile.md +146 -146
- package/kit/framework/templates/user-setup.md +256 -256
- package/kit/framework/templates/verification-report.md +258 -258
- package/kit/framework/workflows/add-phase.md +112 -112
- package/kit/framework/workflows/add-tests.md +351 -351
- package/kit/framework/workflows/add-todo.md +158 -158
- package/kit/framework/workflows/audit-milestone.md +340 -340
- package/kit/framework/workflows/audit-uat.md +109 -109
- package/kit/framework/workflows/autonomous.md +891 -891
- package/kit/framework/workflows/check-todos.md +177 -177
- package/kit/framework/workflows/cleanup.md +152 -152
- package/kit/framework/workflows/complete-milestone.md +696 -696
- package/kit/framework/workflows/diagnose-issues.md +231 -231
- package/kit/framework/workflows/discovery-phase.md +289 -289
- package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
- package/kit/framework/workflows/discuss-phase.md +784 -784
- package/kit/framework/workflows/do.md +104 -104
- package/kit/framework/workflows/execute-phase.md +838 -838
- package/kit/framework/workflows/execute-plan.md +510 -510
- package/kit/framework/workflows/fast.md +102 -102
- package/kit/framework/workflows/forensics.md +265 -265
- package/kit/framework/workflows/health.md +181 -181
- package/kit/framework/workflows/help.md +619 -619
- package/kit/framework/workflows/insert-phase.md +130 -130
- package/kit/framework/workflows/list-phase-assumptions.md +178 -178
- package/kit/framework/workflows/list-workspaces.md +56 -56
- package/kit/framework/workflows/manager.md +362 -362
- package/kit/framework/workflows/map-codebase.md +377 -377
- package/kit/framework/workflows/milestone-summary.md +223 -223
- package/kit/framework/workflows/new-milestone.md +486 -486
- package/kit/framework/workflows/new-project.md +1159 -1159
- package/kit/framework/workflows/new-workspace.md +237 -237
- package/kit/framework/workflows/next.md +97 -97
- package/kit/framework/workflows/node-repair.md +92 -92
- package/kit/framework/workflows/note.md +156 -156
- package/kit/framework/workflows/pause-work.md +176 -176
- package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
- package/kit/framework/workflows/plan-phase.md +765 -765
- package/kit/framework/workflows/plant-seed.md +169 -169
- package/kit/framework/workflows/pr-branch.md +129 -129
- package/kit/framework/workflows/profile-user.md +450 -450
- package/kit/framework/workflows/progress.md +507 -507
- package/kit/framework/workflows/quick.md +757 -757
- package/kit/framework/workflows/remove-phase.md +155 -155
- package/kit/framework/workflows/remove-workspace.md +90 -90
- package/kit/framework/workflows/research-phase.md +82 -82
- package/kit/framework/workflows/resume-project.md +326 -326
- package/kit/framework/workflows/review.md +228 -228
- package/kit/framework/workflows/session-report.md +146 -146
- package/kit/framework/workflows/settings.md +283 -283
- package/kit/framework/workflows/ship.md +228 -228
- package/kit/framework/workflows/stats.md +60 -60
- package/kit/framework/workflows/transition.md +671 -671
- package/kit/framework/workflows/ui-phase.md +302 -302
- package/kit/framework/workflows/ui-review.md +165 -165
- package/kit/framework/workflows/update.md +323 -323
- package/kit/framework/workflows/validate-phase.md +174 -174
- package/kit/framework/workflows/verify-phase.md +252 -252
- package/kit/framework/workflows/verify-work.md +637 -637
- package/kit/hooks/check-update.js +118 -118
- package/kit/hooks/context-monitor.js +163 -163
- package/kit/hooks/prompt-guard.js +103 -103
- package/kit/hooks/statusline.js +125 -125
- package/kit/hooks/workflow-guard.js +101 -101
- package/kit/settings.json +45 -45
- package/kit/skills/_shared-supabase/glossary.md +17 -0
- package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
- package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
- package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
- package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
- package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
- package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
- package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
- package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
- package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
- package/kit/skills/example-skill/SKILL.md +42 -42
- package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
- package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
- package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
- package/kit/skills/legacy-extract-class/SKILL.md +203 -203
- package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
- package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
- package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
- package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
- package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
- package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
- package/kit/skills/member-invite-flow/SKILL.md +305 -305
- package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
- package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
- package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
- package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
- package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
- package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
- package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
- package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
- package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
- package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
- package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
- package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
- package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
- package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
- package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
- package/kit/skills/supabase-edge-functions/SKILL.md +229 -141
- package/kit/skills/supabase-edge-functions-auth/SKILL.md +309 -0
- package/kit/skills/supabase-edge-functions-limits/SKILL.md +302 -0
- package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +279 -0
- package/kit/skills/supabase-edge-functions-testing/SKILL.md +277 -0
- package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +357 -0
- package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
- package/kit/skills/supabase-migrations/SKILL.md +297 -297
- package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
- package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
- package/kit/skills/supabase-realtime/SKILL.md +460 -236
- package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
- package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
- package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
- package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
- package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
- package/package.json +1 -1
- package/src/cli/index.js +33 -0
- package/src/core/kit.js +216 -216
- package/src/core/reflect.js +247 -247
- package/src/core/reverse-sync.js +372 -372
- package/src/core/sync.js +418 -418
- package/src/core/watch.js +121 -121
- package/src/mcp-server/index.js +693 -490
- package/src/mcp-server/roots.js +124 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-edge-functions-auth
|
|
3
|
+
description: Use ao autenticar Edge Functions Supabase em 2026 — `@supabase/server` package, `withSupabase({auth: 'user'|'secret:name'|'publishable:name'|'none'})`, `createSupabaseContext` para erros customizados, distinção Authorization (JWT) vs apikey (API key), toggle `verify_jwt` por função em config.toml, padrões canônicos para webhooks/service-to-service/user-facing/public.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Supabase — Edge Functions Auth (`@supabase/server`) · 2026
|
|
7
|
+
|
|
8
|
+
## Quando usar
|
|
9
|
+
|
|
10
|
+
Carrega quando:
|
|
11
|
+
|
|
12
|
+
- "auth em Edge Function", "withSupabase", "createSupabaseContext"
|
|
13
|
+
- "verify_jwt true/false", "Authorization vs apikey", "401 edge function"
|
|
14
|
+
- "Stripe webhook auth", "service-to-service edge", "cron chamar edge function"
|
|
15
|
+
- "RBAC Edge Function", "user JWT no handler"
|
|
16
|
+
|
|
17
|
+
> Pré-requisito: [`supabase-edge-functions`](../supabase-edge-functions/SKILL.md) (Deno + env vars JSON dict).
|
|
18
|
+
> Complemento: [`supabase-edge-functions-limits`](../supabase-edge-functions-limits/SKILL.md) (status codes 401/403).
|
|
19
|
+
|
|
20
|
+
## Conceito-chave: 2 headers, 2 camadas
|
|
21
|
+
|
|
22
|
+
| Header | Valor | Para |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `Authorization` | `Bearer <user-jwt>` | Usuário logado via Supabase Auth |
|
|
25
|
+
| `apikey` | `sb_publishable_...` ou `sb_secret_...` | Cliente browser ou service-to-service |
|
|
26
|
+
|
|
27
|
+
**2 camadas de validação:**
|
|
28
|
+
|
|
29
|
+
1. **Platform-level (`verify_jwt`)** — plataforma valida JWT em `Authorization` antes do handler. Se header ausente/inválido → **401 não chega no código**.
|
|
30
|
+
2. **Handler-level** — você decide o que fazer com credencial recebida.
|
|
31
|
+
|
|
32
|
+
### Pegadinha canônica (#1 causa de 401)
|
|
33
|
+
|
|
34
|
+
Mandar API key (`sb_publishable_*` / `sb_secret_*`) como Bearer:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
⚠ Authorization: Bearer sb_publishable_abc123 → 401 (não é JWT)
|
|
38
|
+
✓ Authorization: Bearer eyJhbGciOiJI... → JWT válido
|
|
39
|
+
✓ apikey: sb_publishable_abc123 → API key correta
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
API keys 2026 (`sb_*`) **não são JWTs** — a platform check não valida; seu handler não pode usar `auth.getUser()` nelas.
|
|
43
|
+
|
|
44
|
+
## Toggle `verify_jwt` por função
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
# supabase/config.toml
|
|
48
|
+
[functions.user-profile]
|
|
49
|
+
verify_jwt = true # default — para funções chamadas com JWT do usuário
|
|
50
|
+
|
|
51
|
+
[functions.stripe-webhook]
|
|
52
|
+
verify_jwt = false # webhook externo (Stripe assina o body — não envia JWT)
|
|
53
|
+
|
|
54
|
+
[functions.internal-cron]
|
|
55
|
+
verify_jwt = false # chamada por cron com apikey: secret_key
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Regra: deixe `true` quando o caller é browser logado (`supabase.functions.invoke`); desligue para webhooks externos ou service-to-service que autentica via `apikey`.
|
|
59
|
+
|
|
60
|
+
CLI local pode passar `--no-verify-jwt` para um deploy/serve único:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
supabase functions serve hello-world --no-verify-jwt
|
|
64
|
+
supabase functions deploy stripe-webhook --no-verify-jwt
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## `@supabase/server` — wrapper canônico 2026
|
|
68
|
+
|
|
69
|
+
Package npm que reduz boilerplate de auth + contexto Supabase pré-configurado.
|
|
70
|
+
|
|
71
|
+
### Auth modes
|
|
72
|
+
|
|
73
|
+
| Mode | Aceita | `ctx` recebido |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `'user'` | JWT válido em `Authorization` | `ctx.supabase` scoped ao caller (respeita RLS), `ctx.userClaims` |
|
|
76
|
+
| `'secret:<name>'` | `sb_secret_<name>` em `apikey` | `ctx.supabaseAdmin` (bypassa RLS) |
|
|
77
|
+
| `'publishable:<name>'` | `sb_publishable_<name>` em `apikey` | `ctx.supabase` anon |
|
|
78
|
+
| `'none'` | Qualquer caller (sem check) | Cliente plain — handler responsabilidade total |
|
|
79
|
+
|
|
80
|
+
`<name>` referencia chaves nomeadas criadas em **Settings > API keys** (ex: `'secret:automations'`).
|
|
81
|
+
|
|
82
|
+
Combinar modos: `auth: ['user', 'secret:automations']` — primeiro match vence; `ctx.authMode` indica qual.
|
|
83
|
+
|
|
84
|
+
## Patterns canônicos
|
|
85
|
+
|
|
86
|
+
### Pattern 1 — User-facing (RLS aplicada)
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// supabase/functions/notes/index.ts
|
|
90
|
+
import { withSupabase } from 'npm:@supabase/server@1'
|
|
91
|
+
|
|
92
|
+
export default {
|
|
93
|
+
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
|
|
94
|
+
// ctx.supabase já vem scoped ao caller → RLS policies do user aplicadas
|
|
95
|
+
const { data, error } = await ctx.supabase.from('notes').select('*')
|
|
96
|
+
if (error) return Response.json({ error: error.message }, { status: 500 })
|
|
97
|
+
return Response.json({ data, email: ctx.userClaims?.email })
|
|
98
|
+
}),
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```toml
|
|
103
|
+
[functions.notes]
|
|
104
|
+
verify_jwt = true
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Pattern 2 — Service-to-service (cron/pg_net/worker)
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// supabase/functions/run-automations/index.ts
|
|
111
|
+
import { withSupabase } from 'npm:@supabase/server@1'
|
|
112
|
+
|
|
113
|
+
export default {
|
|
114
|
+
fetch: withSupabase({ auth: 'secret:automations' }, async (req, ctx) => {
|
|
115
|
+
// ctx.supabaseAdmin → bypassa RLS, scoped ao secret key 'automations'
|
|
116
|
+
await ctx.supabaseAdmin.from('automation_log').insert({ ran_at: new Date() })
|
|
117
|
+
return Response.json({ ok: true })
|
|
118
|
+
}),
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```toml
|
|
123
|
+
[functions.run-automations]
|
|
124
|
+
verify_jwt = false
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Crie a chave nomeada no Dashboard → **Settings > API keys** com nome `automations`. Compartilhe `sb_secret_...` resultante apenas com o serviço chamador.
|
|
128
|
+
|
|
129
|
+
### Pattern 3 — Webhook externo (Stripe/GitHub/Resend)
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
// supabase/functions/stripe-webhook/index.ts
|
|
133
|
+
import { withSupabase } from 'npm:@supabase/server@1'
|
|
134
|
+
import Stripe from 'npm:stripe@17'
|
|
135
|
+
|
|
136
|
+
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!)
|
|
137
|
+
|
|
138
|
+
export default {
|
|
139
|
+
fetch: withSupabase({ auth: 'none' }, async (req, ctx) => {
|
|
140
|
+
const signature = req.headers.get('stripe-signature') ?? ''
|
|
141
|
+
const body = await req.text()
|
|
142
|
+
try {
|
|
143
|
+
stripe.webhooks.constructEvent(body, signature, Deno.env.get('STRIPE_WEBHOOK_SECRET')!)
|
|
144
|
+
} catch {
|
|
145
|
+
return new Response('bad signature', { status: 400 })
|
|
146
|
+
}
|
|
147
|
+
// ctx.supabaseAdmin disponível para DB writes
|
|
148
|
+
await ctx.supabaseAdmin.from('stripe_events').insert({ raw: body })
|
|
149
|
+
return Response.json({ received: true })
|
|
150
|
+
}),
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```toml
|
|
155
|
+
[functions.stripe-webhook]
|
|
156
|
+
verify_jwt = false
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`auth: 'none'` desliga toda checagem de credencial Supabase. **Você é responsável por validar via signature do provedor.** Nunca use em endpoint sensível sem outro mecanismo.
|
|
160
|
+
|
|
161
|
+
### Pattern 4 — Função pública (health check)
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// supabase/functions/health/index.ts
|
|
165
|
+
import { withSupabase } from 'npm:@supabase/server@1'
|
|
166
|
+
|
|
167
|
+
export default {
|
|
168
|
+
fetch: withSupabase({ auth: 'none' }, async (_req, _ctx) => {
|
|
169
|
+
return Response.json({ ok: true, region: Deno.env.get('SB_REGION') })
|
|
170
|
+
}),
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```toml
|
|
175
|
+
[functions.health]
|
|
176
|
+
verify_jwt = false
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Pattern 5 — Modo combinado (user + service)
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
// supabase/functions/dual/index.ts
|
|
183
|
+
import { withSupabase } from 'npm:@supabase/server@1'
|
|
184
|
+
|
|
185
|
+
export default {
|
|
186
|
+
fetch: withSupabase({ auth: ['user', 'secret:automations'] }, async (req, ctx) => {
|
|
187
|
+
if (ctx.authMode === 'user') {
|
|
188
|
+
// user flow — ctx.supabase scoped ao caller
|
|
189
|
+
const { data } = await ctx.supabase.from('me').select('*').single()
|
|
190
|
+
return Response.json(data)
|
|
191
|
+
}
|
|
192
|
+
// service flow — ctx.supabaseAdmin bypassa RLS
|
|
193
|
+
await ctx.supabaseAdmin.from('audit').insert({ source: 'automation' })
|
|
194
|
+
return Response.json({ ok: true })
|
|
195
|
+
}),
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Pattern 6 — Erros customizados via `createSupabaseContext`
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { createSupabaseContext } from 'npm:@supabase/server@1'
|
|
203
|
+
|
|
204
|
+
export default {
|
|
205
|
+
fetch: async (req: Request) => {
|
|
206
|
+
const { data: ctx, error } = await createSupabaseContext(req, { auth: 'user' })
|
|
207
|
+
if (error) {
|
|
208
|
+
// shape 401 conforme contrato API
|
|
209
|
+
return Response.json(
|
|
210
|
+
{ message: error.message, code: error.code },
|
|
211
|
+
{ status: error.status, headers: { 'WWW-Authenticate': 'Bearer' } },
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
return Response.json({ message: `hello ${ctx.userClaims?.email}` })
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Pattern 7 — Sem `@supabase/server` (RLS manual)
|
|
220
|
+
|
|
221
|
+
Quando precisar de controle fino ou não puder adicionar dependência:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { createClient } from 'npm:@supabase/supabase-js@2.95.0'
|
|
225
|
+
|
|
226
|
+
const PUBLISHABLE = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)
|
|
227
|
+
|
|
228
|
+
Deno.serve(async (req) => {
|
|
229
|
+
// PT-BR: forward Authorization → supabase-js → queries respeitam RLS do user
|
|
230
|
+
const supabase = createClient(
|
|
231
|
+
Deno.env.get('SUPABASE_URL')!,
|
|
232
|
+
PUBLISHABLE['default'],
|
|
233
|
+
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } },
|
|
234
|
+
)
|
|
235
|
+
const { data, error } = await supabase.from('profiles').select('*')
|
|
236
|
+
if (error) return Response.json({ error: error.message }, { status: 500 })
|
|
237
|
+
return Response.json(data)
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Para validar JWT manualmente:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const { data: { user }, error } = await supabase.auth.getUser(
|
|
245
|
+
req.headers.get('Authorization')?.replace('Bearer ', '') ?? ''
|
|
246
|
+
)
|
|
247
|
+
if (!user || error) return new Response('unauthorized', { status: 401 })
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Env vars 2026 esperadas
|
|
251
|
+
|
|
252
|
+
`@supabase/server` lê estas (auto-provisionadas na plataforma + CLI local):
|
|
253
|
+
|
|
254
|
+
| Variável | O que é |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `SUPABASE_URL` | URL do projeto |
|
|
257
|
+
| `SUPABASE_PUBLISHABLE_KEYS` | JSON dict de chaves publishable |
|
|
258
|
+
| `SUPABASE_SECRET_KEYS` | JSON dict de chaves secret |
|
|
259
|
+
| `SUPABASE_JWKS` | JWK Set para validar user JWTs |
|
|
260
|
+
|
|
261
|
+
Fallback local (single-key): `SUPABASE_PUBLISHABLE_KEY` e `SUPABASE_SECRET_KEY` ainda funcionam.
|
|
262
|
+
|
|
263
|
+
## Anti-patterns
|
|
264
|
+
|
|
265
|
+
### AA1 — API key como Bearer
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
⚠ Authorization: Bearer sb_publishable_abc → 401, gateway rejeita
|
|
269
|
+
✓ apikey: sb_publishable_abc
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### AA2 — Chamar `auth.getUser()` em função `verify_jwt=false` sem repassar Authorization
|
|
273
|
+
|
|
274
|
+
Função com `verify_jwt=false` desliga apenas a **plataforma** — `auth.getUser()` ainda pode ser chamada se o caller mandar JWT. Mas se você não recebe JWT (webhook), `auth.getUser()` retorna `null`. Para Service-to-Service, use `auth: 'secret:<name>'` e `ctx.supabaseAdmin`.
|
|
275
|
+
|
|
276
|
+
### AA3 — `service_role` no client browser
|
|
277
|
+
|
|
278
|
+
`SUPABASE_SECRET_KEYS` (e legacy `SUPABASE_SERVICE_ROLE_KEY`) bypassam RLS. NUNCA expor em código browser. Só server-side (Edge Function, backend Node).
|
|
279
|
+
|
|
280
|
+
### AA4 — `auth: 'none'` em endpoint que lê/grava dado sensível
|
|
281
|
+
|
|
282
|
+
`'none'` aceita qualquer caller. Use apenas quando há outra validação (signature webhook, mTLS, etc.) ou endpoint genuinamente público (health check).
|
|
283
|
+
|
|
284
|
+
### AA5 — Hardcode `'service_role'` em vez de chave nomeada
|
|
285
|
+
|
|
286
|
+
Em 2026, prefira chaves nomeadas (`secret:automations`, `secret:internal-cron`) — permite rotação granular e audit por consumidor. Legacy `SUPABASE_SERVICE_ROLE_KEY` ainda funciona mas é uma única chave global.
|
|
287
|
+
|
|
288
|
+
### AA6 — `Authorization` E `apikey` ambos errados
|
|
289
|
+
|
|
290
|
+
Browser logado chamando `supabase.functions.invoke` envia **ambos**: `Authorization: Bearer <user-jwt>` + `apikey: sb_publishable_default`. É esperado e correto.
|
|
291
|
+
|
|
292
|
+
## Diagnóstico de 401
|
|
293
|
+
|
|
294
|
+
| Sintoma | Causa provável | Fix |
|
|
295
|
+
|---|---|---|
|
|
296
|
+
| 401 antes do handler | `verify_jwt=true` + Authorization ausente/inválido | mandar JWT válido OU `verify_jwt = false` |
|
|
297
|
+
| 401 do handler (`@supabase/server`) | apikey errada para o mode declarado | conferir mode vs header enviado |
|
|
298
|
+
| 401 só em produção, ok em dev | env vars não setadas em prod | `supabase secrets set` |
|
|
299
|
+
| 401 com `auth.getUser()` returning null | JWT expirou ou JWKS errado | refresh JWT no client |
|
|
300
|
+
|
|
301
|
+
## Ver também
|
|
302
|
+
|
|
303
|
+
- [`supabase-edge-functions`](../supabase-edge-functions/SKILL.md) — base Deno + env vars JSON dict
|
|
304
|
+
- [`supabase-edge-functions-limits`](../supabase-edge-functions-limits/SKILL.md) — status codes 401/403/405
|
|
305
|
+
- [`supabase-custom-claims-rbac`](../supabase-custom-claims-rbac/SKILL.md) — Custom Claims em JWT acessíveis via `ctx.userClaims`
|
|
306
|
+
- [`supabase-rls-policies`](../supabase-rls-policies/SKILL.md) — RLS aplicada quando `ctx.supabase` scoped
|
|
307
|
+
- [`supabase-rls-defense-in-depth`](../supabase-rls-defense-in-depth/SKILL.md) — `secret_role` caveat
|
|
308
|
+
- [`supabase-auth-ssr`](../supabase-auth-ssr/SKILL.md) — clients Next.js v16
|
|
309
|
+
- [`_shared-supabase/glossary.md`](../_shared-supabase/glossary.md) — termos canônicos auth
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supabase-edge-functions-limits
|
|
3
|
+
description: Use ao planejar limites, falhas e retries em Edge Functions Supabase — runtime caps (256MB / 2s CPU / 150-400s wall clock), platform caps (20MB bundle, 100/500/1000 funcs por plano), status codes canônicos (401/404/405/500/503/504/546 WORKER_LIMIT), recursive call budget ~5k req/min com `RateLimitError` + `retryAfterMs`, client-side error classes `FunctionsHttpError`/`FunctionsRelayError`/`FunctionsFetchError`, idempotency e backoff.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Supabase — Edge Functions Limits, Status Codes & Rate Limits · 2026
|
|
7
|
+
|
|
8
|
+
## Quando usar
|
|
9
|
+
|
|
10
|
+
Carrega quando:
|
|
11
|
+
|
|
12
|
+
- "limites Edge Function", "256MB memory", "wall clock", "CPU time"
|
|
13
|
+
- "504 Edge Function timeout", "503 BOOT_ERROR", "546 WORKER_LIMIT"
|
|
14
|
+
- "FunctionsHttpError supabase", "client edge function error"
|
|
15
|
+
- "RateLimitError edge function", "nested function calls", "recursive function rate limit"
|
|
16
|
+
- "idempotency key edge function", "retry edge function"
|
|
17
|
+
|
|
18
|
+
> Pré-requisito: [`supabase-edge-functions`](../supabase-edge-functions/SKILL.md).
|
|
19
|
+
> Cross-ref SRE: [`retry-strategies`](../retry-strategies/SKILL.md) · [`cascading-failures`](../cascading-failures/SKILL.md) · [`load-shedding-graceful-degradation`](../load-shedding-graceful-degradation/SKILL.md).
|
|
20
|
+
|
|
21
|
+
## Matriz de Limits
|
|
22
|
+
|
|
23
|
+
### Runtime (per request)
|
|
24
|
+
|
|
25
|
+
| Recurso | Free | Pro / Team / Enterprise |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| **Memory** | 256 MB | 256 MB |
|
|
28
|
+
| **CPU time** | 2s | 2s |
|
|
29
|
+
| **Wall clock (isolate lifetime)** | 150s | 400s |
|
|
30
|
+
| **Request idle timeout** | 150s | 150s |
|
|
31
|
+
| **Max log message** | 10.000 chars | 10.000 chars |
|
|
32
|
+
| **Log event rate** | 100 / 10s | 100 / 10s |
|
|
33
|
+
| **Ephemeral `/tmp`** | 256 MB | 512 MB |
|
|
34
|
+
|
|
35
|
+
CPU time é **CPU real** (não inclui I/O async). Wall clock conta tudo. Função pode ter 400s wall clock mas estourar com 2.1s CPU.
|
|
36
|
+
|
|
37
|
+
### Platform
|
|
38
|
+
|
|
39
|
+
| Recurso | Free | Pro | Team | Enterprise |
|
|
40
|
+
|---|---|---|---|---|
|
|
41
|
+
| **Max funcs/project** | 100 | 500 | 1000 | unlimited |
|
|
42
|
+
| **Max bundle size** | 20 MB | 20 MB | 20 MB | 20 MB |
|
|
43
|
+
| **Max secrets/project** | 100 | 100 | 100 | 100 |
|
|
44
|
+
| **Max secret size** | 48 KiB | 48 KiB | 48 KiB | 48 KiB |
|
|
45
|
+
| **Max secret name** | 256 chars (sem prefix `SUPABASE_`) | idem | idem | idem |
|
|
46
|
+
|
|
47
|
+
### Restrições gerais
|
|
48
|
+
|
|
49
|
+
- **Outbound ports** `25` e `587` bloqueados (use Resend/SendGrid HTTP API).
|
|
50
|
+
- **HTML serving** só com custom domains (`text/html` reescrito para `text/plain` em domínio padrão).
|
|
51
|
+
- **Sem Web Worker API / `node:vm`**.
|
|
52
|
+
- **Multithreading** não suportado (Sharp/libvips falham — use `magick-wasm`).
|
|
53
|
+
- **Static files** deploy só via Docker (`--use-api` não suporta).
|
|
54
|
+
|
|
55
|
+
## Status Codes Canônicos
|
|
56
|
+
|
|
57
|
+
| Code | Nome interno | Causa | Fix |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| `2xx` | success | Handler retornou OK | — |
|
|
60
|
+
| `3xx` | redirect | `Response.redirect()` | — |
|
|
61
|
+
| `401` | unauthorized | `verify_jwt=true` + Authorization inválido/ausente | Mandar JWT válido ou `verify_jwt=false` |
|
|
62
|
+
| `404` | not found | Função não existe / path errado | Conferir `supabase functions list` |
|
|
63
|
+
| `405` | method not allowed | Método fora de GET/POST/PUT/PATCH/DELETE/OPTIONS | Trocar método |
|
|
64
|
+
| `500` | `WORKER_ERROR` | Uncaught exception | Wrap em try/catch + log |
|
|
65
|
+
| `503` | `BOOT_ERROR` | Função falhou ao carregar (syntax error, import fail) | `supabase functions serve` local + logs |
|
|
66
|
+
| `504` | gateway timeout | Excedeu request idle timeout (150s) | Otimizar / mover para background |
|
|
67
|
+
| `546` | `WORKER_LIMIT` | Excedeu memory/CPU/wall clock | Reduzir bundle, chunk processing |
|
|
68
|
+
|
|
69
|
+
### Diagnosticar 546 (resource limit)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Ver qual limite foi atingido
|
|
73
|
+
mcp__supabase__get_logs --service edge-function
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Tipicamente em logs:
|
|
77
|
+
```
|
|
78
|
+
ERROR: WORKER_LIMIT: out of memory (256 MB)
|
|
79
|
+
ERROR: WORKER_LIMIT: CPU time limit exceeded (2s)
|
|
80
|
+
ERROR: WORKER_LIMIT: wall clock exceeded (400s)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Client-side Error Classes
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import {
|
|
87
|
+
FunctionsHttpError, // 4xx/5xx da função (status code do handler)
|
|
88
|
+
FunctionsRelayError, // problema gateway↔Supabase (raro)
|
|
89
|
+
FunctionsFetchError, // função inalcançável (network)
|
|
90
|
+
} from 'npm:@supabase/supabase-js@2.95.0'
|
|
91
|
+
|
|
92
|
+
const { data, error } = await supabase.functions.invoke('orders', { body })
|
|
93
|
+
|
|
94
|
+
if (error instanceof FunctionsHttpError) {
|
|
95
|
+
const body = await error.context.json()
|
|
96
|
+
// Trate por status — body.code (seu enum) + retry decision
|
|
97
|
+
} else if (error instanceof FunctionsRelayError) {
|
|
98
|
+
// Quase sempre transiente — retry com backoff
|
|
99
|
+
} else if (error instanceof FunctionsFetchError) {
|
|
100
|
+
// Função down / DNS / cliente offline
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Nested Function Calls — Rate Limit
|
|
105
|
+
|
|
106
|
+
Quando uma Edge Function chama outra Edge Function via `fetch()` ou `supabase.functions.invoke()`, a Supabase aplica rate limit ao **chain** completo:
|
|
107
|
+
|
|
108
|
+
- **Budget**: ~**5.000 requisições/minuto/chain** (regiões mais movimentadas têm budget maior).
|
|
109
|
+
- **Conta**: direct recursion, function chaining, circular calls, fan-out.
|
|
110
|
+
- **Não conta**: inbound requests, chamadas para APIs externas (Stripe, OpenAI).
|
|
111
|
+
|
|
112
|
+
### `RateLimitError` + `retryAfterMs`
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { createClient } from 'npm:@supabase/supabase-js@2.95.0'
|
|
116
|
+
|
|
117
|
+
const SECRET = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
|
|
118
|
+
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SECRET['default'])
|
|
119
|
+
|
|
120
|
+
Deno.serve(async (req) => {
|
|
121
|
+
try {
|
|
122
|
+
const { data, error } = await supabase.functions.invoke('downstream', { body: {} })
|
|
123
|
+
if (error) throw error
|
|
124
|
+
return Response.json(data)
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (err instanceof Deno.errors.RateLimitError) {
|
|
127
|
+
const retryAfterSec = Math.ceil(err.retryAfterMs / 1000)
|
|
128
|
+
return new Response(JSON.stringify({ error: 'service temporarily unavailable' }), {
|
|
129
|
+
status: 429,
|
|
130
|
+
headers: {
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
'Retry-After': retryAfterSec.toString(),
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
throw err
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Retry interno (com `retryAfterMs`)
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
async function invokeWithRetry(fn: string, body: object, maxRetries = 3) {
|
|
145
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
146
|
+
try {
|
|
147
|
+
const { data, error } = await supabase.functions.invoke(fn, { body })
|
|
148
|
+
if (error) throw error
|
|
149
|
+
return data
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err instanceof Deno.errors.RateLimitError && attempt < maxRetries - 1) {
|
|
152
|
+
// PT-BR: usar valor sugerido pelo runtime — evita thundering herd
|
|
153
|
+
await new Promise((r) => setTimeout(r, err.retryAfterMs))
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
throw err
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Padrões para evitar rate limit
|
|
163
|
+
|
|
164
|
+
### P1 — Batch em vez de N invocações
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// ⚠ Errado — N requests, estoura budget
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
await supabase.functions.invoke('process-item', { body: item })
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ✓ Certo — 1 request
|
|
173
|
+
await supabase.functions.invoke('process-items', { body: { items } })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### P2 — Limite de profundidade em recursão
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
Deno.serve(async (req) => {
|
|
180
|
+
const { depth = 0, data } = await req.json()
|
|
181
|
+
if (depth >= 5) return Response.json({ result: data }) // parar
|
|
182
|
+
const next = process(data)
|
|
183
|
+
const { data: result } = await supabase.functions.invoke('me', {
|
|
184
|
+
body: { depth: depth + 1, data: next },
|
|
185
|
+
})
|
|
186
|
+
return Response.json(result)
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### P3 — Shared library (sem HTTP overhead)
|
|
191
|
+
|
|
192
|
+
Funções que compartilham lógica não devem chamar uma à outra via HTTP. Use `_shared/`:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// supabase/functions/_shared/transform.ts
|
|
196
|
+
export function validate(d) { /* ... */ }
|
|
197
|
+
export function transform(d) { /* ... */ }
|
|
198
|
+
export async function save(d) { /* ... */ }
|
|
199
|
+
|
|
200
|
+
// supabase/functions/process/index.ts
|
|
201
|
+
import { validate, transform, save } from '../_shared/transform.ts'
|
|
202
|
+
// invocação direta — zero HTTP, zero rate limit
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### P4 — Queue para workload alto
|
|
206
|
+
|
|
207
|
+
Cargas > 5k/min devem ir para `pgmq` + worker, não recursive Edge Function. Ver [`supabase-cron-queues`](../supabase-cron-queues/SKILL.md).
|
|
208
|
+
|
|
209
|
+
## Idempotency keys
|
|
210
|
+
|
|
211
|
+
Para writes (POST/PATCH/PUT) em Edge Function chamada externamente, exija header `Idempotency-Key`:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
Deno.serve(async (req) => {
|
|
215
|
+
if (req.method === 'POST') {
|
|
216
|
+
const idempKey = req.headers.get('Idempotency-Key') ?? crypto.randomUUID()
|
|
217
|
+
const SECRET = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
|
|
218
|
+
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SECRET['default'])
|
|
219
|
+
|
|
220
|
+
// PT-BR: insert com chave única; se já existe, retorna o registro existente
|
|
221
|
+
const { data, error } = await supabase
|
|
222
|
+
.from('orders')
|
|
223
|
+
.upsert({ idempotency_key: idempKey, /* ... */ }, { onConflict: 'idempotency_key' })
|
|
224
|
+
.select()
|
|
225
|
+
.single()
|
|
226
|
+
return Response.json(data)
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Cross-ref: [`retry-strategies`](../retry-strategies/SKILL.md) — full/equal/decorrelated jitter quando aplicável.
|
|
232
|
+
|
|
233
|
+
## Error handling padrão
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
Deno.serve(async (req) => {
|
|
237
|
+
try {
|
|
238
|
+
const result = await processRequest(req)
|
|
239
|
+
return Response.json(result)
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error('function error:', error) // visível em Logs tab
|
|
242
|
+
// PT-BR: error.type enum fechado (NÃO error.message — cardinalidade)
|
|
243
|
+
const code = classifyError(error)
|
|
244
|
+
const status =
|
|
245
|
+
code === 'validation' ? 400 :
|
|
246
|
+
code === 'auth' ? 401 :
|
|
247
|
+
code === 'not_found' ? 404 :
|
|
248
|
+
code === 'rate_limit' ? 429 :
|
|
249
|
+
code === 'timeout' ? 504 :
|
|
250
|
+
500
|
|
251
|
+
return Response.json({ code, message: error.message }, { status })
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
function classifyError(e: unknown): string {
|
|
256
|
+
if (e instanceof ValidationError) return 'validation'
|
|
257
|
+
if (e instanceof AuthError) return 'auth'
|
|
258
|
+
if (e instanceof NotFoundError) return 'not_found'
|
|
259
|
+
if (e instanceof Deno.errors.RateLimitError) return 'rate_limit'
|
|
260
|
+
if (e instanceof TimeoutError) return 'timeout'
|
|
261
|
+
return 'unknown'
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Quando otimizar
|
|
266
|
+
|
|
267
|
+
| Sintoma | Fix prioritário |
|
|
268
|
+
|---|---|
|
|
269
|
+
| 504 frequente | Mover trabalho para `EdgeRuntime.waitUntil` ou pgmq |
|
|
270
|
+
| 546 OOM (memory) | Stream em vez de buffer in-memory; chunk processing |
|
|
271
|
+
| 546 CPU exceeded | Profiler local (`deno test --inspect`); mover para Wasm |
|
|
272
|
+
| 546 wall clock | Background tasks; reduzir async chains |
|
|
273
|
+
| 503 BOOT_ERROR | Bundle muito grande; lazy-load imports pesados (`await import('npm:openai@4')`) |
|
|
274
|
+
| 503 em deploys novos | Erro de import/syntax; `supabase functions serve` antes do deploy |
|
|
275
|
+
|
|
276
|
+
## Anti-patterns
|
|
277
|
+
|
|
278
|
+
### AL1 — `error.message` como dimension em métricas
|
|
279
|
+
Cardinality explode. Use enum fechado (5-15 valores) em `error.type`.
|
|
280
|
+
|
|
281
|
+
### AL2 — Retry sem `retryAfterMs`
|
|
282
|
+
Backoff manual com `1000ms` ignora hint do runtime. Sempre `setTimeout(_, err.retryAfterMs)` para RateLimitError.
|
|
283
|
+
|
|
284
|
+
### AL3 — Fan-out síncrono massive
|
|
285
|
+
`Promise.all(items.map(invoke))` com 1000 items satura budget. Limite concorrência (`p-limit`) ou batch server-side.
|
|
286
|
+
|
|
287
|
+
### AL4 — Polling em loop dentro da função
|
|
288
|
+
Wall clock estoura. Use webhook + database trigger (`pg_net.http_post`) ou WebSocket.
|
|
289
|
+
|
|
290
|
+
### AL5 — Bundle > 20 MB
|
|
291
|
+
Deploy falha. Lazy-load: `const { OpenAI } = await import('npm:openai@4')` evita carregar tudo no boot.
|
|
292
|
+
|
|
293
|
+
## Ver também
|
|
294
|
+
|
|
295
|
+
- [`supabase-edge-functions`](../supabase-edge-functions/SKILL.md) — base
|
|
296
|
+
- [`supabase-edge-functions-auth`](../supabase-edge-functions-auth/SKILL.md) — 401/403 detalhes
|
|
297
|
+
- [`supabase-edge-runtime-builtins`](../supabase-edge-runtime-builtins/SKILL.md) — `/tmp` 512MB paid
|
|
298
|
+
- [`supabase-cron-queues`](../supabase-cron-queues/SKILL.md) — pgmq quando recursive estoura
|
|
299
|
+
- [`retry-strategies`](../retry-strategies/SKILL.md) — full/equal/decorrelated jitter
|
|
300
|
+
- [`cascading-failures`](../cascading-failures/SKILL.md) — timeout/jitter/deadline/circuit breaker
|
|
301
|
+
- [`load-shedding-graceful-degradation`](../load-shedding-graceful-degradation/SKILL.md) — drop com 503 quando saturated
|
|
302
|
+
- [`four-golden-signals`](../four-golden-signals/SKILL.md) — saturation metric canônico
|