@luanpdd/kit-mcp 1.29.0 → 1.30.1

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.
Files changed (331) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +168 -168
  3. package/gates/agent-no-recursive-dispatch.md +82 -82
  4. package/kit/COMANDOS.md +138 -138
  5. package/kit/README.md +76 -76
  6. package/kit/agents/advisor-researcher.md +106 -106
  7. package/kit/agents/assumptions-analyzer.md +107 -107
  8. package/kit/agents/audit-log-implementer.md +313 -313
  9. package/kit/agents/auditor-consistencia-isolamento.md +413 -413
  10. package/kit/agents/b2b-saas-architect.md +156 -156
  11. package/kit/agents/cascading-failures-auditor.md +298 -298
  12. package/kit/agents/codebase-mapper.md +768 -768
  13. package/kit/agents/crm-pipeline-implementer.md +256 -256
  14. package/kit/agents/debugger.md +813 -813
  15. package/kit/agents/detector-tenant-quente.md +337 -337
  16. package/kit/agents/evolution-go-integrator.md +200 -200
  17. package/kit/agents/example-reviewer.md +21 -21
  18. package/kit/agents/executor.md +564 -564
  19. package/kit/agents/integration-checker.md +200 -200
  20. package/kit/agents/invite-flow-implementer.md +189 -189
  21. package/kit/agents/legacy-characterizer.md +368 -368
  22. package/kit/agents/lgpd-compliance-auditor.md +295 -295
  23. package/kit/agents/multi-tenant-isolation-auditor.md +253 -253
  24. package/kit/agents/multi-tenant-rls-writer.md +340 -340
  25. package/kit/agents/nyquist-auditor.md +178 -178
  26. package/kit/agents/observability-coverage-auditor.md +315 -315
  27. package/kit/agents/org-onboarding-implementer.md +223 -223
  28. package/kit/agents/payload-capture-instrumenter.md +273 -273
  29. package/kit/agents/phase-researcher.md +696 -696
  30. package/kit/agents/plan-checker.md +272 -272
  31. package/kit/agents/planner.md +922 -922
  32. package/kit/agents/project-researcher.md +652 -652
  33. package/kit/agents/refactor-safety-auditor.md +404 -404
  34. package/kit/agents/research-synthesizer.md +245 -245
  35. package/kit/agents/roadmapper.md +677 -677
  36. package/kit/agents/seam-finder.md +359 -359
  37. package/kit/agents/shotgun-surgery-detector.md +349 -349
  38. package/kit/agents/supabase-branching-architect.md +562 -562
  39. package/kit/agents/supabase-cicd-pipeline-implementer.md +777 -777
  40. package/kit/agents/supabase-column-privileges-writer.md +399 -399
  41. package/kit/agents/supabase-edge-fn-tester.md +287 -0
  42. package/kit/agents/supabase-edge-fn-writer.md +239 -210
  43. package/kit/agents/supabase-migration-writer.md +385 -385
  44. package/kit/agents/supabase-rbac-implementer.md +392 -392
  45. package/kit/agents/supabase-realtime-implementer.md +363 -267
  46. package/kit/agents/supabase-rls-hardener.md +521 -521
  47. package/kit/agents/supabase-rls-writer.md +323 -323
  48. package/kit/agents/supabase-roles-implementer.md +355 -355
  49. package/kit/agents/super-admin-implementer.md +281 -281
  50. package/kit/agents/ui-auditor.md +437 -437
  51. package/kit/agents/ui-checker.md +302 -302
  52. package/kit/agents/ui-researcher.md +355 -355
  53. package/kit/agents/user-profiler.md +175 -175
  54. package/kit/agents/validador-evolucao-schema.md +335 -335
  55. package/kit/agents/verifier.md +728 -728
  56. package/kit/commands/adicionar-backlog.md +75 -75
  57. package/kit/commands/adicionar-fase.md +42 -42
  58. package/kit/commands/adicionar-tarefa.md +45 -45
  59. package/kit/commands/adicionar-testes.md +41 -41
  60. package/kit/commands/ajuda.md +21 -21
  61. package/kit/commands/atualizar.md +37 -37
  62. package/kit/commands/auditar-cascading.md +111 -111
  63. package/kit/commands/auditar-marco.md +179 -179
  64. package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
  65. package/kit/commands/auditar-refactor.md +219 -219
  66. package/kit/commands/auditar-release.md +109 -109
  67. package/kit/commands/auditar-uat.md +23 -23
  68. package/kit/commands/autonomo.md +40 -40
  69. package/kit/commands/branch-pr.md +24 -24
  70. package/kit/commands/burn-rate-status.md +408 -408
  71. package/kit/commands/capturar-payloads.md +193 -193
  72. package/kit/commands/caracterizar.md +212 -212
  73. package/kit/commands/concluir-marco.md +247 -247
  74. package/kit/commands/configuracoes.md +36 -36
  75. package/kit/commands/dados-distribuidos.md +188 -188
  76. package/kit/commands/definir-perfil.md +10 -10
  77. package/kit/commands/depurar.md +190 -190
  78. package/kit/commands/detectar-duplicacao.md +197 -197
  79. package/kit/commands/discutir-fase.md +131 -131
  80. package/kit/commands/encontrar-seams.md +136 -136
  81. package/kit/commands/entrar-discord.md +17 -17
  82. package/kit/commands/estatisticas.md +18 -18
  83. package/kit/commands/example-greeting.md +33 -33
  84. package/kit/commands/executar-fase.md +58 -58
  85. package/kit/commands/expresso.md +56 -56
  86. package/kit/commands/fase-ui.md +34 -34
  87. package/kit/commands/fazer.md +57 -57
  88. package/kit/commands/fio.md +125 -125
  89. package/kit/commands/fluxos-trabalho.md +64 -64
  90. package/kit/commands/forense.md +176 -176
  91. package/kit/commands/gerenciador.md +38 -38
  92. package/kit/commands/inserir-fase.md +31 -31
  93. package/kit/commands/legacy.md +263 -263
  94. package/kit/commands/limpeza.md +17 -17
  95. package/kit/commands/listar-hipoteses-fase.md +45 -45
  96. package/kit/commands/listar-workspaces.md +18 -18
  97. package/kit/commands/load-shedding.md +117 -117
  98. package/kit/commands/mapear-codebase.md +70 -70
  99. package/kit/commands/multi-tenant.md +163 -163
  100. package/kit/commands/nota.md +33 -33
  101. package/kit/commands/novo-marco.md +43 -43
  102. package/kit/commands/novo-projeto.md +41 -41
  103. package/kit/commands/novo-workspace.md +43 -43
  104. package/kit/commands/pausar-trabalho.md +37 -37
  105. package/kit/commands/perfil-usuario.md +45 -45
  106. package/kit/commands/pesquisar-fase.md +195 -195
  107. package/kit/commands/planejar-fase.md +67 -67
  108. package/kit/commands/planejar-lacunas.md +33 -33
  109. package/kit/commands/plantar-ideia.md +25 -25
  110. package/kit/commands/progresso.md +24 -24
  111. package/kit/commands/proximo.md +30 -30
  112. package/kit/commands/publicar.md +490 -490
  113. package/kit/commands/rapido.md +35 -35
  114. package/kit/commands/reaplicar-patches.md +124 -124
  115. package/kit/commands/refactor-seguro.md +321 -321
  116. package/kit/commands/relatorio-sessao.md +19 -19
  117. package/kit/commands/remover-fase.md +31 -31
  118. package/kit/commands/remover-workspace.md +26 -26
  119. package/kit/commands/resumo-marco.md +50 -50
  120. package/kit/commands/retomar-trabalho.md +40 -40
  121. package/kit/commands/revisar-backlog.md +60 -60
  122. package/kit/commands/revisar-ui.md +32 -32
  123. package/kit/commands/revisar.md +37 -37
  124. package/kit/commands/saude.md +21 -21
  125. package/kit/commands/setup-notion.md +93 -93
  126. package/kit/commands/storytelling.md +179 -179
  127. package/kit/commands/supabase.md +30 -7
  128. package/kit/commands/sync-main.md +68 -68
  129. package/kit/commands/validar-fase.md +35 -35
  130. package/kit/commands/verificar-tarefas.md +44 -44
  131. package/kit/commands/verificar-trabalho.md +64 -64
  132. package/kit/file-manifest.json +15 -8
  133. package/kit/framework/bin/lib/commands.cjs +959 -959
  134. package/kit/framework/bin/lib/config.cjs +442 -442
  135. package/kit/framework/bin/lib/core.cjs +1230 -1230
  136. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  137. package/kit/framework/bin/lib/init.cjs +1442 -1442
  138. package/kit/framework/bin/lib/milestone.cjs +252 -252
  139. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  140. package/kit/framework/bin/lib/phase.cjs +888 -888
  141. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  142. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  143. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  144. package/kit/framework/bin/lib/security.cjs +382 -382
  145. package/kit/framework/bin/lib/state.cjs +1031 -1031
  146. package/kit/framework/bin/lib/template.cjs +222 -222
  147. package/kit/framework/bin/lib/uat.cjs +282 -282
  148. package/kit/framework/bin/lib/verify.cjs +888 -888
  149. package/kit/framework/bin/lib/workstream.cjs +491 -491
  150. package/kit/framework/bin/tools.cjs +918 -918
  151. package/kit/framework/commands/workstreams.md +63 -63
  152. package/kit/framework/references/checkpoints.md +778 -778
  153. package/kit/framework/references/continuation-format.md +249 -249
  154. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  155. package/kit/framework/references/git-integration.md +295 -295
  156. package/kit/framework/references/git-planning-commit.md +38 -38
  157. package/kit/framework/references/model-profile-resolution.md +36 -36
  158. package/kit/framework/references/model-profiles.md +139 -139
  159. package/kit/framework/references/phase-argument-parsing.md +61 -61
  160. package/kit/framework/references/planning-config.md +202 -202
  161. package/kit/framework/references/questioning.md +162 -162
  162. package/kit/framework/references/tdd.md +263 -263
  163. package/kit/framework/references/ui-brand.md +160 -160
  164. package/kit/framework/references/user-profiling.md +657 -657
  165. package/kit/framework/references/verification-patterns.md +612 -612
  166. package/kit/framework/references/workstream-flag.md +58 -58
  167. package/kit/framework/templates/DEBUG.md +164 -164
  168. package/kit/framework/templates/UAT.md +265 -265
  169. package/kit/framework/templates/UI-SPEC.md +100 -100
  170. package/kit/framework/templates/VALIDATION.md +76 -76
  171. package/kit/framework/templates/claude-md.md +122 -122
  172. package/kit/framework/templates/codebase/architecture.md +185 -185
  173. package/kit/framework/templates/codebase/concerns.md +205 -205
  174. package/kit/framework/templates/codebase/conventions.md +204 -204
  175. package/kit/framework/templates/codebase/integrations.md +192 -192
  176. package/kit/framework/templates/codebase/stack.md +158 -158
  177. package/kit/framework/templates/codebase/structure.md +199 -199
  178. package/kit/framework/templates/codebase/testing.md +301 -301
  179. package/kit/framework/templates/config.json +44 -44
  180. package/kit/framework/templates/context.md +352 -352
  181. package/kit/framework/templates/continue-here.md +78 -78
  182. package/kit/framework/templates/copilot-instructions.md +7 -7
  183. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  184. package/kit/framework/templates/dev-preferences.md +20 -20
  185. package/kit/framework/templates/discovery.md +146 -146
  186. package/kit/framework/templates/discussion-log.md +63 -63
  187. package/kit/framework/templates/milestone-archive.md +123 -123
  188. package/kit/framework/templates/milestone.md +115 -115
  189. package/kit/framework/templates/phase-prompt.md +610 -610
  190. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  191. package/kit/framework/templates/project.md +186 -186
  192. package/kit/framework/templates/requirements.md +231 -231
  193. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  194. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  195. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  196. package/kit/framework/templates/research-project/STACK.md +120 -120
  197. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  198. package/kit/framework/templates/research.md +419 -419
  199. package/kit/framework/templates/retrospective.md +54 -54
  200. package/kit/framework/templates/roadmap.md +202 -202
  201. package/kit/framework/templates/state.md +176 -176
  202. package/kit/framework/templates/summary-complex.md +59 -59
  203. package/kit/framework/templates/summary-minimal.md +41 -41
  204. package/kit/framework/templates/summary-standard.md +48 -48
  205. package/kit/framework/templates/summary.md +209 -209
  206. package/kit/framework/templates/user-profile.md +146 -146
  207. package/kit/framework/templates/user-setup.md +256 -256
  208. package/kit/framework/templates/verification-report.md +258 -258
  209. package/kit/framework/workflows/add-phase.md +112 -112
  210. package/kit/framework/workflows/add-tests.md +351 -351
  211. package/kit/framework/workflows/add-todo.md +158 -158
  212. package/kit/framework/workflows/audit-milestone.md +340 -340
  213. package/kit/framework/workflows/audit-uat.md +109 -109
  214. package/kit/framework/workflows/autonomous.md +891 -891
  215. package/kit/framework/workflows/check-todos.md +177 -177
  216. package/kit/framework/workflows/cleanup.md +152 -152
  217. package/kit/framework/workflows/complete-milestone.md +696 -696
  218. package/kit/framework/workflows/diagnose-issues.md +231 -231
  219. package/kit/framework/workflows/discovery-phase.md +289 -289
  220. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  221. package/kit/framework/workflows/discuss-phase.md +784 -784
  222. package/kit/framework/workflows/do.md +104 -104
  223. package/kit/framework/workflows/execute-phase.md +838 -838
  224. package/kit/framework/workflows/execute-plan.md +510 -510
  225. package/kit/framework/workflows/fast.md +102 -102
  226. package/kit/framework/workflows/forensics.md +265 -265
  227. package/kit/framework/workflows/health.md +181 -181
  228. package/kit/framework/workflows/help.md +619 -619
  229. package/kit/framework/workflows/insert-phase.md +130 -130
  230. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  231. package/kit/framework/workflows/list-workspaces.md +56 -56
  232. package/kit/framework/workflows/manager.md +362 -362
  233. package/kit/framework/workflows/map-codebase.md +377 -377
  234. package/kit/framework/workflows/milestone-summary.md +223 -223
  235. package/kit/framework/workflows/new-milestone.md +486 -486
  236. package/kit/framework/workflows/new-project.md +1159 -1159
  237. package/kit/framework/workflows/new-workspace.md +237 -237
  238. package/kit/framework/workflows/next.md +97 -97
  239. package/kit/framework/workflows/node-repair.md +92 -92
  240. package/kit/framework/workflows/note.md +156 -156
  241. package/kit/framework/workflows/pause-work.md +176 -176
  242. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  243. package/kit/framework/workflows/plan-phase.md +765 -765
  244. package/kit/framework/workflows/plant-seed.md +169 -169
  245. package/kit/framework/workflows/pr-branch.md +129 -129
  246. package/kit/framework/workflows/profile-user.md +450 -450
  247. package/kit/framework/workflows/progress.md +507 -507
  248. package/kit/framework/workflows/quick.md +757 -757
  249. package/kit/framework/workflows/remove-phase.md +155 -155
  250. package/kit/framework/workflows/remove-workspace.md +90 -90
  251. package/kit/framework/workflows/research-phase.md +82 -82
  252. package/kit/framework/workflows/resume-project.md +326 -326
  253. package/kit/framework/workflows/review.md +228 -228
  254. package/kit/framework/workflows/session-report.md +146 -146
  255. package/kit/framework/workflows/settings.md +283 -283
  256. package/kit/framework/workflows/ship.md +228 -228
  257. package/kit/framework/workflows/stats.md +60 -60
  258. package/kit/framework/workflows/transition.md +671 -671
  259. package/kit/framework/workflows/ui-phase.md +302 -302
  260. package/kit/framework/workflows/ui-review.md +165 -165
  261. package/kit/framework/workflows/update.md +323 -323
  262. package/kit/framework/workflows/validate-phase.md +174 -174
  263. package/kit/framework/workflows/verify-phase.md +252 -252
  264. package/kit/framework/workflows/verify-work.md +637 -637
  265. package/kit/hooks/check-update.js +118 -118
  266. package/kit/hooks/context-monitor.js +163 -163
  267. package/kit/hooks/kit-attribution-reminder.cjs +98 -0
  268. package/kit/hooks/prompt-guard.js +103 -103
  269. package/kit/hooks/statusline.js +125 -125
  270. package/kit/hooks/workflow-guard.js +101 -101
  271. package/kit/settings.json +45 -45
  272. package/kit/skills/_shared-supabase/glossary.md +17 -0
  273. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
  274. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
  275. package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
  276. package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
  277. package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
  278. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
  279. package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
  280. package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
  281. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
  282. package/kit/skills/example-skill/SKILL.md +42 -42
  283. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
  284. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
  285. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
  286. package/kit/skills/legacy-extract-class/SKILL.md +203 -203
  287. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
  288. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
  289. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
  290. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
  291. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
  292. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
  293. package/kit/skills/member-invite-flow/SKILL.md +305 -305
  294. package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
  295. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
  296. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
  297. package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
  298. package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
  299. package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
  300. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
  301. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
  302. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
  303. package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
  304. package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
  305. package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
  306. package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
  307. package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
  308. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
  309. package/kit/skills/supabase-edge-functions/SKILL.md +229 -141
  310. package/kit/skills/supabase-edge-functions-auth/SKILL.md +309 -0
  311. package/kit/skills/supabase-edge-functions-limits/SKILL.md +302 -0
  312. package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +279 -0
  313. package/kit/skills/supabase-edge-functions-testing/SKILL.md +277 -0
  314. package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +357 -0
  315. package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
  316. package/kit/skills/supabase-migrations/SKILL.md +297 -297
  317. package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
  318. package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
  319. package/kit/skills/supabase-realtime/SKILL.md +460 -236
  320. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
  321. package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
  322. package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
  323. package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
  324. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
  325. package/package.json +1 -1
  326. package/src/core/kit.js +216 -216
  327. package/src/core/reflect.js +247 -247
  328. package/src/core/reverse-sync.js +372 -372
  329. package/src/core/sync.js +418 -418
  330. package/src/core/watch.js +121 -121
  331. package/src/mcp-server/index.js +715 -693
