@saulwade/swl-ses 1.0.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/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: go-testing
|
|
3
|
+
description: >
|
|
4
|
+
Testing Go con table-driven tests, testify, httptest y benchmarks. Cubre
|
|
5
|
+
subtests con t.Run, mocks con interfaces, tests concurrentes y profiling
|
|
6
|
+
básico. Cargar cuando se escriban tests en Go o se configure cobertura.
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
herramientasPermitidas: [Read, Grep]
|
|
9
|
+
exclusiones:
|
|
10
|
+
- "No cargar para implementar el código de producción que se va a testear — primero implementar con `go-experto`, luego escribir tests."
|
|
11
|
+
- "No cargar para diseñar la arquitectura de paquetes Go — para eso cargar `go-patrones`."
|
|
12
|
+
- "No cargar para errores de compilación Go en los tests — si el test no compila, cargar `build-errors-go`."
|
|
13
|
+
- "No cargar para benchmarking de base de datos o SQL — los benchmarks de BD tienen patrones de teardown que requieren el skill específico de BD."
|
|
14
|
+
evolvable: true # default para skill estandar
|
|
15
|
+
---
|
|
16
|
+
# Go Testing — Table-Driven Tests y Ecosystem de Testing
|
|
17
|
+
|
|
18
|
+
Cubre las prácticas de testing Go idiomático: table-driven tests, subtests,
|
|
19
|
+
testify para assertions, httptest para handlers HTTP, t.Parallel() y benchmarks.
|
|
20
|
+
|
|
21
|
+
## Cuándo cargar este skill
|
|
22
|
+
|
|
23
|
+
Invoca `Skill("go-testing")` cuando:
|
|
24
|
+
|
|
25
|
+
- Se escriban tests unitarios o de integración en Go
|
|
26
|
+
- Se testeen HTTP handlers con httptest
|
|
27
|
+
- Se configuren benchmarks o profiling con testing.B
|
|
28
|
+
- Se implementen mocks mediante interfaces
|
|
29
|
+
- Se use t.Parallel() para acelerar la suite
|
|
30
|
+
|
|
31
|
+
## Cuándo NO cargar
|
|
32
|
+
|
|
33
|
+
- El código de producción aún no está implementado — primero implementar con `go-experto`, luego escribir los tests.
|
|
34
|
+
- La pregunta es sobre diseñar la arquitectura de paquetes Go — para eso cargar `go-patrones`.
|
|
35
|
+
- El test no compila por un error de Go — cargar `build-errors-go` para diagnosticar el error de compilación.
|
|
36
|
+
- Los tests son de otro lenguaje — este skill cubre el ecosistema de testing de Go (table-driven, testify, httptest).
|
|
37
|
+
|
|
38
|
+
## Conceptos clave
|
|
39
|
+
|
|
40
|
+
### Table-Driven Tests
|
|
41
|
+
|
|
42
|
+
El patrón estándar Go: una slice de structs donde cada elemento es un caso de
|
|
43
|
+
prueba con nombre, inputs y resultado esperado. Cada caso se ejecuta con t.Run
|
|
44
|
+
como subtest independiente y paralelo.
|
|
45
|
+
|
|
46
|
+
### Interfaces como mecanismo de mock
|
|
47
|
+
|
|
48
|
+
Go no tiene reflection-based mocking por defecto. El patrón idiomático es
|
|
49
|
+
definir interfaces pequeñas y proporcionar implementaciones falsas en el package
|
|
50
|
+
de test.
|
|
51
|
+
|
|
52
|
+
### httptest para handlers HTTP
|
|
53
|
+
|
|
54
|
+
`httptest.NewRecorder()` captura la respuesta; `httptest.NewServer()` levanta
|
|
55
|
+
un servidor real para tests de integración HTTP.
|
|
56
|
+
|
|
57
|
+
## Reglas obligatorias
|
|
58
|
+
|
|
59
|
+
### Nombres de tabla descriptivos — usar campo "name" o "desc"
|
|
60
|
+
|
|
61
|
+
Cada caso en la tabla tiene un campo string que lo describe. El t.Run usa ese
|
|
62
|
+
campo como nombre del subtest, haciendo el output legible.
|
|
63
|
+
|
|
64
|
+
### t.Parallel() en tests independientes
|
|
65
|
+
|
|
66
|
+
Los tests sin estado compartido deben llamar `t.Parallel()` al inicio para
|
|
67
|
+
habilitar ejecución concurrente y reducir el tiempo total de la suite.
|
|
68
|
+
|
|
69
|
+
### Usar testify/assert — no comparacion manual con if
|
|
70
|
+
|
|
71
|
+
`assert.Equal` y `require.Equal` producen mensajes con diff; una comparación
|
|
72
|
+
manual con `if got != want { t.Errorf(...) }` omite el contexto.
|
|
73
|
+
`require` detiene el test en el primer fallo; `assert` acumula todos los fallos.
|
|
74
|
+
|
|
75
|
+
### Cerrar el body en tests de integracion HTTP
|
|
76
|
+
|
|
77
|
+
```go
|
|
78
|
+
resp, err := http.Get(ts.URL + "/v1/pedidos")
|
|
79
|
+
require.NoError(t, err)
|
|
80
|
+
defer resp.Body.Close() // SIEMPRE defer antes de leer
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Patrones recomendados
|
|
84
|
+
|
|
85
|
+
### Table-driven test completo
|
|
86
|
+
|
|
87
|
+
```go
|
|
88
|
+
func TestCalcularDescuento(t *testing.T) {
|
|
89
|
+
t.Parallel()
|
|
90
|
+
|
|
91
|
+
casos := []struct {
|
|
92
|
+
name string
|
|
93
|
+
precio float64
|
|
94
|
+
categoria string
|
|
95
|
+
esperado float64
|
|
96
|
+
}{
|
|
97
|
+
{name: "sin descuento para electronico", precio: 100, categoria: "electronico", esperado: 100},
|
|
98
|
+
{name: "10% descuento para ropa", precio: 200, categoria: "ropa", esperado: 180},
|
|
99
|
+
{name: "precio cero retorna cero", precio: 0, categoria: "ropa", esperado: 0},
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for _, tc := range casos {
|
|
103
|
+
tc := tc // captura de variable para t.Parallel() en subtests
|
|
104
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
105
|
+
t.Parallel()
|
|
106
|
+
got := calcularDescuento(tc.precio, tc.categoria)
|
|
107
|
+
assert.InDelta(t, tc.esperado, got, 0.001)
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Mock con interfaz
|
|
114
|
+
|
|
115
|
+
```go
|
|
116
|
+
// Interfaz en el package de producción
|
|
117
|
+
type PedidoRepository interface {
|
|
118
|
+
Obtener(ctx context.Context, id uuid.UUID) (*Pedido, error)
|
|
119
|
+
Guardar(ctx context.Context, p *Pedido) error
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Implementación falsa en el package de test
|
|
123
|
+
type repoFalso struct {
|
|
124
|
+
pedidos map[uuid.UUID]*Pedido
|
|
125
|
+
err error
|
|
126
|
+
}
|
|
127
|
+
func (r *repoFalso) Obtener(_ context.Context, id uuid.UUID) (*Pedido, error) {
|
|
128
|
+
if r.err != nil { return nil, r.err }
|
|
129
|
+
return r.pedidos[id], nil
|
|
130
|
+
}
|
|
131
|
+
func (r *repoFalso) Guardar(_ context.Context, p *Pedido) error {
|
|
132
|
+
r.pedidos[p.ID] = p
|
|
133
|
+
return r.err
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Uso en test
|
|
137
|
+
func TestPedidoService_Confirmar(t *testing.T) {
|
|
138
|
+
repo := &repoFalso{pedidos: make(map[uuid.UUID]*Pedido)}
|
|
139
|
+
svc := NewPedidoService(repo)
|
|
140
|
+
// ...
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### httptest con servidor real
|
|
145
|
+
|
|
146
|
+
```go
|
|
147
|
+
func TestPedidoHandler_Crear(t *testing.T) {
|
|
148
|
+
t.Parallel()
|
|
149
|
+
|
|
150
|
+
handler := NuevoRouter(pedidoHandlerFalso())
|
|
151
|
+
ts := httptest.NewServer(handler)
|
|
152
|
+
defer ts.Close()
|
|
153
|
+
|
|
154
|
+
body := strings.NewReader(`{"clienteId":"uuid-123","items":[]}`)
|
|
155
|
+
resp, err := http.Post(ts.URL+"/v1/pedidos", "application/json", body)
|
|
156
|
+
require.NoError(t, err)
|
|
157
|
+
defer resp.Body.Close()
|
|
158
|
+
|
|
159
|
+
assert.Equal(t, http.StatusCreated, resp.StatusCode)
|
|
160
|
+
|
|
161
|
+
var resultado map[string]interface{}
|
|
162
|
+
require.NoError(t, json.NewDecoder(resp.Body).Decode(&resultado))
|
|
163
|
+
assert.NotEmpty(t, resultado["id"])
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### httptest.NewRecorder para tests unitarios de handler
|
|
168
|
+
|
|
169
|
+
```go
|
|
170
|
+
func TestPedidoHandler_Obtener_NotFound(t *testing.T) {
|
|
171
|
+
repo := &repoFalso{err: ErrNotFound}
|
|
172
|
+
handler := NuevoPedidoHandler(NewPedidoService(repo))
|
|
173
|
+
|
|
174
|
+
req := httptest.NewRequest(http.MethodGet, "/v1/pedidos/uuid-404", nil)
|
|
175
|
+
rec := httptest.NewRecorder()
|
|
176
|
+
|
|
177
|
+
handler.Obtener(rec, req)
|
|
178
|
+
|
|
179
|
+
assert.Equal(t, http.StatusNotFound, rec.Code)
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Benchmark
|
|
184
|
+
|
|
185
|
+
```go
|
|
186
|
+
func BenchmarkCalcularDescuento(b *testing.B) {
|
|
187
|
+
b.ReportAllocs()
|
|
188
|
+
for i := 0; i < b.N; i++ {
|
|
189
|
+
calcularDescuento(100.0, "ropa")
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Correr con: `go test -bench=. -benchmem ./...`
|
|
195
|
+
|
|
196
|
+
### Golden files para outputs complejos
|
|
197
|
+
|
|
198
|
+
```go
|
|
199
|
+
func TestGenerarReporte(t *testing.T) {
|
|
200
|
+
got := generarReporte(pedidosDePrueba())
|
|
201
|
+
golden := filepath.Join("testdata", "reporte_esperado.json")
|
|
202
|
+
|
|
203
|
+
if *update { // flag: go test -update
|
|
204
|
+
os.WriteFile(golden, got, 0644)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
esperado, err := os.ReadFile(golden)
|
|
208
|
+
require.NoError(t, err)
|
|
209
|
+
assert.JSONEq(t, string(esperado), string(got))
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Anti-patrones conocidos
|
|
214
|
+
|
|
215
|
+
### Test sin nombre en tabla — output ilegible
|
|
216
|
+
|
|
217
|
+
```go
|
|
218
|
+
// MAL — falla como "--- FAIL: TestCalc/#02"
|
|
219
|
+
casos := []struct{ input, want float64 }{
|
|
220
|
+
{100, 80},
|
|
221
|
+
{0, 0},
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// BIEN — falla como "--- FAIL: TestCalc/precio_cero"
|
|
225
|
+
casos := []struct {
|
|
226
|
+
name string
|
|
227
|
+
input float64
|
|
228
|
+
want float64
|
|
229
|
+
}{
|
|
230
|
+
{name: "descuento estandar", input: 100, want: 80},
|
|
231
|
+
{name: "precio cero", input: 0, want: 0},
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Captura de variable de loop sin tc := tc
|
|
236
|
+
|
|
237
|
+
```go
|
|
238
|
+
// MAL — todos los subtests paralelos leen el mismo tc (el ultimo)
|
|
239
|
+
for _, tc := range casos {
|
|
240
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
241
|
+
t.Parallel()
|
|
242
|
+
// tc.input es siempre el ultimo caso
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// BIEN — captura local
|
|
247
|
+
for _, tc := range casos {
|
|
248
|
+
tc := tc
|
|
249
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
250
|
+
t.Parallel()
|
|
251
|
+
// tc.input es el caso correcto
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### io.Discard en lugar de leer el body en tests de integración
|
|
257
|
+
|
|
258
|
+
```go
|
|
259
|
+
// MAL — el body queda abierto y la conexión no se libera
|
|
260
|
+
resp, _ := http.Get(url)
|
|
261
|
+
// sin defer resp.Body.Close()
|
|
262
|
+
|
|
263
|
+
// BIEN
|
|
264
|
+
resp, err := http.Get(url)
|
|
265
|
+
require.NoError(t, err)
|
|
266
|
+
defer resp.Body.Close()
|
|
267
|
+
io.Copy(io.Discard, resp.Body) // drenar si no se necesita el contenido
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Checklist de verificación
|
|
271
|
+
|
|
272
|
+
- [ ] Todos los casos de tabla tienen campo "name" descriptivo
|
|
273
|
+
- [ ] tc := tc antes de t.Parallel() en subtests con tabla
|
|
274
|
+
- [ ] defer resp.Body.Close() en todo test que hace request HTTP
|
|
275
|
+
- [ ] Mocks implementan interfaces pequeñas, no structs completos
|
|
276
|
+
- [ ] require para fallos que invalidan el resto del test; assert para los demás
|
|
277
|
+
- [ ] Benchmarks con b.ReportAllocs() para detectar allocaciones innecesarias
|
|
278
|
+
- [ ] t.Parallel() en tests sin estado compartido
|
|
279
|
+
|
|
280
|
+
## Gotchas / Errores comunes no obvios
|
|
281
|
+
|
|
282
|
+
**`tc := tc` antes de `t.Parallel()` en table-driven tests con closures**: sin esta captura, todos los goroutines del subtest comparten la variable de loop y leen el último valor al ejecutarse. Go 1.22+ corrigió el comportamiento de loop variables, pero en proyectos con `go 1.21` o menor en `go.mod` sigue siendo necesario. Fix: verificar la versión de Go en `go.mod`; si es < 1.22, agregar `tc := tc` antes de `t.Parallel()` en todos los table-driven tests.
|
|
283
|
+
|
|
284
|
+
**`httptest.NewRecorder()` no replica el comportamiento de flujo de `http.ResponseWriter`**: `ResponseRecorder` no llama `WriteHeader(200)` implícitamente hasta que se llama `Write()`, mientras que un cliente real sí recibe el status code. Un handler que usa `w.WriteHeader()` seguido de `w.Write()` funciona diferente si se inspecciona el recorder antes de que el handler termine. Fix: siempre inspeccionar `recorder.Result()` después de que el handler ha terminado, no durante.
|
|
285
|
+
|
|
286
|
+
**`t.Cleanup()` registrado dentro de un `t.Run()` solo aplica al subtest, no al test padre**: si un helper registra `t.Cleanup(...)` con el `t` del padre pero el recurso se crea en un subtest, el cleanup puede ejecutarse antes de que el subtest termine. Fix: pasar el `t` correcto (del subtest) al helper que registra el cleanup, no el `t` del test padre.
|
|
287
|
+
|
|
288
|
+
**Benchmarks con `b.StopTimer()`/`b.StartTimer()` que no incluyen el setup inicial miden erróneamente**: si el setup costoso está fuera de la función de benchmark (en `b.ResetTimer()` al inicio), las primeras iteraciones incluyen tiempo de calentamiento. Fix: usar `b.ResetTimer()` inmediatamente después del setup costoso para que las `b.N` iteraciones empiecen desde cero.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
*Skill creado con swl:crear-skill el 2026-03-31. Versión 1.0.0.*
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: graphql-experto
|
|
3
|
+
description: >
|
|
4
|
+
GraphQL: schema design, resolvers, DataLoader para N+1, subscriptions,
|
|
5
|
+
paginación Relay, federation y Apollo Server/Client. Cargar cuando se diseñe
|
|
6
|
+
un schema GraphQL, se implementen resolvers o se configure Apollo.
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
herramientasPermitidas: [Read, Grep]
|
|
9
|
+
exclusiones:
|
|
10
|
+
- "No cargar para APIs REST — GraphQL y REST son paradigmas de diseño de API distintos; si el proyecto usa REST cargar `api-rest-diseno` o el skill del framework backend."
|
|
11
|
+
- "No cargar para configurar Apollo Client en el frontend React/Next.js sin contexto del schema — si la pregunta es solo sobre configuración del cliente, no del schema o resolvers del servidor."
|
|
12
|
+
- "No cargar para diseñar el esquema relacional de la base de datos que subyace al schema GraphQL — el schema GraphQL y el esquema SQL son capas distintas; para el esquema SQL cargar `postgresql-experto` o `dbml-experto`."
|
|
13
|
+
- "No cargar para errores de TypeScript en tipos generados por GraphQL Codegen — esos son errores de generación de código; revisar el schema GraphQL fuente y la configuración de codegen."
|
|
14
|
+
evolvable: true # default para skill estandar
|
|
15
|
+
---
|
|
16
|
+
# GraphQL Experto — Schema Design y Resolvers
|
|
17
|
+
|
|
18
|
+
GraphQL es un contrato entre cliente y servidor. Un schema bien diseñado permite
|
|
19
|
+
evolucionar la API sin versionar. Este skill cubre schema-first vs code-first,
|
|
20
|
+
patrones de resolvers, DataLoader para el problema N+1, paginación con cursor
|
|
21
|
+
y autenticación en el contexto de Apollo Server.
|
|
22
|
+
|
|
23
|
+
## Cuándo cargar este skill
|
|
24
|
+
|
|
25
|
+
Invoca `Skill("graphql-experto")` cuando:
|
|
26
|
+
|
|
27
|
+
- Se diseñe un schema GraphQL nuevo (tipos, queries, mutations, subscriptions)
|
|
28
|
+
- Se implementen resolvers con acceso a BD
|
|
29
|
+
- Se configure Apollo Server o un servidor GraphQL similar
|
|
30
|
+
- Se solucione el problema N+1 en resolvers
|
|
31
|
+
- Se implemente paginación cursor-based o Relay spec
|
|
32
|
+
- Se configure Apollo Client en el frontend
|
|
33
|
+
|
|
34
|
+
## Cuándo NO cargar
|
|
35
|
+
|
|
36
|
+
- El proyecto usa REST — GraphQL y REST son paradigmas distintos; si el proyecto usa endpoints REST cargar `api-rest-diseno` o el skill del framework backend.
|
|
37
|
+
- La pregunta es solo sobre configurar Apollo Client en el frontend sin contexto del schema — si no hay diseño de schema o resolvers del servidor, este skill aporta poco.
|
|
38
|
+
- La pregunta es sobre diseñar el esquema relacional de la BD que subyace al schema GraphQL — el schema GraphQL y el esquema SQL son capas distintas; para el esquema SQL cargar `postgresql-experto`.
|
|
39
|
+
- Los errores son de tipos generados por GraphQL Codegen — esos son errores de generación de código; revisar el schema GraphQL fuente y la configuración de codegen.
|
|
40
|
+
|
|
41
|
+
## Conceptos clave
|
|
42
|
+
|
|
43
|
+
### Schema-first vs Code-first
|
|
44
|
+
|
|
45
|
+
Schema-first: se escribe el SDL (Schema Definition Language) primero, los resolvers
|
|
46
|
+
lo implementan. Ventaja: el contrato es claro desde el inicio. Code-first
|
|
47
|
+
(ej: TypeGraphQL, Pothos): el schema se genera desde las clases/funciones.
|
|
48
|
+
Ventaja: un solo source of truth en TypeScript.
|
|
49
|
+
|
|
50
|
+
### DataLoader — solución al N+1
|
|
51
|
+
|
|
52
|
+
Cada campo resuelto en GraphQL puede generar queries independientes. DataLoader
|
|
53
|
+
agrupa múltiples resoluciones del mismo tipo en una sola query batch, ejecutada
|
|
54
|
+
al final del tick de evento. Es la solución estándar al N+1 en GraphQL.
|
|
55
|
+
|
|
56
|
+
### Contexto de Apollo Server
|
|
57
|
+
|
|
58
|
+
El contexto se crea por request y se pasa a todos los resolvers. Es el lugar
|
|
59
|
+
correcto para poner: el usuario autenticado, instancias de DataLoader, y el
|
|
60
|
+
cliente de BD. Los DataLoaders DEBEN crearse en el contexto (nuevo por request),
|
|
61
|
+
no como singletons globales.
|
|
62
|
+
|
|
63
|
+
## Reglas obligatorias
|
|
64
|
+
|
|
65
|
+
### DataLoader OBLIGATORIO para resolver relaciones en listas
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// MAL — N+1: por cada post se hace una query para el autor
|
|
69
|
+
const resolvers = {
|
|
70
|
+
Post: {
|
|
71
|
+
autor: async (post) => {
|
|
72
|
+
return db.usuario.findUnique({ where: { id: post.autorId } });
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// BIEN — DataLoader agrupa los IDs en una sola query batch
|
|
78
|
+
// En el contexto:
|
|
79
|
+
const context = async ({ req }) => ({
|
|
80
|
+
usuarioLoader: new DataLoader(async (ids: readonly string[]) => {
|
|
81
|
+
const usuarios = await db.usuario.findMany({
|
|
82
|
+
where: { id: { in: [...ids] } },
|
|
83
|
+
});
|
|
84
|
+
const mapa = new Map(usuarios.map(u => [u.id, u]));
|
|
85
|
+
return ids.map(id => mapa.get(id) ?? null);
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// En el resolver:
|
|
90
|
+
Post: {
|
|
91
|
+
autor: (post, _, { usuarioLoader }) => usuarioLoader.load(post.autorId),
|
|
92
|
+
},
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Autenticación en el contexto — nunca en cada resolver individualmente
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// MAL — verificar auth en cada resolver
|
|
99
|
+
const resolvers = {
|
|
100
|
+
Query: {
|
|
101
|
+
miPerfil: async (_, __, { req }) => {
|
|
102
|
+
const token = req.headers.authorization;
|
|
103
|
+
const usuario = verificarToken(token); // duplicado en cada resolver
|
|
104
|
+
if (!usuario) throw new GraphQLError('No autenticado');
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// BIEN — una vez en el contexto
|
|
110
|
+
const context = async ({ req }) => {
|
|
111
|
+
const token = req.headers.authorization?.split(' ')[1];
|
|
112
|
+
const usuario = token ? verificarToken(token) : null;
|
|
113
|
+
return { usuario, db, usuarioLoader: crearUsuarioLoader() };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Resolver usa el usuario del contexto
|
|
117
|
+
Query: {
|
|
118
|
+
miPerfil: (_, __, { usuario }) => {
|
|
119
|
+
if (!usuario) throw new GraphQLError('No autenticado',
|
|
120
|
+
{ extensions: { code: 'UNAUTHENTICATED' } });
|
|
121
|
+
return usuario;
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Validación de inputs con tipos estrictos en el schema
|
|
127
|
+
|
|
128
|
+
```graphql
|
|
129
|
+
# MAL — input sin restricciones
|
|
130
|
+
input CrearProductoInput {
|
|
131
|
+
nombre: String # permite null, string vacio, etc.
|
|
132
|
+
precio: Float
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# BIEN — tipos no-nullable donde aplica, scalares custom para restricciones
|
|
136
|
+
scalar PositiveFloat
|
|
137
|
+
scalar NonEmptyString
|
|
138
|
+
|
|
139
|
+
input CrearProductoInput {
|
|
140
|
+
nombre: NonEmptyString!
|
|
141
|
+
precio: PositiveFloat!
|
|
142
|
+
categoriaId: ID!
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Paginación cursor-based (Relay spec) para listas grandes
|
|
147
|
+
|
|
148
|
+
```graphql
|
|
149
|
+
type ProductoConnection {
|
|
150
|
+
edges: [ProductoEdge!]!
|
|
151
|
+
pageInfo: PageInfo!
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type ProductoEdge {
|
|
155
|
+
node: Producto!
|
|
156
|
+
cursor: String!
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type PageInfo {
|
|
160
|
+
hasNextPage: Boolean!
|
|
161
|
+
hasPreviousPage: Boolean!
|
|
162
|
+
startCursor: String
|
|
163
|
+
endCursor: String
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type Query {
|
|
167
|
+
productos(first: Int, after: String, last: Int, before: String): ProductoConnection!
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Patrones recomendados
|
|
172
|
+
|
|
173
|
+
### Schema SDL con organización por dominio
|
|
174
|
+
|
|
175
|
+
```graphql
|
|
176
|
+
# schema/producto.graphql
|
|
177
|
+
type Producto {
|
|
178
|
+
id: ID!
|
|
179
|
+
nombre: String!
|
|
180
|
+
precio: Float!
|
|
181
|
+
stock: Int!
|
|
182
|
+
categoria: Categoria!
|
|
183
|
+
resenas: [Resena!]!
|
|
184
|
+
creadoEn: DateTime!
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
type Query {
|
|
188
|
+
producto(id: ID!): Producto
|
|
189
|
+
productos(filtros: FiltrosProductoInput, paginacion: PaginacionInput): ProductoConnection!
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
type Mutation {
|
|
193
|
+
crearProducto(input: CrearProductoInput!): CrearProductoPayload!
|
|
194
|
+
actualizarProducto(id: ID!, input: ActualizarProductoInput!): ActualizarProductoPayload!
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Error handling con union types
|
|
199
|
+
|
|
200
|
+
```graphql
|
|
201
|
+
# En lugar de lanzar excepciones, modelar errores como tipos
|
|
202
|
+
union CrearProductoPayload = ProductoCreado | ErrorValidacion | ErrorAutorizacion
|
|
203
|
+
|
|
204
|
+
type ProductoCreado { producto: Producto! }
|
|
205
|
+
type ErrorValidacion { campo: String!, mensaje: String! }
|
|
206
|
+
type ErrorAutorizacion { mensaje: String! }
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Resolver retorna el tipo correcto segun el caso
|
|
211
|
+
Mutation: {
|
|
212
|
+
crearProducto: async (_, { input }, { usuario }) => {
|
|
213
|
+
if (!usuario?.esAdmin) return { __typename: 'ErrorAutorizacion', mensaje: 'Sin permiso' };
|
|
214
|
+
const validacion = validarInput(input);
|
|
215
|
+
if (!validacion.ok) return { __typename: 'ErrorValidacion', ...validacion };
|
|
216
|
+
const producto = await db.producto.create({ data: input });
|
|
217
|
+
return { __typename: 'ProductoCreado', producto };
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Subscriptions con filtrado por usuario
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const resolvers = {
|
|
226
|
+
Subscription: {
|
|
227
|
+
pedidoActualizado: {
|
|
228
|
+
subscribe: withFilter(
|
|
229
|
+
() => pubsub.asyncIterator(['PEDIDO_ACTUALIZADO']),
|
|
230
|
+
(payload, variables, context) =>
|
|
231
|
+
payload.pedidoActualizado.usuarioId === context.usuario?.id
|
|
232
|
+
),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Depth limiting y complexity limits
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import depthLimit from 'graphql-depth-limit';
|
|
242
|
+
import { createComplexityLimitRule } from 'graphql-validation-complexity';
|
|
243
|
+
|
|
244
|
+
const server = new ApolloServer({
|
|
245
|
+
typeDefs,
|
|
246
|
+
resolvers,
|
|
247
|
+
validationRules: [
|
|
248
|
+
depthLimit(7), // Maximo 7 niveles de anidamiento
|
|
249
|
+
createComplexityLimitRule(1000), // Score de complejidad maximo
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Anti-patrones conocidos
|
|
255
|
+
|
|
256
|
+
### Resolver con lógica de negocio — usar services
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// MAL — logica de negocio en el resolver
|
|
260
|
+
Mutation: {
|
|
261
|
+
crearPedido: async (_, { items }, { usuario, db }) => {
|
|
262
|
+
// 40 lineas de calculo de descuentos, validacion de stock, etc.
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
// BIEN — resolver delega a un service
|
|
267
|
+
Mutation: {
|
|
268
|
+
crearPedido: async (_, { items }, { usuario, pedidoService }) => {
|
|
269
|
+
return pedidoService.crear({ usuario, items });
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Mutations sin payload tipado
|
|
275
|
+
|
|
276
|
+
```graphql
|
|
277
|
+
# MAL — mutation que solo retorna Boolean o el objeto
|
|
278
|
+
type Mutation {
|
|
279
|
+
eliminarProducto(id: ID!): Boolean
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# BIEN — payload con información útil y manejo de errores
|
|
283
|
+
type Mutation {
|
|
284
|
+
eliminarProducto(id: ID!): EliminarProductoPayload!
|
|
285
|
+
}
|
|
286
|
+
union EliminarProductoPayload = ProductoEliminado | ErrorNotFound
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### DataLoader como singleton global
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// MAL — comparte cache entre requests de distintos usuarios
|
|
293
|
+
const usuarioLoader = new DataLoader(batchFn); // modulo global
|
|
294
|
+
|
|
295
|
+
// BIEN — nuevo por request en el contexto
|
|
296
|
+
const context = async ({ req }) => ({
|
|
297
|
+
usuarioLoader: new DataLoader(batchFn),
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Gotchas / Errores comunes no obvios
|
|
302
|
+
|
|
303
|
+
**DataLoader retorna `null` para IDs no encontrados pero el resolver espera el objeto**: si la función batch de DataLoader retorna `ids.map(id => mapa.get(id) ?? null)` y el resolver tipado espera `User` (no nullable), GraphQL lanza un error de tipo en runtime aunque el campo del schema sea `User` (no `User!`). Causa: un `null` en DataLoader se propaga como valor del campo aunque no se haya declarado como nullable en el schema. Fix: si el campo es non-nullable en el schema (`User!`), la función batch debe lanzar un error para IDs no encontrados en lugar de retornar `null`, o usar `notFound: true` si la librería lo soporta.
|
|
304
|
+
|
|
305
|
+
**`context` del Apollo Server no se re-crea entre operaciones en subscriptions**: en queries y mutations, la función `context()` se llama una vez por request. En subscriptions WebSocket, el contexto se crea cuando el cliente conecta y persiste mientras dura la conexión — no por cada mensaje. Causa: WebSocket es una conexión persistente, no un request/response. Fix: si el contexto necesita datos frescos por operación (ej: validar token en cada mensaje), usar el hook `onOperation` del transporte WebSocket para re-validar.
|
|
306
|
+
|
|
307
|
+
**Mutations con `__typename` en union types no funciona si el resolver no lo retorna explícitamente**: `{ __typename: 'ErrorValidacion', campo: 'email', mensaje: '...' }` puede fallar si Apollo no puede resolver el `__typename` automáticamente del objeto retornado sin `resolveType`. Causa: para union types, Apollo necesita saber a qué tipo mapear el objeto — lo detecta por `__typename` en el objeto o por la función `__resolveType` en el resolver. Fix: siempre incluir `__typename` explícitamente en el objeto retornado o definir `__resolveType` en el resolver de la union.
|
|
308
|
+
|
|
309
|
+
**`depthLimit(N)` cuenta fragmentos anidados una vez, no por uso**: si un fragmento `F` se usa 3 veces en una query pero el fragmento en sí tiene profundidad 3, `depthLimit` cuenta la profundidad del fragmento una vez, no multiplica. Pero inline fragments y `... on Tipo` sí cuentan en la profundidad. Causa: la librería `graphql-depth-limit` analiza el documento AST con reglas específicas. Fix: complementar depth limit con complexity limit para capturar queries que abusan de fragmentos reutilizados para multiplicar la carga sin exceder la profundidad.
|
|
310
|
+
|
|
311
|
+
## Checklist de verificación
|
|
312
|
+
|
|
313
|
+
- [ ] DataLoader para cada relación que se resuelve en listas
|
|
314
|
+
- [ ] DataLoaders creados en el contexto (uno por request, no globales)
|
|
315
|
+
- [ ] Autenticación verificada en el contexto, no en cada resolver
|
|
316
|
+
- [ ] Inputs con tipos no-nullable donde el campo es requerido
|
|
317
|
+
- [ ] Paginación cursor-based para colecciones grandes
|
|
318
|
+
- [ ] Depth limit y complexity limit configurados
|
|
319
|
+
- [ ] Error handling con union types, no solo excepciones
|
|
320
|
+
|
|
321
|
+
## Referencias
|
|
322
|
+
|
|
323
|
+
- [GraphQL Spec](https://spec.graphql.org) | [Apollo Server](https://www.apollographql.com/docs/apollo-server) | [DataLoader](https://github.com/graphql/dataloader)
|