@@ -1,236 +1,460 @@
1
- ---
2
- name: supabase-realtime
3
- description: Use ao implementar Realtime — broadcast com private:true, naming scope:entity:id, RLS sobre realtime.messages, removeChannel cleanup, migrar de postgres_changes.
4
- ---
5
-
6
- # Supabase — Realtime
7
-
8
- ## Quando usar
9
-
10
- LLM carrega esta skill quando implementar features Realtime em Supabase (chat, presence, notifications, live dashboards). Trigger phrases:
11
-
12
- - "Supabase Realtime", "broadcast", "presence"
13
- - "subscrever a mudanças no banco em tempo real"
14
- - "WebSocket Supabase"
15
- - "migrar postgres_changes para broadcast"
16
- - "RLS realtime.messages"
17
- - "channel state", "removeChannel"
18
-
19
- ## Regras absolutas
20
-
21
- - **Use `broadcast` por default** — `postgres_changes` é pattern legado (single-threaded, não escala). **Migrar para broadcast** em features novas.
22
- - **`private: true`** em todos os canais novos — exige autenticação + RLS sobre `realtime.messages`. Default em produção 2026.
23
- - **Naming canônico `scope:entity:id`** — ex: `room:messages:abc123`, `user:notifications:xyz789`, `org:announcements:org_42`.
24
- - **Eventos em `entity_action`** — ex: `message_inserted`, `task_updated`, `presence_joined`.
25
- - **`removeChannel` no cleanup obrigatório** — chamar `supabase.removeChannel(channel)` em `useEffect return` ou equivalente. Sem cleanup, memory leak + stale state (anti-pitfall B1).
26
- - **State checking antes de subscribe** — `if (channel.state === 'joined') return;` evita double-subscribe.
27
- - **RLS sobre `realtime.messages`** — SELECT (read) e INSERT (write) policies separadas, com index nas colunas usadas.
28
- - **Use Presence com moderação** — apenas para online status / cursors colaborativos, não para listas de objects (use queries normais).
29
- - Realtime tem **retry built-in** — log `status` no callback do `subscribe` mas não implementar retry manual.
30
-
31
- ## Patterns canônicos
32
-
33
- ### Subscribe via broadcast — client com cleanup
34
-
35
- ```ts
36
- // PT-BR: subscrição típica em Client Component
37
- 'use client'
38
- import { useEffect, useState } from 'react'
39
- import { createClient } from '@/utils/supabase/client'
40
-
41
- export function ChatRoom({ roomId }: { roomId: string }) {
42
- const supabase = createClient()
43
- const [messages, setMessages] = useState<Message[]>([])
44
-
45
- useEffect(() => {
46
- const channel = supabase
47
- .channel(`room:messages:${roomId}`, { config: { private: true } })
48
- .on('broadcast', { event: 'message_inserted' }, ({ payload }) => {
49
- setMessages((prev) => [...prev, payload as Message])
50
- })
51
- .subscribe((status) => {
52
- if (status === 'SUBSCRIBED') console.log('joined channel')
53
- if (status === 'CHANNEL_ERROR') console.error('channel error')
54
- })
55
-
56
- // PT-BR: cleanup obrigatório — sem isso, memory leak
57
- return () => {
58
- supabase.removeChannel(channel)
59
- }
60
- }, [roomId, supabase])
61
-
62
- return <ul>{messages.map((m) => <li key={m.id}>{m.text}</li>)}</ul>
63
- }
64
- ```
65
-
66
- ### RLS sobre `realtime.messages`
67
-
68
- ```sql
69
- -- PT-BR: SELECT policy permite ouvir broadcast em canal autenticado
70
- -- Granular: SELECT = read, INSERT = write — duas policies separadas
71
- create policy "auth_select_realtime_messages"
72
- on realtime.messages
73
- for select
74
- to authenticated
75
- using ((select auth.uid()) is not null);
76
-
77
- -- PT-BR: INSERT policy permite enviar broadcast
78
- create policy "auth_insert_realtime_messages"
79
- on realtime.messages
80
- for insert
81
- to authenticated
82
- with check ((select auth.uid()) is not null);
83
-
84
- -- PT-BR: index obrigatório (extension é a coluna usada por broadcast)
85
- create index if not exists realtime_messages_extension_idx
86
- on realtime.messages (extension);
87
- ```
88
-
89
- ### DB trigger via `realtime.broadcast_changes`
90
-
91
- Para emitir broadcast quando linha de tabela muda (substitui `postgres_changes`):
92
-
93
- ```sql
94
- -- PT-BR: trigger function emite broadcast no canal scope:entity:id
95
- create or replace function public.notify_message_insert()
96
- returns trigger
97
- language plpgsql
98
- security invoker
99
- set search_path = ''
100
- as $$
101
- begin
102
- perform realtime.broadcast_changes(
103
- 'room:messages:' || new.room_id::text, -- canal
104
- 'message_inserted', -- event name
105
- 'INSERT', -- operation
106
- 'messages', -- table
107
- 'public', -- schema
108
- new, -- new row
109
- null -- old row
110
- );
111
- return new;
112
- end;
113
- $$;
114
-
115
- create trigger messages_broadcast_on_insert
116
- after insert on public.messages
117
- for each row
118
- execute function public.notify_message_insert();
119
- ```
120
-
121
- ### Presence — apenas para online status
122
-
123
- ```ts
124
- // PT-BR: presence é sparingly — só para "quem está online"
125
- const channel = supabase
126
- .channel(`room:${roomId}`, { config: { private: true } })
127
- .on('presence', { event: 'sync' }, () => {
128
- const state = channel.presenceState()
129
- setOnlineUsers(Object.keys(state))
130
- })
131
- .subscribe(async (status) => {
132
- if (status !== 'SUBSCRIBED') return
133
- await channel.track({ user_id: userId, online_at: new Date().toISOString() })
134
- })
135
-
136
- return () => {
137
- supabase.removeChannel(channel)
138
- }
139
- ```
140
-
141
- ### Migrar de `postgres_changes` para `broadcast`
142
-
143
- ```ts
144
- // ❌ PADRÃO LEGADO — postgres_changes
145
- const channel = supabase
146
- .channel('messages_changes')
147
- .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, callback)
148
- .subscribe()
149
-
150
- // ✅ PADRÃO ATUAL — broadcast com trigger DB
151
- // 1. Criar trigger SQL `realtime.broadcast_changes` (ver pattern acima)
152
- // 2. Subscribe via broadcast no client:
153
- const channel = supabase
154
- .channel(`room:messages:${roomId}`, { config: { private: true } })
155
- .on('broadcast', { event: 'message_inserted' }, callback)
156
- .subscribe()
157
- ```
158
-
159
- ## Anti-patterns
160
-
161
- ### Anti-pattern 1: Canal sem `private: true`
162
-
163
- **Errado:**
164
- ```ts
165
- const channel = supabase.channel('messages') // canal público
166
- .on('broadcast', { event: 'msg' }, callback)
167
- .subscribe()
168
- ```
169
-
170
- **Por quê:** canal público — qualquer cliente recebe payload sem RLS. Em produção isso vaza dados (broadcast pode incluir info sensível).
171
-
172
- **Certo:**
173
- ```ts
174
- const channel = supabase
175
- .channel(`room:messages:${roomId}`, { config: { private: true } })
176
- .on('broadcast', { event: 'message_inserted' }, callback)
177
- .subscribe()
178
- ```
179
-
180
- ### Anti-pattern 2: Subscribe sem `removeChannel` no cleanup
181
-
182
- **Errado:**
183
- ```tsx
184
- useEffect(() => {
185
- const channel = supabase.channel('...').subscribe()
186
- // sem return canal nunca limpo
187
- }, [])
188
- ```
189
-
190
- **Por quê:** memory leak. Em SPA com navegação, canais antigos continuam recebendo eventos — UI fica em estado inconsistente. WebSocket connections crescem indefinidamente.
191
-
192
- **Certo:**
193
- ```tsx
194
- useEffect(() => {
195
- const channel = supabase.channel('...').subscribe()
196
- return () => {
197
- supabase.removeChannel(channel)
198
- }
199
- }, [])
200
- ```
201
-
202
- ### Anti-pattern 3: `postgres_changes` em features novas
203
-
204
- **Errado:**
205
- ```ts
206
- supabase.channel('changes')
207
- .on('postgres_changes', { event: '*', schema: 'public', table: 'messages' }, callback)
208
- .subscribe()
209
- ```
210
-
211
- **Por quê:** `postgres_changes` é single-threaded em Realtime backend. Em escala (>100 connections, >1k events/sec), throughput cai drasticamente. Documentado em [Realtime Limits](https://supabase.com/docs/guides/realtime/limits).
212
-
213
- **Certo:** trigger DB com `realtime.broadcast_changes` + subscribe via `broadcast` (ver pattern "Migrar" acima).
214
-
215
- ### Anti-pattern 4: Presence para listar objetos
216
-
217
- **Errado:**
218
- ```ts
219
- // ⚠ usar presence para listar tasks ativas
220
- channel.on('presence', { event: 'sync' }, () => {
221
- const tasks = Object.values(channel.presenceState())
222
- setTasks(tasks)
223
- })
224
- ```
225
-
226
- **Por quê:** Presence é projetado para "quem está online" — state efêmero ligado a connection. Para listas de objetos, use query normal + broadcast quando muda. Presence inflado degrada toda a infraestrutura Realtime do projeto.
227
-
228
- **Certo:** query SQL para `tasks` + broadcast em mudanças via trigger DB.
229
-
230
- ## Ver também
231
-
232
- - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) RLS sobre `realtime.messages` (SELECT + INSERT separados)
233
- - [supabase-database-functions](../supabase-database-functions/SKILL.md) — trigger functions com `set search_path = ''`
234
- - [supabase-auth-ssr](../supabase-auth-ssr/SKILL.md) — autenticação que habilita canais `private: true`
235
- - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — Edge Functions disparando broadcast via `realtime.send`
236
- - [glossário](../_shared-supabase/glossary.md) — termos PT-BR↔EN
1
+ ---
2
+ name: supabase-realtime
3
+ description: Use ao implementar Realtime — broadcast com private:true, naming scope:entity:id, RLS sobre realtime.messages, removeChannel cleanup, migrar de postgres_changes.
4
+ ---
5
+
6
+ # Supabase — Realtime
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando implementar features Realtime em Supabase (chat, presence, notifications, live dashboards). Trigger phrases:
11
+
12
+ - "Supabase Realtime", "broadcast", "presence"
13
+ - "subscrever a mudanças no banco em tempo real"
14
+ - "WebSocket Supabase"
15
+ - "migrar postgres_changes para broadcast"
16
+ - "RLS realtime.messages"
17
+ - "channel state", "removeChannel"
18
+
19
+ ## Regras absolutas
20
+
21
+ - **Use `broadcast` por default** — `postgres_changes` é pattern legado (single-threaded, não escala). **Migrar para broadcast** em features novas.
22
+ - **`private: true`** em todos os canais novos — exige autenticação + RLS sobre `realtime.messages`. Default em produção 2026.
23
+ - **Naming canônico `scope:entity:id`** — ex: `room:messages:abc123`, `user:notifications:xyz789`, `org:announcements:org_42`.
24
+ - **Eventos em `entity_action`** — ex: `message_inserted`, `task_updated`, `presence_joined`.
25
+ - **`removeChannel` no cleanup obrigatório** — chamar `supabase.removeChannel(channel)` em `useEffect return` ou equivalente. Sem cleanup, memory leak + stale state (anti-pitfall B1).
26
+ - **State checking antes de subscribe** — `if (channel.state === 'joined') return;` evita double-subscribe.
27
+ - **RLS sobre `realtime.messages`** — SELECT (read) e INSERT (write) policies separadas, com index nas colunas usadas.
28
+ - **Use Presence com moderação** — apenas para online status / cursors colaborativos, não para listas de objects (use queries normais).
29
+ - Realtime tem **retry built-in** — log `status` no callback do `subscribe` mas não implementar retry manual.
30
+
31
+ ## Patterns canônicos
32
+
33
+ ### Subscribe via broadcast — client com cleanup
34
+
35
+ ```ts
36
+ // PT-BR: subscrição típica em Client Component
37
+ 'use client'
38
+ import { useEffect, useState } from 'react'
39
+ import { createClient } from '@/utils/supabase/client'
40
+
41
+ export function ChatRoom({ roomId }: { roomId: string }) {
42
+ const supabase = createClient()
43
+ const [messages, setMessages] = useState<Message[]>([])
44
+
45
+ useEffect(() => {
46
+ const channel = supabase
47
+ .channel(`room:messages:${roomId}`, { config: { private: true } })
48
+ .on('broadcast', { event: 'message_inserted' }, ({ payload }) => {
49
+ setMessages((prev) => [...prev, payload as Message])
50
+ })
51
+ .subscribe((status) => {
52
+ if (status === 'SUBSCRIBED') console.log('joined channel')
53
+ if (status === 'CHANNEL_ERROR') console.error('channel error')
54
+ })
55
+
56
+ // PT-BR: cleanup obrigatório — sem isso, memory leak
57
+ return () => {
58
+ supabase.removeChannel(channel)
59
+ }
60
+ }, [roomId, supabase])
61
+
62
+ return <ul>{messages.map((m) => <li key={m.id}>{m.text}</li>)}</ul>
63
+ }
64
+ ```
65
+
66
+ ### RLS sobre `realtime.messages`
67
+
68
+ ```sql
69
+ -- PT-BR: SELECT policy permite ouvir broadcast em canal autenticado
70
+ -- Granular: SELECT = read, INSERT = write — duas policies separadas
71
+ create policy "auth_select_realtime_messages"
72
+ on realtime.messages
73
+ for select
74
+ to authenticated
75
+ using ((select auth.uid()) is not null);
76
+
77
+ -- PT-BR: INSERT policy permite enviar broadcast
78
+ create policy "auth_insert_realtime_messages"
79
+ on realtime.messages
80
+ for insert
81
+ to authenticated
82
+ with check ((select auth.uid()) is not null);
83
+
84
+ -- PT-BR: index obrigatório (extension é a coluna usada por broadcast)
85
+ create index if not exists realtime_messages_extension_idx
86
+ on realtime.messages (extension);
87
+ ```
88
+
89
+ ### DB trigger via `realtime.broadcast_changes`
90
+
91
+ Para emitir broadcast quando linha de tabela muda (substitui `postgres_changes`):
92
+
93
+ ```sql
94
+ -- PT-BR: trigger function emite broadcast no canal scope:entity:id
95
+ create or replace function public.notify_message_insert()
96
+ returns trigger
97
+ language plpgsql
98
+ security invoker
99
+ set search_path = ''
100
+ as $$
101
+ begin
102
+ perform realtime.broadcast_changes(
103
+ 'room:messages:' || new.room_id::text, -- canal
104
+ 'message_inserted', -- event name
105
+ 'INSERT', -- operation
106
+ 'messages', -- table
107
+ 'public', -- schema
108
+ new, -- new row
109
+ null -- old row
110
+ );
111
+ return new;
112
+ end;
113
+ $$;
114
+
115
+ create trigger messages_broadcast_on_insert
116
+ after insert on public.messages
117
+ for each row
118
+ execute function public.notify_message_insert();
119
+ ```
120
+
121
+ ### Presence — apenas para online status
122
+
123
+ ```ts
124
+ // PT-BR: presence é sparingly — só para "quem está online"
125
+ const channel = supabase
126
+ .channel(`room:${roomId}`, { config: { private: true } })
127
+ .on('presence', { event: 'sync' }, () => {
128
+ const state = channel.presenceState()
129
+ setOnlineUsers(Object.keys(state))
130
+ })
131
+ .subscribe(async (status) => {
132
+ if (status !== 'SUBSCRIBED') return
133
+ await channel.track({ user_id: userId, online_at: new Date().toISOString() })
134
+ })
135
+
136
+ return () => {
137
+ supabase.removeChannel(channel)
138
+ }
139
+ ```
140
+
141
+ ### Migrar de `postgres_changes` para `broadcast`
142
+
143
+ ```ts
144
+ // ❌ PADRÃO LEGADO — postgres_changes
145
+ const channel = supabase
146
+ .channel('messages_changes')
147
+ .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, callback)
148
+ .subscribe()
149
+
150
+ // ✅ PADRÃO ATUAL — broadcast com trigger DB
151
+ // 1. Criar trigger SQL `realtime.broadcast_changes` (ver pattern acima)
152
+ // 2. Subscribe via broadcast no client:
153
+ const channel = supabase
154
+ .channel(`room:messages:${roomId}`, { config: { private: true } })
155
+ .on('broadcast', { event: 'message_inserted' }, callback)
156
+ .subscribe()
157
+ ```
158
+
159
+ ### `realtime.send` vs `realtime.broadcast_changes` — qual usar
160
+
161
+ A doc oficial expõe **duas funções SQL** para emitir broadcast a partir do banco. Escolha por intent:
162
+
163
+ | Função | Quando usar | Payload |
164
+ |---|---|---|
165
+ | `realtime.broadcast_changes(topic, event, op, table, schema, new, old)` | **Espelhar mudança de tabela** — INSERT/UPDATE/DELETE. Payload já formatado com schema/table/op/record. | Auto a partir de `NEW`/`OLD` |
166
+ | `realtime.send(payload jsonb, event text, topic text, is_private bool)` | **Notificação custom / payload filtrado** — eventos sem mapeamento 1:1 a row change. Você define exatamente o que vai. | Manual |
167
+
168
+ ```sql
169
+ -- PT-BR: notificação custom — só campos públicos, sem PII
170
+ create or replace function public.notify_message_activity()
171
+ returns trigger
172
+ language plpgsql
173
+ security definer
174
+ set search_path = ''
175
+ as $$
176
+ begin
177
+ if tg_op = 'INSERT' then
178
+ perform realtime.send(
179
+ jsonb_build_object(
180
+ 'message_id', new.id,
181
+ 'room_id', new.room_id,
182
+ 'created_at', new.created_at
183
+ -- author_id intencionalmente omitido (PII)
184
+ ),
185
+ 'message_created', -- event
186
+ 'room:' || new.room_id::text || ':activity', -- topic
187
+ true -- is_private (default em prod)
188
+ );
189
+ end if;
190
+ return null;
191
+ end;
192
+ $$;
193
+ ```
194
+
195
+ > O flag `is_private` no `realtime.send` **deve casar** com o `private` config no client. Public message para channel private = não entrega.
196
+
197
+ ### REST API — broadcast server-side (sem WebSocket)
198
+
199
+ Para enviar broadcast a partir de um servidor (Edge Function, backend Next.js API route, worker, cron), use o endpoint HTTP em vez de manter um WebSocket aberto:
200
+
201
+ ```ts
202
+ // PT-BR: enviar broadcast via REST de um server — não precisa subscribe
203
+ const response = await fetch(
204
+ `https://${PROJECT_REF}.supabase.co/realtime/v1/api/broadcast`,
205
+ {
206
+ method: 'POST',
207
+ headers: {
208
+ 'Content-Type': 'application/json',
209
+ apikey: process.env.SUPABASE_SERVICE_ROLE_KEY!, // server-only key
210
+ },
211
+ body: JSON.stringify({
212
+ messages: [
213
+ {
214
+ topic: `room:messages:${roomId}`,
215
+ event: 'message_inserted',
216
+ payload: { id, text, user_id },
217
+ private: true, // deve casar com o channel client-side
218
+ },
219
+ ],
220
+ }),
221
+ }
222
+ )
223
+ ```
224
+
225
+ Tradeoff: HTTP add ~1 RTT por mensagem vs WebSocket persistente; mas elimina necessidade de manter conexão WS em serverless/edge functions.
226
+
227
+ ### Broadcast Replay (v2.74.0+) — recuperar mensagens passadas
228
+
229
+ Disponível em `@supabase/supabase-js@2.74.0+`. Permite que clients **private** acessem broadcasts emitidos pelo DB (via `realtime.send`/`realtime.broadcast_changes`) **antes** de subscribar.
230
+
231
+ ```ts
232
+ const channel = supabase.channel(`room:${roomId}`, {
233
+ config: {
234
+ private: true,
235
+ broadcast: {
236
+ replay: {
237
+ since: Date.now() - 60_000, // últimos 60s (epoch ms)
238
+ limit: 25, // máximo permitido pela spec
239
+ },
240
+ },
241
+ },
242
+ })
243
+
244
+ channel.on('broadcast', { event: 'message_inserted' }, ({ payload, meta }) => {
245
+ if (meta?.replayed) {
246
+ // PT-BR: mensagem do passado — não tocar som de notificação
247
+ appendMessage(payload, { historical: true })
248
+ } else {
249
+ appendMessage(payload, { historical: false })
250
+ }
251
+ }).subscribe()
252
+ ```
253
+
254
+ **Quando usar:** chat com histórico recente, dashboards com últimos N eventos, reconexão após network drop, page reload sem perder estado. **Limitação:** só mensagens emitidas pelo DB (`realtime.send`/`broadcast_changes`); mensagens enviadas via client `channel.send()` NÃO são replayed.
255
+
256
+ ### Authorization — usar `realtime.topic()` e JWT claims em RLS
257
+
258
+ Em policies sobre `realtime.messages`, helpers:
259
+
260
+ - **`realtime.topic()`** — retorna o nome do canal que o cliente está tentando entrar. Use para matchear policy contra app data (ex: room_id no topic).
261
+ - **`current_setting('request.jwt.claims')::json`** — acessa claims do JWT do client. Permite checks tipo "só usuário com role X", "só email @empresa.com".
262
+
263
+ ```sql
264
+ -- PT-BR: só membros da room podem ouvir broadcast daquela room
265
+ -- Topic pattern: room:messages:<room_id> — split_part extrai room_id (3º segmento)
266
+ create policy "room_members_can_listen"
267
+ on realtime.messages
268
+ for select
269
+ to authenticated
270
+ using (
271
+ exists (
272
+ select 1
273
+ from public.room_members rm
274
+ where rm.user_id = (select auth.uid())
275
+ and rm.room_id::text = split_part(realtime.topic(), ':', 3)
276
+ )
277
+ );
278
+
279
+ -- PT-BR: claim-based — só usuários com role 'admin' podem broadcast em :admin topics
280
+ create policy "admin_role_can_broadcast_admin"
281
+ on realtime.messages
282
+ for insert
283
+ to authenticated
284
+ with check (
285
+ realtime.topic() like '%:admin:%'
286
+ and (current_setting('request.jwt.claims')::json->>'user_role') = 'admin'
287
+ );
288
+ ```
289
+
290
+ > RLS sobre `realtime.messages` é **avaliada na hora do JOIN do canal** (cache por sessão). Token refresh dispara reavaliação — ver "Custom JWT" abaixo.
291
+
292
+ ### Custom JWT e refresh — `supabase.realtime.setAuth()`
293
+
294
+ Realtime mantém o token ativo do client em memória. Para tokens custom (assinados com seu JWT secret) ou refresh após expiração:
295
+
296
+ ```ts
297
+ // PT-BR: setar token customizado ANTES de subscribar canais
298
+ supabase.realtime.setAuth(customSignedJwt)
299
+
300
+ const channel = supabase
301
+ .channel('private-thing', { config: { private: true } })
302
+ .on('broadcast', { event: 'x' }, callback)
303
+ .subscribe()
304
+
305
+ // PT-BR: refresh quando token está perto de expirar
306
+ function onTokenRefreshed(newJwt: string) {
307
+ supabase.realtime.setAuth(newJwt)
308
+ // canais privados existentes mantêm conexão, mas RLS é reavaliada
309
+ }
310
+ ```
311
+
312
+ **NUNCA** exponha `service_role` no client — ele bypassa RLS. Token refresh deve ser feito proativamente; sem refresh, conexão fecha quando JWT expira (TTL default 1h no Supabase Auth).
313
+
314
+ ### Self-send e Ack — opções para teste e confirmação
315
+
316
+ ```ts
317
+ const channel = supabase.channel('test-channel', {
318
+ config: {
319
+ private: true,
320
+ broadcast: {
321
+ self: true, // PT-BR: receber as próprias broadcasts (default false) — útil em testes
322
+ ack: true, // PT-BR: confirmação do server (Promise resolve quando entregue)
323
+ },
324
+ },
325
+ })
326
+
327
+ channel.subscribe(async (status) => {
328
+ if (status !== 'SUBSCRIBED') return
329
+ const response = await channel.send({
330
+ type: 'broadcast',
331
+ event: 'test',
332
+ payload: {},
333
+ })
334
+ // PT-BR: com ack:true, response indica 'ok' | 'error' | 'timed_out'
335
+ console.log('delivery:', response)
336
+ })
337
+ ```
338
+
339
+ Use `self:true` em **dev/test only** (loops em prod). Use `ack:true` quando latência matters e você precisa retry (ex: chat com "enviada/falhou").
340
+
341
+ ### `replica identity full` — old record em UPDATE/DELETE (postgres_changes legacy)
342
+
343
+ Aplica-se apenas a **`postgres_changes`** legacy (broadcast com trigger não precisa disso — você passa `old` explicit). Por default Postgres só envia o **primary key** do row antigo em UPDATE/DELETE pelo replication slot. Para receber **todas** as colunas anteriores:
344
+
345
+ ```sql
346
+ -- PT-BR: replica identity full faz logical replication carregar row inteiro
347
+ alter table public.messages replica identity full;
348
+ ```
349
+
350
+ Custo: aumenta volume de WAL (toda coluna replicada). **DELETE com RLS não dispara para clients** — Postgres não consegue avaliar policies em rows que não existem mais; só PK aparece e mesmo assim sem auth check.
351
+
352
+ ## Limits & quotas por plano
353
+
354
+ Limites canônicos da doc oficial — saber estes evita surpresa em prod:
355
+
356
+ | Métrica | Free | Pro | Pro no spend cap / Team | Enterprise |
357
+ |---|---|---|---|---|
358
+ | Concurrent connections | 200 | 500 | 10.000 | 10.000+ |
359
+ | Messages/sec | 100 | 500 | 2.500 | 2.500+ |
360
+ | Channel joins/sec | 100 | 500 | 2.500 | 2.500+ |
361
+ | Channels por connection | 100 | 100 | 100 | 100+ |
362
+ | Presence keys por objeto | 10 | 10 | 10 | 10+ |
363
+ | Presence msgs/sec | 20 | 50 | 1.000 | 1.000+ |
364
+ | Broadcast payload | 256 KB | 3 MB | 3 MB | 3+ MB |
365
+ | Postgres changes payload | 1 MB | 1 MB | 1 MB | 1+ MB |
366
+
367
+ **Pricing usage (pós-quota):** ~$2.50 / milhão de messages, ~$10 / mil peak connections (Pro). Quotas mensais: 2M msgs + 200 connections (Free), 5M msgs + 500 connections (Pro/Team).
368
+
369
+ ## Error codes canônicos
370
+
371
+ Mensagens que aparecem em `subscribe(status, err)` callback ou em [Realtime Logs](https://supabase.com/dashboard/project/_/database/realtime-logs):
372
+
373
+ | Código | Significado | Fix |
374
+ |---|---|---|
375
+ | `too_many_connections` | Project bateu Concurrent Connections limit | Upgrade plan ou audit connection leak (cleanup faltando) |
376
+ | `too_many_joins` | Channel join rate excedido (joins/sec) | Throttle subscribes; usa 1 canal com vários filters em vez de N canais |
377
+ | `too_many_channels` | Channels por connection > 100 | Use 1 connection para N channels; nunca 1 connection por channel |
378
+ | `tenant_events` | Project excedeu Messages/sec | Reduza throughput ou upgrade plan; reconnect é automático quando baixa |
379
+ | `unauthorized` | RLS sobre `realtime.messages` negou; token inválido | Verifique policy + JWT válido + `private: true` casando com SQL `is_private` |
380
+ | `CHANNEL_ERROR` | Erro genérico — server-side issue | Veja Realtime Logs para detalhe; nunca silenciar este status |
381
+ | `TIMED_OUT` | WebSocket heartbeat não recebido (~30s) | Verifique network; em Node v18+ heartbeat custom pode ser necessário |
382
+
383
+ ## Anti-patterns
384
+
385
+ ### Anti-pattern 1: Canal sem `private: true`
386
+
387
+ **Errado:**
388
+ ```ts
389
+ const channel = supabase.channel('messages') // canal público
390
+ .on('broadcast', { event: 'msg' }, callback)
391
+ .subscribe()
392
+ ```
393
+
394
+ **Por quê:** canal público — qualquer cliente recebe payload sem RLS. Em produção isso vaza dados (broadcast pode incluir info sensível).
395
+
396
+ **Certo:**
397
+ ```ts
398
+ const channel = supabase
399
+ .channel(`room:messages:${roomId}`, { config: { private: true } })
400
+ .on('broadcast', { event: 'message_inserted' }, callback)
401
+ .subscribe()
402
+ ```
403
+
404
+ ### Anti-pattern 2: Subscribe sem `removeChannel` no cleanup
405
+
406
+ **Errado:**
407
+ ```tsx
408
+ useEffect(() => {
409
+ const channel = supabase.channel('...').subscribe()
410
+ // ⚠ sem return — canal nunca limpo
411
+ }, [])
412
+ ```
413
+
414
+ **Por quê:** memory leak. Em SPA com navegação, canais antigos continuam recebendo eventos — UI fica em estado inconsistente. WebSocket connections crescem indefinidamente.
415
+
416
+ **Certo:**
417
+ ```tsx
418
+ useEffect(() => {
419
+ const channel = supabase.channel('...').subscribe()
420
+ return () => {
421
+ supabase.removeChannel(channel)
422
+ }
423
+ }, [])
424
+ ```
425
+
426
+ ### Anti-pattern 3: `postgres_changes` em features novas
427
+
428
+ **Errado:**
429
+ ```ts
430
+ supabase.channel('changes')
431
+ .on('postgres_changes', { event: '*', schema: 'public', table: 'messages' }, callback)
432
+ .subscribe()
433
+ ```
434
+
435
+ **Por quê:** `postgres_changes` é single-threaded em Realtime backend. Em escala (>100 connections, >1k events/sec), throughput cai drasticamente. Documentado em [Realtime Limits](https://supabase.com/docs/guides/realtime/limits).
436
+
437
+ **Certo:** trigger DB com `realtime.broadcast_changes` + subscribe via `broadcast` (ver pattern "Migrar" acima).
438
+
439
+ ### Anti-pattern 4: Presence para listar objetos
440
+
441
+ **Errado:**
442
+ ```ts
443
+ // ⚠ usar presence para listar tasks ativas
444
+ channel.on('presence', { event: 'sync' }, () => {
445
+ const tasks = Object.values(channel.presenceState())
446
+ setTasks(tasks)
447
+ })
448
+ ```
449
+
450
+ **Por quê:** Presence é projetado para "quem está online" — state efêmero ligado a connection. Para listas de objetos, use query normal + broadcast quando muda. Presence inflado degrada toda a infraestrutura Realtime do projeto.
451
+
452
+ **Certo:** query SQL para `tasks` + broadcast em mudanças via trigger DB.
453
+
454
+ ## Ver também
455
+
456
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) — RLS sobre `realtime.messages` (SELECT + INSERT separados)
457
+ - [supabase-database-functions](../supabase-database-functions/SKILL.md) — trigger functions com `set search_path = ''`
458
+ - [supabase-auth-ssr](../supabase-auth-ssr/SKILL.md) — autenticação que habilita canais `private: true`
459
+ - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — Edge Functions disparando broadcast via `realtime.send`
460
+ - [glossário](../_shared-supabase/glossary.md) — termos PT-BR↔EN