opencode-skills-collection 4.0.52 → 4.0.54
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/bundled-skills/.antigravity-install-manifest.json +6 -2
- package/bundled-skills/00-andruia-consultant/SKILL.md +6 -0
- package/bundled-skills/007/SKILL.md +2 -610
- package/bundled-skills/007/references/detailed-guide.md +647 -0
- package/bundled-skills/10-andruia-skill-smith/SKILL.md +6 -0
- package/bundled-skills/20-andruia-niche-intelligence/SKILL.md +6 -0
- package/bundled-skills/2slides-ppt-generator/SKILL.md +2 -741
- package/bundled-skills/2slides-ppt-generator/references/detailed-guide.md +761 -0
- package/bundled-skills/ab-test-setup/SKILL.md +59 -27
- package/bundled-skills/ab-testing/SKILL.md +1 -1
- package/bundled-skills/acceptance-orchestrator/SKILL.md +6 -0
- package/bundled-skills/accint-commitments/SKILL.md +6 -0
- package/bundled-skills/accint-frames/SKILL.md +6 -0
- package/bundled-skills/accint-solve/SKILL.md +6 -0
- package/bundled-skills/advogado-criminal/SKILL.md +2 -912
- package/bundled-skills/advogado-criminal/references/detailed-guide.md +946 -0
- package/bundled-skills/advogado-especialista/SKILL.md +2 -1071
- package/bundled-skills/advogado-especialista/references/detailed-guide.md +1114 -0
- package/bundled-skills/agent-evaluation/SKILL.md +53 -1102
- package/bundled-skills/agent-evaluation/references/architecture-sketches.md +1112 -0
- package/bundled-skills/agent-memory/SKILL.md +6 -0
- package/bundled-skills/agent-memory-systems/SKILL.md +8 -1042
- package/bundled-skills/agent-memory-systems/references/detailed-guide.md +1088 -0
- package/bundled-skills/agent-squad/SKILL.md +1 -0
- package/bundled-skills/agent-tool-builder/SKILL.md +2 -624
- package/bundled-skills/agent-tool-builder/references/detailed-guide.md +650 -0
- package/bundled-skills/agentic-actions-auditor/SKILL.md +6 -0
- package/bundled-skills/agentmail/SKILL.md +1 -0
- package/bundled-skills/agentphone/SKILL.md +5 -1323
- package/bundled-skills/agentphone/references/detailed-guide.md +1333 -0
- package/bundled-skills/agents-v2-py/SKILL.md +23 -5
- package/bundled-skills/ai-agents-architect/SKILL.md +25 -25
- package/bundled-skills/ai-analyzer/SKILL.md +1 -0
- package/bundled-skills/ai-md/SKILL.md +25 -0
- package/bundled-skills/ai-md/references/detailed-guide.md +512 -0
- package/bundled-skills/ai-product/SKILL.md +2 -726
- package/bundled-skills/ai-product/references/detailed-guide.md +746 -0
- package/bundled-skills/ai-wrapper-product/SKILL.md +2 -641
- package/bundled-skills/ai-wrapper-product/references/detailed-guide.md +659 -0
- package/bundled-skills/airtable-automation/SKILL.md +6 -0
- package/bundled-skills/algolia-search/SKILL.md +2 -893
- package/bundled-skills/algolia-search/references/detailed-guide.md +901 -0
- package/bundled-skills/alpha-vantage/SKILL.md +1 -0
- package/bundled-skills/alternatives-pages/SKILL.md +7 -1
- package/bundled-skills/amazon-alexa/SKILL.md +2 -627
- package/bundled-skills/amazon-alexa/references/detailed-guide.md +644 -0
- package/bundled-skills/analytics/SKILL.md +1 -1
- package/bundled-skills/analytics-product/SKILL.md +57 -44
- package/bundled-skills/analytics-tracking/SKILL.md +21 -12
- package/bundled-skills/analyze-project/SKILL.md +1 -0
- package/bundled-skills/android-dev/SKILL.md +2 -490
- package/bundled-skills/android-dev/references/detailed-guide.md +506 -0
- package/bundled-skills/angular/SKILL.md +4 -783
- package/bundled-skills/angular/references/detailed-guide.md +801 -0
- package/bundled-skills/angular-best-practices/SKILL.md +4 -535
- package/bundled-skills/angular-best-practices/references/detailed-guide.md +549 -0
- package/bundled-skills/angular-state-management/SKILL.md +4 -608
- package/bundled-skills/angular-state-management/references/detailed-guide.md +618 -0
- package/bundled-skills/angular-ui-patterns/SKILL.md +2 -498
- package/bundled-skills/angular-ui-patterns/references/detailed-guide.md +513 -0
- package/bundled-skills/animejs-animation/SKILL.md +6 -0
- package/bundled-skills/anti-deception/SKILL.md +7 -1
- package/bundled-skills/antigravity-design-expert/SKILL.md +6 -0
- package/bundled-skills/antigravity-maintainer-batch-release/SKILL.md +7 -0
- package/bundled-skills/antigravity-workflows/SKILL.md +20 -3
- package/bundled-skills/antigravity-workflows/references/workflow-cards.md +78 -0
- package/bundled-skills/api-analyzer/SKILL.md +1 -1
- package/bundled-skills/api-designer/SKILL.md +1 -1
- package/bundled-skills/api-integration/SKILL.md +1 -1
- package/bundled-skills/api-onboarding/SKILL.md +1 -1
- package/bundled-skills/api-patterns/SKILL.md +19 -8
- package/bundled-skills/api-patterns/api-style.md +2 -2
- package/bundled-skills/api-patterns/auth.md +3 -2
- package/bundled-skills/api-patterns/graphql.md +1 -1
- package/bundled-skills/api-patterns/scripts/api_validator.py +9 -7
- package/bundled-skills/api-patterns/trpc.md +2 -2
- package/bundled-skills/api-patterns/versioning.md +1 -1
- package/bundled-skills/api-sdk-generator/SKILL.md +1 -1
- package/bundled-skills/api-security-best-practices/SKILL.md +179 -879
- package/bundled-skills/apify-actor-development/SKILL.md +1 -0
- package/bundled-skills/apify-actorization/SKILL.md +1 -0
- package/bundled-skills/apify-audience-analysis/SKILL.md +1 -0
- package/bundled-skills/apify-brand-reputation-monitoring/SKILL.md +1 -0
- package/bundled-skills/apify-competitor-intelligence/SKILL.md +1 -0
- package/bundled-skills/apify-content-analytics/SKILL.md +1 -0
- package/bundled-skills/apify-ecommerce/SKILL.md +1 -0
- package/bundled-skills/apify-influencer-discovery/SKILL.md +1 -0
- package/bundled-skills/apify-lead-generation/SKILL.md +1 -0
- package/bundled-skills/apify-market-research/SKILL.md +1 -0
- package/bundled-skills/apify-trend-analysis/SKILL.md +1 -0
- package/bundled-skills/apify-ultimate-scraper/SKILL.md +1 -0
- package/bundled-skills/appium-skill/SKILL.md +1 -1
- package/bundled-skills/apple-notes-search/SKILL.md +6 -0
- package/bundled-skills/application-performance-performance-optimization/SKILL.md +6 -0
- package/bundled-skills/applicationinsights-web-ts/SKILL.md +1 -1
- package/bundled-skills/ask-matt/SKILL.md +5 -0
- package/bundled-skills/ask-questions-if-underspecified/SKILL.md +1 -0
- package/bundled-skills/astropy/SKILL.md +1 -0
- package/bundled-skills/atlas-contract/SKILL.md +117 -0
- package/bundled-skills/atlas-contract/references/detailed-guide.md +572 -0
- package/bundled-skills/audio-transcriber/SKILL.md +2 -425
- package/bundled-skills/audio-transcriber/references/detailed-guide.md +430 -0
- package/bundled-skills/audit-context-building/SKILL.md +1 -0
- package/bundled-skills/auri-core/SKILL.md +5 -562
- package/bundled-skills/auri-core/references/detailed-guide.md +623 -0
- package/bundled-skills/auth-implementation-patterns/SKILL.md +14 -4
- package/bundled-skills/auth-implementation-patterns/resources/implementation-playbook.md +72 -180
- package/bundled-skills/automated-triage/SKILL.md +6 -0
- package/bundled-skills/autonomous-agent-patterns/SKILL.md +4 -741
- package/bundled-skills/autonomous-agent-patterns/references/detailed-guide.md +751 -0
- package/bundled-skills/autonomous-agents/SKILL.md +8 -1010
- package/bundled-skills/autonomous-agents/references/detailed-guide.md +1065 -0
- package/bundled-skills/awareness-stage-mapper/SKILL.md +6 -0
- package/bundled-skills/aws-agentic-ai/SKILL.md +7 -1
- package/bundled-skills/aws-cdk-development/SKILL.md +1 -1
- package/bundled-skills/aws-cost-operations/SKILL.md +1 -1
- package/bundled-skills/aws-mcp-setup/SKILL.md +1 -1
- package/bundled-skills/aws-serverless/SKILL.md +2 -1305
- package/bundled-skills/aws-serverless/references/detailed-guide.md +1340 -0
- package/bundled-skills/aws-serverless-eda/SKILL.md +1 -1
- package/bundled-skills/aws-skills/SKILL.md +6 -0
- package/bundled-skills/aws-sst-development/SKILL.md +7 -1
- package/bundled-skills/awt-e2e-testing/SKILL.md +7 -0
- package/bundled-skills/azure-functions/SKILL.md +2 -1321
- package/bundled-skills/azure-functions/references/detailed-guide.md +1356 -0
- package/bundled-skills/azure-maps-search-dotnet/SKILL.md +2 -482
- package/bundled-skills/azure-maps-search-dotnet/references/detailed-guide.md +496 -0
- package/bundled-skills/azure-search-documents-py/SKILL.md +2 -515
- package/bundled-skills/azure-search-documents-py/references/detailed-guide.md +548 -0
- package/bundled-skills/azure-storage-file-share-ts/SKILL.md +2 -481
- package/bundled-skills/azure-storage-file-share-ts/references/detailed-guide.md +500 -0
- package/bundled-skills/azure-storage-queue-ts/SKILL.md +2 -512
- package/bundled-skills/azure-storage-queue-ts/references/detailed-guide.md +530 -0
- package/bundled-skills/backend-development-feature-development/SKILL.md +6 -0
- package/bundled-skills/basecamp-automation/SKILL.md +6 -0
- package/bundled-skills/baseline-ui/SKILL.md +6 -0
- package/bundled-skills/bash-pro/SKILL.md +6 -0
- package/bundled-skills/bdi-mental-states/SKILL.md +1 -0
- package/bundled-skills/beautiful-prose/SKILL.md +1 -0
- package/bundled-skills/bill-gates/SKILL.md +2 -778
- package/bundled-skills/bill-gates/references/detailed-guide.md +784 -0
- package/bundled-skills/biopython/SKILL.md +1 -0
- package/bundled-skills/bitbucket-automation/SKILL.md +6 -0
- package/bundled-skills/blog-writing-guide/SKILL.md +6 -0
- package/bundled-skills/box-automation/SKILL.md +6 -0
- package/bundled-skills/brain-to-docs/SKILL.md +6 -0
- package/bundled-skills/brainstorming/SKILL.md +6 -0
- package/bundled-skills/brand-guidelines/SKILL.md +1 -0
- package/bundled-skills/brand-guidelines-anthropic/SKILL.md +23 -8
- package/bundled-skills/brand-guidelines-community/SKILL.md +23 -8
- package/bundled-skills/brand-perception-psychologist/SKILL.md +6 -0
- package/bundled-skills/brooks-audit/SKILL.md +1 -1
- package/bundled-skills/brooks-debt/SKILL.md +1 -1
- package/bundled-skills/brooks-harness/SKILL.md +1 -1
- package/bundled-skills/brooks-review/SKILL.md +7 -1
- package/bundled-skills/brooks-sweep/SKILL.md +1 -1
- package/bundled-skills/brooks-test/SKILL.md +1 -1
- package/bundled-skills/browser-automation/SKILL.md +18 -1044
- package/bundled-skills/browser-automation/references/detailed-guide.md +852 -0
- package/bundled-skills/bug-hunt-swarm/SKILL.md +7 -1
- package/bundled-skills/build/SKILL.md +5 -627
- package/bundled-skills/build/references/detailed-guide.md +637 -0
- package/bundled-skills/bun-development/SKILL.md +4 -678
- package/bundled-skills/bun-development/references/detailed-guide.md +691 -0
- package/bundled-skills/burpsuite-project-parser/SKILL.md +1 -0
- package/bundled-skills/c-pro/SKILL.md +6 -0
- package/bundled-skills/calendly-automation/SKILL.md +6 -0
- package/bundled-skills/cc-skill-backend-patterns/SKILL.md +2 -572
- package/bundled-skills/cc-skill-backend-patterns/references/detailed-guide.md +584 -0
- package/bundled-skills/cc-skill-coding-standards/SKILL.md +2 -510
- package/bundled-skills/cc-skill-coding-standards/references/detailed-guide.md +523 -0
- package/bundled-skills/cc-skill-continuous-learning/SKILL.md +49 -7
- package/bundled-skills/cc-skill-continuous-learning/config.json +1 -16
- package/bundled-skills/cc-skill-continuous-learning/evaluate-session.sh +56 -59
- package/bundled-skills/cc-skill-frontend-patterns/SKILL.md +2 -621
- package/bundled-skills/cc-skill-frontend-patterns/references/detailed-guide.md +633 -0
- package/bundled-skills/cc-skill-security-review/SKILL.md +4 -36
- package/bundled-skills/cc-skill-security-review/references/detailed-guide.md +40 -0
- package/bundled-skills/cc-skill-strategic-compact/SKILL.md +53 -7
- package/bundled-skills/cc-skill-strategic-compact/suggest-compact.sh +20 -51
- package/bundled-skills/changelog-updates/SKILL.md +6 -528
- package/bundled-skills/changelog-updates/references/detailed-guide.md +540 -0
- package/bundled-skills/chat-widget/SKILL.md +5 -879
- package/bundled-skills/chat-widget/references/detailed-guide.md +890 -0
- package/bundled-skills/cirq/SKILL.md +1 -0
- package/bundled-skills/citation-management/SKILL.md +3 -960
- package/bundled-skills/citation-management/references/detailed-guide.md +975 -0
- package/bundled-skills/ckw-design/SKILL.md +6 -0
- package/bundled-skills/claimable-postgres/SKILL.md +1 -1
- package/bundled-skills/clarity-gate/SKILL.md +3 -635
- package/bundled-skills/clarity-gate/references/detailed-guide.md +653 -0
- package/bundled-skills/claude-ally-health/SKILL.md +6 -0
- package/bundled-skills/claude-code-expert/SKILL.md +2 -527
- package/bundled-skills/claude-code-expert/references/detailed-guide.md +548 -0
- package/bundled-skills/claude-d3js-skill/SKILL.md +2 -798
- package/bundled-skills/claude-d3js-skill/references/detailed-guide.md +811 -0
- package/bundled-skills/claude-in-chrome-troubleshooting/SKILL.md +1 -0
- package/bundled-skills/claude-scientific-skills/SKILL.md +6 -0
- package/bundled-skills/claude-settings-audit/SKILL.md +1 -0
- package/bundled-skills/claude-speed-reader/SKILL.md +6 -0
- package/bundled-skills/claude-win11-speckit-update-skill/SKILL.md +6 -0
- package/bundled-skills/clean-code/SKILL.md +6 -0
- package/bundled-skills/clerk-auth/SKILL.md +2 -812
- package/bundled-skills/clerk-auth/references/detailed-guide.md +820 -0
- package/bundled-skills/clickup-automation/SKILL.md +6 -0
- package/bundled-skills/closed-loop-delivery/SKILL.md +6 -0
- package/bundled-skills/co-marketing/SKILL.md +1 -1
- package/bundled-skills/code-documentation-doc-generate/SKILL.md +16 -1
- package/bundled-skills/code-documentation-doc-generate/resources/implementation-playbook.md +33 -55
- package/bundled-skills/code-refactoring-context-restore/SKILL.md +40 -177
- package/bundled-skills/code-refactoring-tech-debt/SKILL.md +20 -9
- package/bundled-skills/code-simplifier/SKILL.md +1 -0
- package/bundled-skills/codebase-cleanup-tech-debt/SKILL.md +20 -9
- package/bundled-skills/cold-email/SKILL.md +6 -0
- package/bundled-skills/commit/SKILL.md +1 -0
- package/bundled-skills/community-building/SKILL.md +1 -1
- package/bundled-skills/competitor-alternatives/SKILL.md +2 -740
- package/bundled-skills/competitor-alternatives/references/detailed-guide.md +756 -0
- package/bundled-skills/competitor-profiling/SKILL.md +1 -1
- package/bundled-skills/competitor-tracking/SKILL.md +1 -1
- package/bundled-skills/comprehensive-review-full-review/SKILL.md +6 -0
- package/bundled-skills/comprehensive-review-pr-enhance/SKILL.md +1 -0
- package/bundled-skills/computer-use-agents/SKILL.md +2 -2131
- package/bundled-skills/computer-use-agents/references/detailed-guide.md +2158 -0
- package/bundled-skills/computer-vision-expert/SKILL.md +6 -0
- package/bundled-skills/conductor-setup/SKILL.md +1 -0
- package/bundled-skills/confluence-automation/SKILL.md +6 -0
- package/bundled-skills/constant-time-analysis/SKILL.md +1 -0
- package/bundled-skills/container-security-hardening/SKILL.md +4 -915
- package/bundled-skills/container-security-hardening/references/detailed-guide.md +927 -0
- package/bundled-skills/content-creator/SKILL.md +53 -23
- package/bundled-skills/content-creator/assets/content_calendar_template.md +5 -2
- package/bundled-skills/content-creator/references/brand_guidelines.md +7 -3
- package/bundled-skills/content-creator/references/content_frameworks.md +12 -7
- package/bundled-skills/content-creator/references/social_media_optimization.md +61 -317
- package/bundled-skills/content-creator/scripts/brand_voice_analyzer.py +14 -9
- package/bundled-skills/content-creator/scripts/seo_optimizer.py +31 -94
- package/bundled-skills/context-compression/SKILL.md +1 -0
- package/bundled-skills/context-degradation/SKILL.md +1 -0
- package/bundled-skills/context-fundamentals/SKILL.md +1 -0
- package/bundled-skills/context-management-context-restore/SKILL.md +40 -177
- package/bundled-skills/context-optimization/SKILL.md +1 -0
- package/bundled-skills/convex/SKILL.md +4 -766
- package/bundled-skills/convex/references/detailed-guide.md +772 -0
- package/bundled-skills/copilot-sdk/SKILL.md +4 -496
- package/bundled-skills/copilot-sdk/references/detailed-guide.md +516 -0
- package/bundled-skills/copy-editing/SKILL.md +6 -0
- package/bundled-skills/copywriting/SKILL.md +6 -0
- package/bundled-skills/copywriting-psychologist/SKILL.md +6 -0
- package/bundled-skills/create-branch/SKILL.md +1 -0
- package/bundled-skills/create-pr/SKILL.md +7 -0
- package/bundled-skills/cred-omega/SKILL.md +2 -847
- package/bundled-skills/cred-omega/references/detailed-guide.md +872 -0
- package/bundled-skills/cro/SKILL.md +1 -1
- package/bundled-skills/csharp-pro/SKILL.md +6 -0
- package/bundled-skills/cucumber-skill/SKILL.md +1 -1
- package/bundled-skills/customer-psychographic-profiler/SKILL.md +6 -0
- package/bundled-skills/customer-research/SKILL.md +1 -1
- package/bundled-skills/cv-generator/SKILL.md +4 -828
- package/bundled-skills/cv-generator/references/detailed-guide.md +845 -0
- package/bundled-skills/cypress-skill/SKILL.md +1 -1
- package/bundled-skills/daily-gift/SKILL.md +6 -0
- package/bundled-skills/debug-buttercup/SKILL.md +1 -0
- package/bundled-skills/debugger/SKILL.md +6 -0
- package/bundled-skills/debugging-code/SKILL.md +1 -1
- package/bundled-skills/deepapi/SKILL.md +4 -619
- package/bundled-skills/deepapi/references/detailed-guide.md +627 -0
- package/bundled-skills/delegating-to-agents/SKILL.md +6 -0
- package/bundled-skills/deployment-validation-config-validate/SKILL.md +4 -478
- package/bundled-skills/deployment-validation-config-validate/references/detailed-guide.md +484 -0
- package/bundled-skills/design-it/SKILL.md +6 -0
- package/bundled-skills/design-orchestration/SKILL.md +6 -0
- package/bundled-skills/design-spells/SKILL.md +6 -0
- package/bundled-skills/design-system/SKILL.md +7 -1
- package/bundled-skills/design-taste-frontend/SKILL.md +6 -0
- package/bundled-skills/design-thinking/SKILL.md +6 -0
- package/bundled-skills/design-ux/SKILL.md +7 -1
- package/bundled-skills/deterministic-design/SKILL.md +6 -0
- package/bundled-skills/developer-advocacy/SKILL.md +1 -1
- package/bundled-skills/developer-audience-context/SKILL.md +1 -1
- package/bundled-skills/developer-churn/SKILL.md +5 -625
- package/bundled-skills/developer-churn/references/detailed-guide.md +639 -0
- package/bundled-skills/developer-listening/SKILL.md +7 -1
- package/bundled-skills/developer-onboarding/SKILL.md +6 -474
- package/bundled-skills/developer-onboarding/references/detailed-guide.md +486 -0
- package/bundled-skills/developer-sandbox/SKILL.md +6 -414
- package/bundled-skills/developer-sandbox/references/detailed-guide.md +425 -0
- package/bundled-skills/developer-seo/SKILL.md +1 -1
- package/bundled-skills/developer-signup-flow/SKILL.md +1 -1
- package/bundled-skills/devrel-content/SKILL.md +1 -1
- package/bundled-skills/diagnosing-bugs/SKILL.md +5 -0
- package/bundled-skills/diary/SKILL.md +1 -0
- package/bundled-skills/differential-review/SKILL.md +1 -0
- package/bundled-skills/discord-automation/SKILL.md +6 -0
- package/bundled-skills/discord-bot-architect/SKILL.md +2 -1434
- package/bundled-skills/discord-bot-architect/references/detailed-guide.md +1465 -0
- package/bundled-skills/django-access-review/SKILL.md +1 -0
- package/bundled-skills/django-perf-review/SKILL.md +1 -0
- package/bundled-skills/doc-coauthoring/SKILL.md +6 -0
- package/bundled-skills/docs-architect/SKILL.md +6 -0
- package/bundled-skills/docs-as-marketing/SKILL.md +1 -1
- package/bundled-skills/documentation-generation-doc-generate/SKILL.md +16 -1
- package/bundled-skills/documentation-generation-doc-generate/resources/implementation-playbook.md +33 -55
- package/bundled-skills/doubt-driven-development/SKILL.md +1 -1
- package/bundled-skills/dropbox-automation/SKILL.md +6 -0
- package/bundled-skills/dwarf-expert/SKILL.md +1 -0
- package/bundled-skills/dx-optimizer/SKILL.md +6 -0
- package/bundled-skills/e2e-testing-patterns/SKILL.md +14 -4
- package/bundled-skills/e2e-testing-patterns/resources/implementation-playbook.md +22 -41
- package/bundled-skills/eas-update-insights/SKILL.md +1 -1
- package/bundled-skills/ecl-harness-engineer/SKILL.md +4 -681
- package/bundled-skills/ecl-harness-engineer/references/detailed-guide.md +691 -0
- package/bundled-skills/efficient-web-research/SKILL.md +1 -0
- package/bundled-skills/electron-development/SKILL.md +4 -820
- package/bundled-skills/electron-development/references/detailed-guide.md +828 -0
- package/bundled-skills/elixir-pro/SKILL.md +6 -0
- package/bundled-skills/elon-musk/SKILL.md +5 -1278
- package/bundled-skills/elon-musk/references/detailed-guide.md +1293 -0
- package/bundled-skills/email-sequence/SKILL.md +2 -915
- package/bundled-skills/email-sequence/references/detailed-guide.md +932 -0
- package/bundled-skills/email-systems/SKILL.md +2 -654
- package/bundled-skills/email-systems/references/detailed-guide.md +669 -0
- package/bundled-skills/emergency-card/SKILL.md +1 -0
- package/bundled-skills/emil-design-eng/SKILL.md +4 -673
- package/bundled-skills/emil-design-eng/references/detailed-guide.md +690 -0
- package/bundled-skills/emotional-arc-designer/SKILL.md +6 -0
- package/bundled-skills/enhance-prompt/SKILL.md +1 -0
- package/bundled-skills/entropy-box/SKILL.md +400 -0
- package/bundled-skills/entropy-box/references/api.md +196 -0
- package/bundled-skills/entropy-box/references/knowledge-compiler.md +61 -0
- package/bundled-skills/entropy-box/references/panorama.md +72 -0
- package/bundled-skills/error-debugging-error-analysis/SKILL.md +15 -0
- package/bundled-skills/error-debugging-error-analysis/resources/implementation-playbook.md +122 -1125
- package/bundled-skills/error-debugging-multi-agent-review/SKILL.md +42 -214
- package/bundled-skills/error-detective/SKILL.md +6 -0
- package/bundled-skills/error-diagnostics-error-analysis/SKILL.md +15 -0
- package/bundled-skills/error-diagnostics-error-analysis/resources/implementation-playbook.md +122 -1125
- package/bundled-skills/event-sourcing-architect/SKILL.md +6 -0
- package/bundled-skills/event-staffing-compliance/SKILL.md +6 -0
- package/bundled-skills/event-staffing-ordering/SKILL.md +6 -0
- package/bundled-skills/evolution/SKILL.md +1 -0
- package/bundled-skills/executing-plans/SKILL.md +6 -0
- package/bundled-skills/explain-like-socrates/SKILL.md +6 -0
- package/bundled-skills/expo-deployment/SKILL.md +1 -1
- package/bundled-skills/expo-examples/SKILL.md +1 -1
- package/bundled-skills/expo-module/SKILL.md +1 -1
- package/bundled-skills/expo-observe/SKILL.md +1 -1
- package/bundled-skills/expo-ui/SKILL.md +1 -1
- package/bundled-skills/expo-ui-jetpack-compose/SKILL.md +1 -0
- package/bundled-skills/expo-ui-swift-ui/SKILL.md +1 -0
- package/bundled-skills/fable-safe-prompt/SKILL.md +6 -0
- package/bundled-skills/fal-audio/SKILL.md +6 -0
- package/bundled-skills/fal-generate/SKILL.md +6 -0
- package/bundled-skills/fal-image-edit/SKILL.md +6 -0
- package/bundled-skills/fal-platform/SKILL.md +6 -0
- package/bundled-skills/fal-upscale/SKILL.md +6 -0
- package/bundled-skills/fal-workflow/SKILL.md +6 -0
- package/bundled-skills/family-health-analyzer/SKILL.md +1 -0
- package/bundled-skills/favicon/SKILL.md +1 -0
- package/bundled-skills/fda-food-safety-auditor/SKILL.md +1 -0
- package/bundled-skills/fda-medtech-compliance-auditor/SKILL.md +1 -0
- package/bundled-skills/ffuf-claude-skill/SKILL.md +6 -0
- package/bundled-skills/ffuf-web-fuzzing/SKILL.md +5 -492
- package/bundled-skills/ffuf-web-fuzzing/references/detailed-guide.md +510 -0
- package/bundled-skills/file-organizer/SKILL.md +6 -0
- package/bundled-skills/file-uploads/SKILL.md +6 -0
- package/bundled-skills/filesystem-context/SKILL.md +1 -0
- package/bundled-skills/find-bugs/SKILL.md +7 -0
- package/bundled-skills/firebase/SKILL.md +8 -651
- package/bundled-skills/firebase/references/detailed-guide.md +662 -0
- package/bundled-skills/fitness-analyzer/SKILL.md +1 -0
- package/bundled-skills/fix-review/SKILL.md +6 -0
- package/bundled-skills/fixing-metadata/SKILL.md +7 -1
- package/bundled-skills/food-database-query/SKILL.md +5 -772
- package/bundled-skills/food-database-query/references/detailed-guide.md +785 -0
- package/bundled-skills/form-cro/SKILL.md +6 -0
- package/bundled-skills/fp-async/SKILL.md +5 -716
- package/bundled-skills/fp-async/references/detailed-guide.md +726 -0
- package/bundled-skills/fp-backend/SKILL.md +5 -1313
- package/bundled-skills/fp-backend/references/detailed-guide.md +1323 -0
- package/bundled-skills/fp-data-transforms/SKILL.md +5 -1181
- package/bundled-skills/fp-data-transforms/references/detailed-guide.md +1190 -0
- package/bundled-skills/fp-either-ref/SKILL.md +1 -0
- package/bundled-skills/fp-errors/SKILL.md +5 -835
- package/bundled-skills/fp-errors/references/detailed-guide.md +846 -0
- package/bundled-skills/fp-option-ref/SKILL.md +1 -0
- package/bundled-skills/fp-pipe-ref/SKILL.md +1 -0
- package/bundled-skills/fp-pragmatic/SKILL.md +5 -500
- package/bundled-skills/fp-pragmatic/references/detailed-guide.md +510 -0
- package/bundled-skills/fp-react/SKILL.md +3 -761
- package/bundled-skills/fp-react/references/detailed-guide.md +775 -0
- package/bundled-skills/fp-refactor/SKILL.md +5 -1761
- package/bundled-skills/fp-refactor/references/detailed-guide.md +1775 -0
- package/bundled-skills/fp-taskeither-ref/SKILL.md +1 -0
- package/bundled-skills/fp-ts-errors/SKILL.md +4 -835
- package/bundled-skills/fp-ts-errors/references/detailed-guide.md +846 -0
- package/bundled-skills/fp-ts-pragmatic/SKILL.md +4 -500
- package/bundled-skills/fp-ts-pragmatic/references/detailed-guide.md +510 -0
- package/bundled-skills/fp-ts-react/SKILL.md +4 -763
- package/bundled-skills/fp-ts-react/references/detailed-guide.md +775 -0
- package/bundled-skills/fp-types-ref/SKILL.md +1 -0
- package/bundled-skills/framework-migration-legacy-modernize/SKILL.md +6 -0
- package/bundled-skills/free-tool-strategy/SKILL.md +2 -532
- package/bundled-skills/free-tool-strategy/references/detailed-guide.md +550 -0
- package/bundled-skills/freshdesk-automation/SKILL.md +6 -0
- package/bundled-skills/frontend-architecture/SKILL.md +1 -1
- package/bundled-skills/frontend-data-contracts/SKILL.md +1 -1
- package/bundled-skills/frontend-design/SKILL.md +33 -34
- package/bundled-skills/frontend-observability/SKILL.md +1 -1
- package/bundled-skills/frontend-optimistic-mutations/SKILL.md +1 -1
- package/bundled-skills/frontend-seo/SKILL.md +6 -675
- package/bundled-skills/frontend-seo/references/detailed-guide.md +688 -0
- package/bundled-skills/frontend-slides-frontend-slides/SKILL.md +7 -1
- package/bundled-skills/frontend-ui-dark-ts/SKILL.md +2 -561
- package/bundled-skills/frontend-ui-dark-ts/references/detailed-guide.md +573 -0
- package/bundled-skills/full-stack-orchestration-full-stack-feature/SKILL.md +6 -0
- package/bundled-skills/game-development/2d-games/SKILL.md +6 -0
- package/bundled-skills/game-development/mobile-games/SKILL.md +6 -0
- package/bundled-skills/game-development/vr-ar/SKILL.md +6 -0
- package/bundled-skills/gcp-cloud-run/SKILL.md +2 -1303
- package/bundled-skills/gcp-cloud-run/references/detailed-guide.md +1350 -0
- package/bundled-skills/gemini-api-dev/SKILL.md +1 -1
- package/bundled-skills/gemini-live-api-dev/SKILL.md +1 -1
- package/bundled-skills/gemini-omni-flash-api/SKILL.md +1 -1
- package/bundled-skills/geo-fundamentals/SKILL.md +6 -0
- package/bundled-skills/geoffrey-hinton/SKILL.md +5 -1235
- package/bundled-skills/geoffrey-hinton/references/detailed-guide.md +1324 -0
- package/bundled-skills/gh-review-requests/SKILL.md +1 -0
- package/bundled-skills/github-actions-advanced/SKILL.md +4 -971
- package/bundled-skills/github-actions-advanced/references/detailed-guide.md +991 -0
- package/bundled-skills/github-automation/SKILL.md +6 -0
- package/bundled-skills/github-workflow-automation/SKILL.md +4 -826
- package/bundled-skills/github-workflow-automation/references/detailed-guide.md +832 -0
- package/bundled-skills/gitlab-automation/SKILL.md +6 -0
- package/bundled-skills/gmail-automation/SKILL.md +1 -0
- package/bundled-skills/go-rod-master/SKILL.md +2 -496
- package/bundled-skills/go-rod-master/references/detailed-guide.md +508 -0
- package/bundled-skills/goal-analyzer/SKILL.md +5 -596
- package/bundled-skills/goal-analyzer/references/detailed-guide.md +602 -0
- package/bundled-skills/google-calendar-automation/SKILL.md +1 -0
- package/bundled-skills/google-docs-automation/SKILL.md +1 -0
- package/bundled-skills/google-drive-automation/SKILL.md +1 -0
- package/bundled-skills/google-sheets-automation/SKILL.md +1 -0
- package/bundled-skills/google-slides-automation/SKILL.md +1 -0
- package/bundled-skills/googlesheets-automation/SKILL.md +6 -0
- package/bundled-skills/gpt-taste/SKILL.md +6 -0
- package/bundled-skills/graphql/SKILL.md +8 -1031
- package/bundled-skills/graphql/references/detailed-guide.md +1044 -0
- package/bundled-skills/grill-me/SKILL.md +5 -0
- package/bundled-skills/grill-with-docs/SKILL.md +5 -0
- package/bundled-skills/grilling/SKILL.md +5 -0
- package/bundled-skills/growth-engine/SKILL.md +6 -0
- package/bundled-skills/handoff/SKILL.md +5 -0
- package/bundled-skills/haskell-pro/SKILL.md +6 -0
- package/bundled-skills/headline-psychologist/SKILL.md +6 -0
- package/bundled-skills/health-trend-analyzer/SKILL.md +1 -0
- package/bundled-skills/hig-components-content/SKILL.md +6 -0
- package/bundled-skills/hig-components-controls/SKILL.md +6 -0
- package/bundled-skills/hig-components-dialogs/SKILL.md +6 -0
- package/bundled-skills/hig-components-layout/SKILL.md +6 -0
- package/bundled-skills/hig-components-menus/SKILL.md +6 -0
- package/bundled-skills/hig-components-search/SKILL.md +6 -0
- package/bundled-skills/hig-components-status/SKILL.md +6 -0
- package/bundled-skills/hig-components-system/SKILL.md +6 -0
- package/bundled-skills/hig-foundations/SKILL.md +6 -0
- package/bundled-skills/hig-inputs/SKILL.md +6 -0
- package/bundled-skills/hig-patterns/SKILL.md +6 -0
- package/bundled-skills/hig-platforms/SKILL.md +6 -0
- package/bundled-skills/hig-technologies/SKILL.md +6 -0
- package/bundled-skills/high-end-visual-design/SKILL.md +6 -0
- package/bundled-skills/hosted-agents/SKILL.md +7 -0
- package/bundled-skills/hosted-agents-v2-py/SKILL.md +23 -5
- package/bundled-skills/html-injection-testing/SKILL.md +2 -453
- package/bundled-skills/html-injection-testing/references/detailed-guide.md +462 -0
- package/bundled-skills/hubspot-automation/SKILL.md +6 -0
- package/bundled-skills/hubspot-integration/SKILL.md +8 -807
- package/bundled-skills/hubspot-integration/references/detailed-guide.md +815 -0
- package/bundled-skills/hugging-face-cli/SKILL.md +7 -1
- package/bundled-skills/hugging-face-community-evals/SKILL.md +1 -1
- package/bundled-skills/hugging-face-datasets/SKILL.md +5 -430
- package/bundled-skills/hugging-face-datasets/references/detailed-guide.md +442 -0
- package/bundled-skills/hugging-face-evaluation/SKILL.md +5 -640
- package/bundled-skills/hugging-face-evaluation/references/detailed-guide.md +652 -0
- package/bundled-skills/hugging-face-jobs/SKILL.md +3 -801
- package/bundled-skills/hugging-face-jobs/references/detailed-guide.md +821 -0
- package/bundled-skills/hugging-face-model-trainer/SKILL.md +3 -681
- package/bundled-skills/hugging-face-model-trainer/references/detailed-guide.md +702 -0
- package/bundled-skills/hugging-face-paper-publisher/SKILL.md +5 -617
- package/bundled-skills/hugging-face-paper-publisher/references/detailed-guide.md +624 -0
- package/bundled-skills/hugging-face-papers/SKILL.md +1 -1
- package/bundled-skills/hugging-face-tool-builder/SKILL.md +1 -0
- package/bundled-skills/hugging-face-trackio/SKILL.md +1 -1
- package/bundled-skills/hugging-face-vision-trainer/SKILL.md +5 -534
- package/bundled-skills/hugging-face-vision-trainer/references/detailed-guide.md +549 -0
- package/bundled-skills/huggingface-best/SKILL.md +1 -1
- package/bundled-skills/huggingface-lora-space-builder/SKILL.md +1 -1
- package/bundled-skills/huggingface-spaces/SKILL.md +1 -1
- package/bundled-skills/huggingface-tool-builder/SKILL.md +1 -1
- package/bundled-skills/huggingface-zerogpu/SKILL.md +1 -1
- package/bundled-skills/hugo-to-markdown/SKILL.md +1 -1
- package/bundled-skills/hyperexecute-skill/SKILL.md +7 -1
- package/bundled-skills/iconsax-library/SKILL.md +6 -0
- package/bundled-skills/idea-refine/SKILL.md +1 -1
- package/bundled-skills/identity-mirror/SKILL.md +6 -0
- package/bundled-skills/ilya-sutskever/SKILL.md +2 -1126
- package/bundled-skills/ilya-sutskever/references/detailed-guide.md +1167 -0
- package/bundled-skills/image-generator/SKILL.md +4 -320
- package/bundled-skills/image-generator/references/detailed-guide.md +332 -0
- package/bundled-skills/implement/SKILL.md +6 -0
- package/bundled-skills/incident-responder/SKILL.md +6 -0
- package/bundled-skills/incident-response-incident-response/SKILL.md +6 -0
- package/bundled-skills/industrial-brutalist-ui/SKILL.md +6 -0
- package/bundled-skills/interactive-portfolio/SKILL.md +2 -482
- package/bundled-skills/interactive-portfolio/references/detailed-guide.md +500 -0
- package/bundled-skills/internal-comms/SKILL.md +20 -1
- package/bundled-skills/internal-comms/examples/3p-updates.md +5 -4
- package/bundled-skills/internal-comms/examples/company-newsletter.md +3 -2
- package/bundled-skills/internal-comms/examples/faq-answers.md +1 -0
- package/bundled-skills/internal-comms/examples/general-comms.md +1 -0
- package/bundled-skills/internal-comms-anthropic/SKILL.md +21 -2
- package/bundled-skills/internal-comms-anthropic/examples/3p-updates.md +5 -4
- package/bundled-skills/internal-comms-anthropic/examples/company-newsletter.md +3 -2
- package/bundled-skills/internal-comms-anthropic/examples/faq-answers.md +1 -0
- package/bundled-skills/internal-comms-anthropic/examples/general-comms.md +1 -0
- package/bundled-skills/internal-comms-community/SKILL.md +21 -2
- package/bundled-skills/internal-comms-community/examples/3p-updates.md +48 -0
- package/bundled-skills/internal-comms-community/examples/company-newsletter.md +66 -0
- package/bundled-skills/internal-comms-community/examples/faq-answers.md +31 -0
- package/bundled-skills/internal-comms-community/examples/general-comms.md +17 -0
- package/bundled-skills/ios-debugger-agent/SKILL.md +6 -0
- package/bundled-skills/issues/SKILL.md +1 -0
- package/bundled-skills/iterate-pr/SKILL.md +1 -0
- package/bundled-skills/javascript-mastery/SKILL.md +4 -625
- package/bundled-skills/javascript-mastery/references/detailed-guide.md +636 -0
- package/bundled-skills/javascript-pro/SKILL.md +6 -0
- package/bundled-skills/jest-skill/SKILL.md +1 -1
- package/bundled-skills/jira-automation/SKILL.md +6 -0
- package/bundled-skills/jobs-to-be-done-analyst/SKILL.md +6 -0
- package/bundled-skills/junit-5-skill/SKILL.md +1 -1
- package/bundled-skills/k6-load-testing/SKILL.md +2 -544
- package/bundled-skills/k6-load-testing/references/detailed-guide.md +563 -0
- package/bundled-skills/kaizen/SKILL.md +2 -711
- package/bundled-skills/kaizen/references/detailed-guide.md +720 -0
- package/bundled-skills/langfuse/SKILL.md +41 -468
- package/bundled-skills/langgraph/SKILL.md +2 -445
- package/bundled-skills/langgraph/references/detailed-guide.md +455 -0
- package/bundled-skills/laravel-development-workflow/SKILL.md +106 -0
- package/bundled-skills/laravel-expert/SKILL.md +6 -0
- package/bundled-skills/launch-strategy/SKILL.md +6 -0
- package/bundled-skills/lead-magnets/SKILL.md +6 -0
- package/bundled-skills/learn/SKILL.md +5 -0
- package/bundled-skills/legacy-modernizer/SKILL.md +6 -0
- package/bundled-skills/legal-advisor/SKILL.md +6 -0
- package/bundled-skills/leiloeiro-avaliacao/SKILL.md +2 -469
- package/bundled-skills/leiloeiro-avaliacao/references/detailed-guide.md +503 -0
- package/bundled-skills/leiloeiro-edital/SKILL.md +2 -468
- package/bundled-skills/leiloeiro-edital/references/detailed-guide.md +491 -0
- package/bundled-skills/leiloeiro-mercado/SKILL.md +2 -458
- package/bundled-skills/leiloeiro-mercado/references/detailed-guide.md +488 -0
- package/bundled-skills/leiloeiro-risco/SKILL.md +2 -457
- package/bundled-skills/leiloeiro-risco/references/detailed-guide.md +482 -0
- package/bundled-skills/lesson-generator/SKILL.md +5 -0
- package/bundled-skills/lightning-architecture-review/SKILL.md +6 -0
- package/bundled-skills/lightning-channel-factories/SKILL.md +6 -0
- package/bundled-skills/lightning-factory-explainer/SKILL.md +6 -0
- package/bundled-skills/linear-claude-skill/SKILL.md +4 -502
- package/bundled-skills/linear-claude-skill/references/detailed-guide.md +517 -0
- package/bundled-skills/linkedin-cli/SKILL.md +4 -513
- package/bundled-skills/linkedin-cli/references/detailed-guide.md +520 -0
- package/bundled-skills/lint-and-validate/SKILL.md +6 -0
- package/bundled-skills/linux-privilege-escalation/SKILL.md +2 -426
- package/bundled-skills/linux-privilege-escalation/references/detailed-guide.md +436 -0
- package/bundled-skills/linux-shell-scripting/SKILL.md +2 -473
- package/bundled-skills/linux-shell-scripting/references/detailed-guide.md +481 -0
- package/bundled-skills/llm-app-patterns/SKILL.md +16 -741
- package/bundled-skills/llm-app-patterns/references/detailed-guide.md +760 -0
- package/bundled-skills/llm-council/SKILL.md +4 -550
- package/bundled-skills/llm-council/references/detailed-guide.md +556 -0
- package/bundled-skills/llm-ops/SKILL.md +6 -0
- package/bundled-skills/local-legal-seo-audit/SKILL.md +6 -0
- package/bundled-skills/logic-diff/SKILL.md +1 -1
- package/bundled-skills/logic-explain/SKILL.md +7 -1
- package/bundled-skills/logic-fix-all/SKILL.md +1 -1
- package/bundled-skills/logic-locate/SKILL.md +1 -1
- package/bundled-skills/logic-review/SKILL.md +1 -1
- package/bundled-skills/loki-mode/SKILL.md +4 -696
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +13 -12
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +1 -1
- package/bundled-skills/loki-mode/references/detailed-guide.md +717 -0
- package/bundled-skills/longbridge-content/SKILL.md +1 -1
- package/bundled-skills/longbridge-fundamentals/SKILL.md +1 -1
- package/bundled-skills/longbridge-market-data/SKILL.md +1 -1
- package/bundled-skills/lookdev-auto/SKILL.md +6 -0
- package/bundled-skills/loopy/SKILL.md +1 -1
- package/bundled-skills/loss-aversion-designer/SKILL.md +6 -0
- package/bundled-skills/machine-learning-ops-ml-pipeline/SKILL.md +6 -0
- package/bundled-skills/magic-animator/SKILL.md +6 -0
- package/bundled-skills/magic-ui-generator/SKILL.md +6 -0
- package/bundled-skills/mailtrap-setting-up-sending-domain/SKILL.md +6 -0
- package/bundled-skills/makepad-animation/SKILL.md +1 -0
- package/bundled-skills/makepad-basics/SKILL.md +1 -0
- package/bundled-skills/makepad-deployment/SKILL.md +1 -0
- package/bundled-skills/makepad-dsl/SKILL.md +1 -0
- package/bundled-skills/makepad-event-action/SKILL.md +1 -0
- package/bundled-skills/makepad-font/SKILL.md +1 -0
- package/bundled-skills/makepad-layout/SKILL.md +1 -0
- package/bundled-skills/makepad-platform/SKILL.md +1 -0
- package/bundled-skills/makepad-reference/SKILL.md +1 -0
- package/bundled-skills/makepad-shaders/SKILL.md +1 -0
- package/bundled-skills/makepad-skills/SKILL.md +6 -0
- package/bundled-skills/makepad-splash/SKILL.md +1 -0
- package/bundled-skills/makepad-widgets/SKILL.md +1 -0
- package/bundled-skills/manage-skills/SKILL.md +1 -0
- package/bundled-skills/marketing-plan/SKILL.md +1 -1
- package/bundled-skills/matematico-tao/SKILL.md +2 -632
- package/bundled-skills/matematico-tao/references/detailed-guide.md +651 -0
- package/bundled-skills/matplotlib/SKILL.md +1 -0
- package/bundled-skills/maxia/SKILL.md +1 -0
- package/bundled-skills/mcp-builder/SKILL.md +46 -14
- package/bundled-skills/mcp-builder/reference/evaluation.md +69 -226
- package/bundled-skills/mcp-builder/reference/mcp_best_practices.md +8 -5
- package/bundled-skills/mcp-builder/reference/node_mcp_server.md +46 -144
- package/bundled-skills/mcp-builder/reference/python_mcp_server.md +42 -80
- package/bundled-skills/mcp-builder/scripts/connections.py +18 -11
- package/bundled-skills/mcp-builder/scripts/evaluation.py +88 -95
- package/bundled-skills/mcp-builder/scripts/example_evaluation.xml +1 -1
- package/bundled-skills/mcp-builder/scripts/requirements.txt +4 -2
- package/bundled-skills/mental-health-analyzer/SKILL.md +5 -974
- package/bundled-skills/mental-health-analyzer/references/detailed-guide.md +1014 -0
- package/bundled-skills/micro-saas-launcher/SKILL.md +2 -473
- package/bundled-skills/micro-saas-launcher/references/detailed-guide.md +491 -0
- package/bundled-skills/minecraft-bukkit-pro/SKILL.md +6 -0
- package/bundled-skills/minimalist-ui/SKILL.md +6 -0
- package/bundled-skills/moatmri/SKILL.md +6 -0
- package/bundled-skills/molykit/SKILL.md +1 -0
- package/bundled-skills/monday-automation/SKILL.md +6 -0
- package/bundled-skills/monopoly/SKILL.md +1 -0
- package/bundled-skills/monopoly/patterns/SKILL.md +1 -0
- package/bundled-skills/monopoly/scale-benchmarks/SKILL.md +1 -0
- package/bundled-skills/monopoly/security-checklist/SKILL.md +6 -0
- package/bundled-skills/monopoly/tech-matrix/SKILL.md +1 -0
- package/bundled-skills/monorepo-architect/SKILL.md +6 -0
- package/bundled-skills/monte-carlo-analyze-root-cause/SKILL.md +1 -1
- package/bundled-skills/monte-carlo-performance-diagnosis/SKILL.md +7 -1
- package/bundled-skills/monte-carlo-validation-notebook/SKILL.md +4 -601
- package/bundled-skills/monte-carlo-validation-notebook/references/detailed-guide.md +613 -0
- package/bundled-skills/moodle-external-api-development/SKILL.md +4 -517
- package/bundled-skills/moodle-external-api-development/references/detailed-guide.md +529 -0
- package/bundled-skills/multi-agent-architect/SKILL.md +1 -0
- package/bundled-skills/multi-agent-brainstorming/SKILL.md +6 -0
- package/bundled-skills/multi-agent-patterns/SKILL.md +1 -0
- package/bundled-skills/multi-platform-apps-multi-platform/SKILL.md +6 -0
- package/bundled-skills/n8n-code-javascript/SKILL.md +3 -667
- package/bundled-skills/n8n-code-javascript/references/detailed-guide.md +683 -0
- package/bundled-skills/n8n-code-python/SKILL.md +3 -664
- package/bundled-skills/n8n-code-python/references/detailed-guide.md +682 -0
- package/bundled-skills/n8n-expression-syntax/SKILL.md +5 -401
- package/bundled-skills/n8n-expression-syntax/references/detailed-guide.md +416 -0
- package/bundled-skills/n8n-mcp-tools-expert/SKILL.md +5 -485
- package/bundled-skills/n8n-mcp-tools-expert/references/detailed-guide.md +499 -0
- package/bundled-skills/n8n-node-configuration/SKILL.md +5 -775
- package/bundled-skills/n8n-node-configuration/references/detailed-guide.md +789 -0
- package/bundled-skills/n8n-validation-expert/SKILL.md +5 -679
- package/bundled-skills/n8n-validation-expert/references/detailed-guide.md +694 -0
- package/bundled-skills/n8n-workflow-patterns/SKILL.md +1 -0
- package/bundled-skills/nanobanana-ppt-skills/SKILL.md +6 -0
- package/bundled-skills/native-data-fetching/SKILL.md +2 -456
- package/bundled-skills/native-data-fetching/references/detailed-guide.md +465 -0
- package/bundled-skills/neon-ai-gateway/SKILL.md +1 -1
- package/bundled-skills/neon-functions/SKILL.md +1 -1
- package/bundled-skills/neon-object-storage/SKILL.md +1 -1
- package/bundled-skills/neon-postgres/SKILL.md +1 -1
- package/bundled-skills/neon-postgres-branches/SKILL.md +1 -1
- package/bundled-skills/neon-postgres-egress-optimizer/SKILL.md +1 -1
- package/bundled-skills/nerdzao-elite/SKILL.md +6 -0
- package/bundled-skills/nerdzao-elite-gemini-high/SKILL.md +6 -0
- package/bundled-skills/nestjs-expert/SKILL.md +2 -524
- package/bundled-skills/nestjs-expert/references/detailed-guide.md +539 -0
- package/bundled-skills/networkx/SKILL.md +1 -0
- package/bundled-skills/new-rails-project/SKILL.md +7 -0
- package/bundled-skills/newman-cicd-integration/SKILL.md +1 -1
- package/bundled-skills/nika/SKILL.md +1 -0
- package/bundled-skills/nosql-expert/SKILL.md +6 -0
- package/bundled-skills/not-a-vibe-coder/SKILL.md +7 -0
- package/bundled-skills/notion-template-business/SKILL.md +2 -505
- package/bundled-skills/notion-template-business/references/detailed-guide.md +523 -0
- package/bundled-skills/nutrition-analyzer/SKILL.md +5 -766
- package/bundled-skills/nutrition-analyzer/references/detailed-guide.md +775 -0
- package/bundled-skills/objection-preemptor/SKILL.md +6 -0
- package/bundled-skills/observability-and-instrumentation/SKILL.md +1 -1
- package/bundled-skills/observability-engineer/SKILL.md +16 -8
- package/bundled-skills/obsidian-bases/SKILL.md +4 -366
- package/bundled-skills/obsidian-bases/references/detailed-guide.md +380 -0
- package/bundled-skills/occupational-health-analyzer/SKILL.md +1 -0
- package/bundled-skills/odoo-accounting-setup/SKILL.md +1 -0
- package/bundled-skills/odoo-automated-tests/SKILL.md +1 -0
- package/bundled-skills/odoo-backup-strategy/SKILL.md +1 -0
- package/bundled-skills/odoo-docker-deployment/SKILL.md +1 -0
- package/bundled-skills/odoo-ecommerce-configurator/SKILL.md +1 -0
- package/bundled-skills/odoo-edi-connector/SKILL.md +1 -0
- package/bundled-skills/odoo-hr-payroll-setup/SKILL.md +1 -0
- package/bundled-skills/odoo-inventory-optimizer/SKILL.md +1 -0
- package/bundled-skills/odoo-l10n-compliance/SKILL.md +1 -0
- package/bundled-skills/odoo-manufacturing-advisor/SKILL.md +1 -0
- package/bundled-skills/odoo-migration-helper/SKILL.md +1 -0
- package/bundled-skills/odoo-module-developer/SKILL.md +1 -0
- package/bundled-skills/odoo-orm-expert/SKILL.md +1 -0
- package/bundled-skills/odoo-performance-tuner/SKILL.md +1 -0
- package/bundled-skills/odoo-project-timesheet/SKILL.md +1 -0
- package/bundled-skills/odoo-purchase-workflow/SKILL.md +1 -0
- package/bundled-skills/odoo-qweb-templates/SKILL.md +1 -0
- package/bundled-skills/odoo-rpc-api/SKILL.md +1 -0
- package/bundled-skills/odoo-sales-crm-expert/SKILL.md +1 -0
- package/bundled-skills/odoo-security-rules/SKILL.md +1 -0
- package/bundled-skills/odoo-shopify-integration/SKILL.md +1 -0
- package/bundled-skills/odoo-upgrade-advisor/SKILL.md +1 -0
- package/bundled-skills/odoo-woocommerce-bridge/SKILL.md +1 -0
- package/bundled-skills/odoo-xml-views-builder/SKILL.md +1 -0
- package/bundled-skills/odw/SKILL.md +6 -0
- package/bundled-skills/offers/SKILL.md +1 -1
- package/bundled-skills/onboarding/SKILL.md +1 -1
- package/bundled-skills/onboarding-psychologist/SKILL.md +6 -0
- package/bundled-skills/one-drive-automation/SKILL.md +6 -0
- package/bundled-skills/openapi-spec-generator/SKILL.md +1 -1
- package/bundled-skills/oral-health-analyzer/SKILL.md +8 -515
- package/bundled-skills/oral-health-analyzer/references/detailed-guide.md +529 -0
- package/bundled-skills/orca-replay/SKILL.md +284 -0
- package/bundled-skills/outlook-automation/SKILL.md +6 -0
- package/bundled-skills/pagespeed-enhancer/SKILL.md +4 -520
- package/bundled-skills/pagespeed-enhancer/references/detailed-guide.md +533 -0
- package/bundled-skills/paid-ads/SKILL.md +2 -541
- package/bundled-skills/paid-ads/references/detailed-guide.md +558 -0
- package/bundled-skills/parallel-search-mcp/SKILL.md +134 -0
- package/bundled-skills/payment-integration/SKILL.md +6 -0
- package/bundled-skills/paywall-upgrade-cro/SKILL.md +2 -560
- package/bundled-skills/paywall-upgrade-cro/references/detailed-guide.md +579 -0
- package/bundled-skills/performance-testing-review-multi-agent-review/SKILL.md +42 -214
- package/bundled-skills/personal-tool-builder/SKILL.md +2 -688
- package/bundled-skills/personal-tool-builder/references/detailed-guide.md +705 -0
- package/bundled-skills/phase-gated-debugging/SKILL.md +6 -0
- package/bundled-skills/photopea-embedded-editor/SKILL.md +3 -1218
- package/bundled-skills/photopea-embedded-editor/references/detailed-guide.md +1252 -0
- package/bundled-skills/php-pro/SKILL.md +6 -0
- package/bundled-skills/pi-custom-model/SKILL.md +6 -0
- package/bundled-skills/pipedrive-automation/SKILL.md +6 -0
- package/bundled-skills/pitch-psychologist/SKILL.md +6 -0
- package/bundled-skills/plaid-fintech/SKILL.md +8 -829
- package/bundled-skills/plaid-fintech/references/detailed-guide.md +837 -0
- package/bundled-skills/plotly/SKILL.md +1 -0
- package/bundled-skills/polars/SKILL.md +1 -0
- package/bundled-skills/popup-cro/SKILL.md +6 -0
- package/bundled-skills/posix-shell-pro/SKILL.md +6 -0
- package/bundled-skills/postgresql-cli/SKILL.md +1 -1
- package/bundled-skills/postman-collection-generator/SKILL.md +1 -1
- package/bundled-skills/postman-newman-automation/SKILL.md +1 -1
- package/bundled-skills/postman-openapi-converter/SKILL.md +1 -1
- package/bundled-skills/power-user-cultivation/SKILL.md +6 -572
- package/bundled-skills/power-user-cultivation/references/detailed-guide.md +585 -0
- package/bundled-skills/pr-writer/SKILL.md +1 -0
- package/bundled-skills/price-psychology-strategist/SKILL.md +6 -0
- package/bundled-skills/pricing/SKILL.md +1 -1
- package/bundled-skills/privacy-mask/SKILL.md +1 -1
- package/bundled-skills/product-decision-agent/SKILL.md +6 -0
- package/bundled-skills/product-inventor/SKILL.md +2 -627
- package/bundled-skills/product-inventor/references/detailed-guide.md +646 -0
- package/bundled-skills/product-manager/SKILL.md +6 -0
- package/bundled-skills/product-marketing/SKILL.md +1 -1
- package/bundled-skills/production-code-audit/SKILL.md +2 -264
- package/bundled-skills/production-code-audit/references/detailed-guide.md +277 -0
- package/bundled-skills/professional-proofreader/SKILL.md +6 -0
- package/bundled-skills/programmatic-seo/SKILL.md +6 -0
- package/bundled-skills/project-development/SKILL.md +1 -0
- package/bundled-skills/project-skill-audit/SKILL.md +6 -0
- package/bundled-skills/public-relations/SKILL.md +1 -1
- package/bundled-skills/pubmed-database/SKILL.md +1 -0
- package/bundled-skills/push-skill-to-github/SKILL.md +6 -0
- package/bundled-skills/pypict-skill/SKILL.md +6 -0
- package/bundled-skills/pytest-skill/SKILL.md +1 -1
- package/bundled-skills/qiskit/SKILL.md +1 -0
- package/bundled-skills/quant-analyst/SKILL.md +6 -0
- package/bundled-skills/radix-ui-design-system/SKILL.md +2 -693
- package/bundled-skills/radix-ui-design-system/references/detailed-guide.md +711 -0
- package/bundled-skills/rag-engineer/SKILL.md +6 -0
- package/bundled-skills/rclone-cli/SKILL.md +1 -1
- package/bundled-skills/react-flow-architect/SKILL.md +2 -507
- package/bundled-skills/react-flow-architect/references/detailed-guide.md +517 -0
- package/bundled-skills/react-patterns/SKILL.md +6 -0
- package/bundled-skills/read-all-adrs/SKILL.md +6 -0
- package/bundled-skills/readme/SKILL.md +4 -822
- package/bundled-skills/readme/references/detailed-guide.md +829 -0
- package/bundled-skills/redesign-existing-projects/SKILL.md +6 -0
- package/bundled-skills/redis-cli/SKILL.md +1 -1
- package/bundled-skills/referral-program/SKILL.md +2 -508
- package/bundled-skills/referral-program/references/detailed-guide.md +524 -0
- package/bundled-skills/rehabilitation-analyzer/SKILL.md +5 -629
- package/bundled-skills/rehabilitation-analyzer/references/detailed-guide.md +641 -0
- package/bundled-skills/remotion/SKILL.md +1 -0
- package/bundled-skills/research-prompt/SKILL.md +6 -0
- package/bundled-skills/resolving-merge-conflicts/SKILL.md +6 -0
- package/bundled-skills/review-and-simplify-changes/SKILL.md +7 -1
- package/bundled-skills/review-swarm/SKILL.md +7 -1
- package/bundled-skills/risk-manager/SKILL.md +6 -0
- package/bundled-skills/robius-app-architecture/SKILL.md +1 -0
- package/bundled-skills/robius-event-action/SKILL.md +1 -0
- package/bundled-skills/robius-matrix-integration/SKILL.md +1 -0
- package/bundled-skills/robius-state-management/SKILL.md +1 -0
- package/bundled-skills/robius-widget-patterns/SKILL.md +1 -0
- package/bundled-skills/robot-framework-skill/SKILL.md +1 -1
- package/bundled-skills/ruby-pro/SKILL.md +6 -0
- package/bundled-skills/saga-orchestration/SKILL.md +4 -482
- package/bundled-skills/saga-orchestration/references/detailed-guide.md +491 -0
- package/bundled-skills/sales-automator/SKILL.md +6 -0
- package/bundled-skills/salesforce-development/SKILL.md +2 -985
- package/bundled-skills/salesforce-development/references/detailed-guide.md +994 -0
- package/bundled-skills/sam-altman/SKILL.md +5 -1040
- package/bundled-skills/sam-altman/references/detailed-guide.md +1102 -0
- package/bundled-skills/scala-pro/SKILL.md +6 -0
- package/bundled-skills/scanning-tools/SKILL.md +2 -546
- package/bundled-skills/scanning-tools/references/detailed-guide.md +555 -0
- package/bundled-skills/scanpy/SKILL.md +1 -0
- package/bundled-skills/scarcity-urgency-psychologist/SKILL.md +6 -0
- package/bundled-skills/scientific-writing/SKILL.md +3 -691
- package/bundled-skills/scientific-writing/references/detailed-guide.md +702 -0
- package/bundled-skills/scikit-learn/SKILL.md +3 -462
- package/bundled-skills/scikit-learn/references/detailed-guide.md +475 -0
- package/bundled-skills/scroll-experience/SKILL.md +2 -560
- package/bundled-skills/scroll-experience/references/detailed-guide.md +578 -0
- package/bundled-skills/sdk-dx/SKILL.md +6 -517
- package/bundled-skills/sdk-dx/references/detailed-guide.md +530 -0
- package/bundled-skills/seaborn/SKILL.md +5 -661
- package/bundled-skills/seaborn/references/detailed-guide.md +677 -0
- package/bundled-skills/search-specialist/SKILL.md +6 -0
- package/bundled-skills/security/aws-compliance-checker/SKILL.md +4 -492
- package/bundled-skills/security/aws-compliance-checker/references/detailed-guide.md +502 -0
- package/bundled-skills/security-bluebook-builder/SKILL.md +7 -0
- package/bundled-skills/security-scanning-security-hardening/SKILL.md +6 -0
- package/bundled-skills/security-scanning-security-sast/SKILL.md +2 -463
- package/bundled-skills/security-scanning-security-sast/references/detailed-guide.md +476 -0
- package/bundled-skills/segment-cdp/SKILL.md +8 -822
- package/bundled-skills/segment-cdp/references/detailed-guide.md +830 -0
- package/bundled-skills/selenium-skill/SKILL.md +1 -1
- package/bundled-skills/semgrep-rule-creator/SKILL.md +1 -0
- package/bundled-skills/semgrep-rule-variant-creator/SKILL.md +1 -0
- package/bundled-skills/sendgrid-automation/SKILL.md +6 -0
- package/bundled-skills/seo-audit/SKILL.md +33 -44
- package/bundled-skills/seo-content/SKILL.md +6 -0
- package/bundled-skills/seo-content-auditor/SKILL.md +6 -0
- package/bundled-skills/seo-content-writer/SKILL.md +6 -0
- package/bundled-skills/seo-forensic-incident-response/SKILL.md +6 -0
- package/bundled-skills/seo-fundamentals/SKILL.md +6 -0
- package/bundled-skills/seo-programmatic/SKILL.md +6 -0
- package/bundled-skills/sequence-psychologist/SKILL.md +6 -0
- package/bundled-skills/server-management/SKILL.md +6 -0
- package/bundled-skills/service-mesh-expert/SKILL.md +6 -0
- package/bundled-skills/setup-help/SKILL.md +6 -0
- package/bundled-skills/sexual-health-analyzer/SKILL.md +8 -1109
- package/bundled-skills/sexual-health-analyzer/references/detailed-guide.md +1123 -0
- package/bundled-skills/sharp-coder/SKILL.md +1 -0
- package/bundled-skills/sharp-edges/SKILL.md +1 -0
- package/bundled-skills/shodan-reconnaissance/SKILL.md +2 -356
- package/bundled-skills/shodan-reconnaissance/references/detailed-guide.md +366 -0
- package/bundled-skills/shopify-apps/SKILL.md +2 -1477
- package/bundled-skills/shopify-apps/references/detailed-guide.md +1512 -0
- package/bundled-skills/short/SKILL.md +6 -0
- package/bundled-skills/signup-flow-cro/SKILL.md +6 -0
- package/bundled-skills/simplify-code/SKILL.md +6 -0
- package/bundled-skills/skill-creator/SKILL.md +2 -568
- package/bundled-skills/skill-creator/references/detailed-guide.md +581 -0
- package/bundled-skills/skill-creator-ms/SKILL.md +2 -601
- package/bundled-skills/skill-creator-ms/references/detailed-guide.md +614 -0
- package/bundled-skills/skill-improver/SKILL.md +1 -0
- package/bundled-skills/skill-router/SKILL.md +1 -0
- package/bundled-skills/skill-scanner/SKILL.md +1 -0
- package/bundled-skills/skill-security-audit/SKILL.md +87 -0
- package/bundled-skills/skill-seekers/SKILL.md +6 -0
- package/bundled-skills/skill-writer/SKILL.md +1 -0
- package/bundled-skills/skin-health-analyzer/SKILL.md +8 -696
- package/bundled-skills/skin-health-analyzer/references/detailed-guide.md +710 -0
- package/bundled-skills/slack-automation/SKILL.md +6 -0
- package/bundled-skills/slack-bot-builder/SKILL.md +2 -1372
- package/bundled-skills/slack-bot-builder/references/detailed-guide.md +1395 -0
- package/bundled-skills/sleep-analyzer/SKILL.md +5 -764
- package/bundled-skills/sleep-analyzer/references/detailed-guide.md +773 -0
- package/bundled-skills/smartui-skill/SKILL.md +1 -1
- package/bundled-skills/smtp-penetration-testing/SKILL.md +2 -346
- package/bundled-skills/smtp-penetration-testing/references/detailed-guide.md +355 -0
- package/bundled-skills/social-content/SKILL.md +2 -797
- package/bundled-skills/social-content/references/detailed-guide.md +816 -0
- package/bundled-skills/social-proof-architect/SKILL.md +6 -0
- package/bundled-skills/software-architecture/SKILL.md +6 -0
- package/bundled-skills/spec-to-code-compliance/SKILL.md +7 -0
- package/bundled-skills/speckit-updater/SKILL.md +1 -0
- package/bundled-skills/speed/SKILL.md +7 -0
- package/bundled-skills/sred-project-organizer/SKILL.md +1 -0
- package/bundled-skills/sred-work-summary/SKILL.md +1 -0
- package/bundled-skills/statsmodels/SKILL.md +3 -586
- package/bundled-skills/statsmodels/references/detailed-guide.md +600 -0
- package/bundled-skills/steve-jobs/SKILL.md +2 -564
- package/bundled-skills/steve-jobs/references/detailed-guide.md +574 -0
- package/bundled-skills/stitch-loop/SKILL.md +1 -0
- package/bundled-skills/stripe-automation/SKILL.md +6 -0
- package/bundled-skills/stripe-integration/SKILL.md +60 -79
- package/bundled-skills/styleseed-design-review/SKILL.md +1 -1
- package/bundled-skills/subagent-orchestrator/SKILL.md +1 -0
- package/bundled-skills/subject-line-psychologist/SKILL.md +6 -0
- package/bundled-skills/supabase/SKILL.md +1 -1
- package/bundled-skills/supabase-automation/SKILL.md +6 -0
- package/bundled-skills/superpowers-lab/SKILL.md +6 -0
- package/bundled-skills/supply-chain-risk-auditor/SKILL.md +7 -0
- package/bundled-skills/swiftui-expert-skill/SKILL.md +1 -1
- package/bundled-skills/sympy/SKILL.md +3 -426
- package/bundled-skills/sympy/references/detailed-guide.md +439 -0
- package/bundled-skills/systematic-debugging/CREATION-LOG.md +3 -0
- package/bundled-skills/systematic-debugging/SKILL.md +30 -34
- package/bundled-skills/systematic-debugging/condition-based-waiting.md +9 -6
- package/bundled-skills/systematic-debugging/defense-in-depth.md +13 -7
- package/bundled-skills/systematic-debugging/find-polluter.sh +77 -57
- package/bundled-skills/systematic-debugging/root-cause-tracing.md +7 -5
- package/bundled-skills/systematic-debugging/test-academic.md +3 -0
- package/bundled-skills/systematic-debugging/test-pressure-1.md +3 -0
- package/bundled-skills/systematic-debugging/test-pressure-2.md +3 -0
- package/bundled-skills/systematic-debugging/test-pressure-3.md +3 -0
- package/bundled-skills/tcm-constitution-analyzer/SKILL.md +5 -655
- package/bundled-skills/tcm-constitution-analyzer/references/detailed-guide.md +660 -0
- package/bundled-skills/tdd-workflows-tdd-cycle/SKILL.md +6 -0
- package/bundled-skills/technical-tutorials/SKILL.md +5 -483
- package/bundled-skills/technical-tutorials/references/detailed-guide.md +496 -0
- package/bundled-skills/telegram/SKILL.md +2 -541
- package/bundled-skills/telegram/references/detailed-guide.md +569 -0
- package/bundled-skills/telegram-mini-app/SKILL.md +2 -643
- package/bundled-skills/telegram-mini-app/references/detailed-guide.md +661 -0
- package/bundled-skills/temporal-python-pro/SKILL.md +6 -0
- package/bundled-skills/terraform-skill/SKILL.md +4 -417
- package/bundled-skills/terraform-skill/references/detailed-guide.md +430 -0
- package/bundled-skills/test-driven-development/SKILL.md +21 -124
- package/bundled-skills/test-driven-development/testing-anti-patterns.md +6 -6
- package/bundled-skills/test-framework-migration-skill/SKILL.md +7 -1
- package/bundled-skills/threat-modeling-expert/SKILL.md +6 -0
- package/bundled-skills/threejs-animation/SKILL.md +5 -551
- package/bundled-skills/threejs-animation/references/detailed-guide.md +566 -0
- package/bundled-skills/threejs-fundamentals/SKILL.md +5 -525
- package/bundled-skills/threejs-fundamentals/references/detailed-guide.md +535 -0
- package/bundled-skills/threejs-geometry/SKILL.md +5 -572
- package/bundled-skills/threejs-geometry/references/detailed-guide.md +587 -0
- package/bundled-skills/threejs-interaction/SKILL.md +5 -666
- package/bundled-skills/threejs-interaction/references/detailed-guide.md +679 -0
- package/bundled-skills/threejs-lighting/SKILL.md +1 -0
- package/bundled-skills/threejs-loaders/SKILL.md +5 -637
- package/bundled-skills/threejs-loaders/references/detailed-guide.md +651 -0
- package/bundled-skills/threejs-materials/SKILL.md +3 -535
- package/bundled-skills/threejs-materials/references/detailed-guide.md +560 -0
- package/bundled-skills/threejs-postprocessing/SKILL.md +5 -617
- package/bundled-skills/threejs-postprocessing/references/detailed-guide.md +630 -0
- package/bundled-skills/threejs-shaders/SKILL.md +5 -679
- package/bundled-skills/threejs-shaders/references/detailed-guide.md +695 -0
- package/bundled-skills/threejs-skills/SKILL.md +4 -683
- package/bundled-skills/threejs-skills/references/detailed-guide.md +693 -0
- package/bundled-skills/threejs-textures/SKILL.md +5 -630
- package/bundled-skills/threejs-textures/references/detailed-guide.md +649 -0
- package/bundled-skills/to-issues/SKILL.md +5 -0
- package/bundled-skills/to-prd/SKILL.md +5 -0
- package/bundled-skills/todoist-automation/SKILL.md +6 -0
- package/bundled-skills/tools-page-seo-optimizer/SKILL.md +2 -580
- package/bundled-skills/tools-page-seo-optimizer/references/detailed-guide.md +602 -0
- package/bundled-skills/top-web-vulnerabilities/SKILL.md +2 -513
- package/bundled-skills/top-web-vulnerabilities/references/detailed-guide.md +523 -0
- package/bundled-skills/train-sentence-transformers/SKILL.md +1 -1
- package/bundled-skills/transformers-js/SKILL.md +5 -669
- package/bundled-skills/transformers-js/references/detailed-guide.md +683 -0
- package/bundled-skills/travel-health-analyzer/SKILL.md +1 -0
- package/bundled-skills/trello-automation/SKILL.md +6 -0
- package/bundled-skills/trigger-dev/SKILL.md +2 -937
- package/bundled-skills/trigger-dev/references/detailed-guide.md +950 -0
- package/bundled-skills/trust-calibrator/SKILL.md +6 -0
- package/bundled-skills/tutorial-engineer/SKILL.md +6 -0
- package/bundled-skills/twilio-communications/SKILL.md +2 -1549
- package/bundled-skills/twilio-communications/references/detailed-guide.md +1574 -0
- package/bundled-skills/typescript-pro/SKILL.md +6 -0
- package/bundled-skills/ui-a11y/SKILL.md +6 -0
- package/bundled-skills/ui-component/SKILL.md +6 -0
- package/bundled-skills/ui-motion/SKILL.md +7 -1
- package/bundled-skills/ui-pattern/SKILL.md +6 -0
- package/bundled-skills/ui-review/SKILL.md +6 -0
- package/bundled-skills/ui-skills/SKILL.md +6 -0
- package/bundled-skills/ui-tokens/SKILL.md +6 -0
- package/bundled-skills/uniprot-database/SKILL.md +1 -0
- package/bundled-skills/unslop-commit/SKILL.md +1 -1
- package/bundled-skills/unslop-file/SKILL.md +1 -1
- package/bundled-skills/unslop-review/SKILL.md +1 -1
- package/bundled-skills/unsplash-integration/SKILL.md +6 -0
- package/bundled-skills/update-swiftui-apis/SKILL.md +1 -1
- package/bundled-skills/upstash-qstash/SKILL.md +2 -919
- package/bundled-skills/upstash-qstash/references/detailed-guide.md +932 -0
- package/bundled-skills/usage-based-pricing/SKILL.md +5 -395
- package/bundled-skills/usage-based-pricing/references/detailed-guide.md +407 -0
- package/bundled-skills/ux-audit/SKILL.md +6 -0
- package/bundled-skills/ux-flow/SKILL.md +6 -0
- package/bundled-skills/ux-persuasion-engineer/SKILL.md +6 -0
- package/bundled-skills/variant-analysis/SKILL.md +1 -0
- package/bundled-skills/varlock/SKILL.md +1 -0
- package/bundled-skills/varlock-claude-skill/SKILL.md +6 -0
- package/bundled-skills/vector-database-engineer/SKILL.md +6 -0
- package/bundled-skills/vercel-deployment/SKILL.md +2 -650
- package/bundled-skills/vercel-deployment/references/detailed-guide.md +660 -0
- package/bundled-skills/vexor/SKILL.md +6 -0
- package/bundled-skills/vexor-cli/SKILL.md +1 -0
- package/bundled-skills/visual-emotion-engineer/SKILL.md +6 -0
- package/bundled-skills/vizcom/SKILL.md +6 -0
- package/bundled-skills/voice-agents/SKILL.md +2 -867
- package/bundled-skills/voice-agents/references/detailed-guide.md +890 -0
- package/bundled-skills/voice-ai-development/SKILL.md +2 -583
- package/bundled-skills/voice-ai-development/references/detailed-guide.md +594 -0
- package/bundled-skills/voice-ai-engine-development/SKILL.md +2 -702
- package/bundled-skills/voice-ai-engine-development/references/detailed-guide.md +720 -0
- package/bundled-skills/warren-buffett/SKILL.md +2 -577
- package/bundled-skills/warren-buffett/references/detailed-guide.md +583 -0
- package/bundled-skills/web-performance-optimization/SKILL.md +2 -214
- package/bundled-skills/web-performance-optimization/references/detailed-guide.md +226 -0
- package/bundled-skills/web-scraper/SKILL.md +2 -728
- package/bundled-skills/web-scraper/references/detailed-guide.md +792 -0
- package/bundled-skills/webflow-automation/SKILL.md +6 -0
- package/bundled-skills/weightloss-analyzer/SKILL.md +1 -0
- package/bundled-skills/wellally-tech/SKILL.md +5 -367
- package/bundled-skills/wellally-tech/references/detailed-guide.md +379 -0
- package/bundled-skills/wiki-architect/SKILL.md +6 -0
- package/bundled-skills/wiki-changelog/SKILL.md +6 -0
- package/bundled-skills/wiki-onboarding/SKILL.md +6 -0
- package/bundled-skills/wiki-qa/SKILL.md +6 -0
- package/bundled-skills/wiki-researcher/SKILL.md +6 -0
- package/bundled-skills/windows-privilege-escalation/SKILL.md +131 -0
- package/bundled-skills/windows-privilege-escalation/references/detailed-guide.md +397 -0
- package/bundled-skills/wireshark-analysis/SKILL.md +2 -419
- package/bundled-skills/wireshark-analysis/references/detailed-guide.md +429 -0
- package/bundled-skills/wjttc-builder/SKILL.md +1 -1
- package/bundled-skills/wjttc-tester/SKILL.md +1 -1
- package/bundled-skills/wordpress/SKILL.md +2 -572
- package/bundled-skills/wordpress/references/detailed-guide.md +582 -0
- package/bundled-skills/wordpress-penetration-testing/SKILL.md +68 -0
- package/bundled-skills/wordpress-penetration-testing/references/detailed-guide.md +555 -0
- package/bundled-skills/wordpress-plugin-development/SKILL.md +2 -474
- package/bundled-skills/wordpress-plugin-development/references/detailed-guide.md +485 -0
- package/bundled-skills/wordpress-theme-development/SKILL.md +2 -479
- package/bundled-skills/wordpress-theme-development/references/detailed-guide.md +490 -0
- package/bundled-skills/wordpress-woocommerce-development/SKILL.md +2 -605
- package/bundled-skills/wordpress-woocommerce-development/references/detailed-guide.md +615 -0
- package/bundled-skills/workflow-automation/SKILL.md +2 -875
- package/bundled-skills/workflow-automation/references/detailed-guide.md +902 -0
- package/bundled-skills/writing-great-skills/SKILL.md +5 -0
- package/bundled-skills/x-article-publisher-skill/SKILL.md +6 -0
- package/bundled-skills/x402-express-wrapper/SKILL.md +1 -0
- package/bundled-skills/xss-html-injection/SKILL.md +2 -389
- package/bundled-skills/xss-html-injection/references/detailed-guide.md +399 -0
- package/bundled-skills/yann-lecun/SKILL.md +2 -1431
- package/bundled-skills/yann-lecun/references/detailed-guide.md +1484 -0
- package/bundled-skills/yann-lecun-filosofia/SKILL.md +6 -0
- package/bundled-skills/yann-lecun-tecnico/SKILL.md +2 -485
- package/bundled-skills/yann-lecun-tecnico/references/detailed-guide.md +502 -0
- package/bundled-skills/youtube-seo-optimizer/SKILL.md +4 -884
- package/bundled-skills/youtube-seo-optimizer/references/detailed-guide.md +907 -0
- package/bundled-skills/zapier-make-patterns/SKILL.md +2 -728
- package/bundled-skills/zapier-make-patterns/references/detailed-guide.md +752 -0
- package/bundled-skills/zipai-optimizer/SKILL.md +7 -0
- package/bundled-skills/zoom-automation/SKILL.md +6 -0
- package/package.json +1 -1
- package/skills_index.json +608 -383
- package/bundled-skills/docs/AUDIT.md +0 -3
- package/bundled-skills/docs/BUNDLES.md +0 -3
- package/bundled-skills/docs/CATEGORIZATION_IMPLEMENTATION.md +0 -3
- package/bundled-skills/docs/CI_DRIFT_FIX.md +0 -3
- package/bundled-skills/docs/COMMUNITY_GUIDELINES.md +0 -3
- package/bundled-skills/docs/DATE_TRACKING_IMPLEMENTATION.md +0 -3
- package/bundled-skills/docs/EXAMPLES.md +0 -3
- package/bundled-skills/docs/FAQ.md +0 -3
- package/bundled-skills/docs/GETTING_STARTED.md +0 -3
- package/bundled-skills/docs/KIRO_INTEGRATION.md +0 -3
- package/bundled-skills/docs/QUALITY_BAR.md +0 -3
- package/bundled-skills/docs/README.md +0 -51
- package/bundled-skills/docs/SECURITY_GUARDRAILS.md +0 -3
- package/bundled-skills/docs/SEC_SKILLS.md +0 -3
- package/bundled-skills/docs/SKILLS_DATE_TRACKING.md +0 -3
- package/bundled-skills/docs/SKILL_ANATOMY.md +0 -3
- package/bundled-skills/docs/SKILL_TEMPLATE.md +0 -3
- package/bundled-skills/docs/SMART_AUTO_CATEGORIZATION.md +0 -3
- package/bundled-skills/docs/SOURCES.md +0 -3
- package/bundled-skills/docs/USAGE.md +0 -3
- package/bundled-skills/docs/VISUAL_GUIDE.md +0 -3
- package/bundled-skills/docs/WORKFLOWS.md +0 -3
- package/bundled-skills/docs/contributors/community-guidelines.md +0 -4
- package/bundled-skills/docs/contributors/examples.md +0 -760
- package/bundled-skills/docs/contributors/quality-bar.md +0 -104
- package/bundled-skills/docs/contributors/security-guardrails.md +0 -70
- package/bundled-skills/docs/contributors/skill-anatomy.md +0 -637
- package/bundled-skills/docs/contributors/skill-template.md +0 -93
- package/bundled-skills/docs/integrations/jetski-cortex.md +0 -282
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +0 -103
- package/bundled-skills/docs/integrations/jetski-gemini-loader/loader.mjs +0 -154
- package/bundled-skills/docs/integrations/jetski-gemini-loader/package.json +0 -1
- package/bundled-skills/docs/maintainers/aas-agent-first-control-plane-preview-profile.md +0 -63
- package/bundled-skills/docs/maintainers/aas-agent-first-control-plane-v1-design.md +0 -304
- package/bundled-skills/docs/maintainers/aas-agent-first-control-plane-v1-goal.md +0 -171
- package/bundled-skills/docs/maintainers/aas-agent-first-control-plane-v1-worklog.md +0 -104
- package/bundled-skills/docs/maintainers/audit.md +0 -89
- package/bundled-skills/docs/maintainers/backups/README-2026-06-02.md +0 -687
- package/bundled-skills/docs/maintainers/categorization-implementation.md +0 -160
- package/bundled-skills/docs/maintainers/ci-drift-fix.md +0 -64
- package/bundled-skills/docs/maintainers/date-tracking-implementation.md +0 -66
- package/bundled-skills/docs/maintainers/full-repo-audit-2026-05-23.md +0 -289
- package/bundled-skills/docs/maintainers/legacy-redirect-bridge.md +0 -46
- package/bundled-skills/docs/maintainers/merge-batch.md +0 -81
- package/bundled-skills/docs/maintainers/merging-prs.md +0 -79
- package/bundled-skills/docs/maintainers/pr-autonomy.md +0 -102
- package/bundled-skills/docs/maintainers/provenance-identity-exceptions.json +0 -14
- package/bundled-skills/docs/maintainers/release-notes-7.2.0.md +0 -32
- package/bundled-skills/docs/maintainers/release-process.md +0 -124
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +0 -133
- package/bundled-skills/docs/maintainers/rollback-procedure.md +0 -43
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-15.csv +0 -34
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-15.md +0 -60
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-18-addendum.md +0 -22
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-29-addendum.md +0 -48
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-29-refresh.csv +0 -34
- package/bundled-skills/docs/maintainers/security-findings-triage-2026-03-29-refresh.md +0 -86
- package/bundled-skills/docs/maintainers/skills-date-tracking.md +0 -228
- package/bundled-skills/docs/maintainers/skills-import-2026-03-21.md +0 -81
- package/bundled-skills/docs/maintainers/skills-update-guide.md +0 -92
- package/bundled-skills/docs/maintainers/smart-auto-categorization.md +0 -218
- package/bundled-skills/docs/plugin-submissions/aas-agent-mcp-builder/README.md +0 -19
- package/bundled-skills/docs/plugin-submissions/aas-agent-mcp-builder/evaluation-cases.json +0 -74
- package/bundled-skills/docs/plugin-submissions/aas-agent-mcp-builder/evaluation-results.json +0 -86
- package/bundled-skills/docs/plugin-submissions/aas-agent-mcp-builder/submission.json +0 -41
- package/bundled-skills/docs/sources/LICENSE-MICROSOFT +0 -21
- package/bundled-skills/docs/sources/microsoft-skills-attribution.json +0 -709
- package/bundled-skills/docs/sources/sources.md +0 -179
- package/bundled-skills/docs/users/aas-core.md +0 -225
- package/bundled-skills/docs/users/agent-overload-recovery.md +0 -54
- package/bundled-skills/docs/users/agentic-awesome-skills-vs-awesome-claude-skills.md +0 -44
- package/bundled-skills/docs/users/ai-agent-skills.md +0 -48
- package/bundled-skills/docs/users/best-claude-code-skills-github.md +0 -62
- package/bundled-skills/docs/users/best-cursor-skills-github.md +0 -62
- package/bundled-skills/docs/users/bundles.md +0 -1067
- package/bundled-skills/docs/users/claude-code-skills.md +0 -89
- package/bundled-skills/docs/users/codex-cli-skills.md +0 -88
- package/bundled-skills/docs/users/cursor-skills.md +0 -56
- package/bundled-skills/docs/users/discovery-manifest.md +0 -65
- package/bundled-skills/docs/users/faq.md +0 -521
- package/bundled-skills/docs/users/gemini-cli-skills.md +0 -56
- package/bundled-skills/docs/users/getting-started.md +0 -233
- package/bundled-skills/docs/users/kiro-integration.md +0 -304
- package/bundled-skills/docs/users/local-config.md +0 -152
- package/bundled-skills/docs/users/plugins.md +0 -201
- package/bundled-skills/docs/users/security-and-antivirus.md +0 -63
- package/bundled-skills/docs/users/security-skills.md +0 -1722
- package/bundled-skills/docs/users/skills-library-overview.md +0 -87
- package/bundled-skills/docs/users/skills-vs-mcp-tools.md +0 -108
- package/bundled-skills/docs/users/specialized-plugin-roadmap.md +0 -101
- package/bundled-skills/docs/users/usage.md +0 -465
- package/bundled-skills/docs/users/visual-guide.md +0 -515
- package/bundled-skills/docs/users/walkthrough.md +0 -46
- package/bundled-skills/docs/users/windows-truncation-recovery.md +0 -135
- package/bundled-skills/docs/users/workflows.md +0 -215
- package/bundled-skills/docs/vietnamese/AAS_CORE.vi.md +0 -28
- package/bundled-skills/docs/vietnamese/BUNDLES.vi.md +0 -124
- package/bundled-skills/docs/vietnamese/CONTRIBUTING.vi.md +0 -242
- package/bundled-skills/docs/vietnamese/EXAMPLES.vi.md +0 -56
- package/bundled-skills/docs/vietnamese/FAQ.vi.md +0 -195
- package/bundled-skills/docs/vietnamese/GETTING_STARTED.vi.md +0 -123
- package/bundled-skills/docs/vietnamese/QUALITY_BAR.vi.md +0 -69
- package/bundled-skills/docs/vietnamese/README.vi.md +0 -191
- package/bundled-skills/docs/vietnamese/SECURITY.vi.md +0 -19
- package/bundled-skills/docs/vietnamese/SECURITY_GUARDRAILS.vi.md +0 -51
- package/bundled-skills/docs/vietnamese/SKILLS_README.vi.md +0 -106
- package/bundled-skills/docs/vietnamese/SKILL_ANATOMY.vi.md +0 -633
- package/bundled-skills/docs/vietnamese/SOURCES.vi.md +0 -21
- package/bundled-skills/docs/vietnamese/TRANSLATION_PLAN.vi.md +0 -65
- package/bundled-skills/docs/vietnamese/VISUAL_GUIDE.vi.md +0 -511
- package/bundled-skills/docs/walkthrough.md +0 -3
|
@@ -3,6 +3,7 @@ name: fp-refactor
|
|
|
3
3
|
description: Comprehensive guide for refactoring imperative TypeScript code to fp-ts functional patterns
|
|
4
4
|
risk: critical
|
|
5
5
|
source: community
|
|
6
|
+
date_added: "2026-09-04"
|
|
6
7
|
version: 1.0.0
|
|
7
8
|
author: fp-ts-skills
|
|
8
9
|
tags:
|
|
@@ -21,1772 +22,15 @@ tags:
|
|
|
21
22
|
|
|
22
23
|
This skill provides comprehensive patterns and strategies for migrating existing imperative TypeScript code to fp-ts functional programming patterns.
|
|
23
24
|
|
|
25
|
+
## Detailed Guide
|
|
26
|
+
|
|
27
|
+
Read [the detailed guide](references/detailed-guide.md) before executing this skill. It retains the complete procedure and reference material. Treat its safety, prerequisites, and validation requirements as mandatory. For focused work, load the relevant sections; for end-to-end work, read the guide completely.
|
|
28
|
+
|
|
24
29
|
## When to Use
|
|
25
30
|
- You are refactoring an existing imperative TypeScript codebase toward fp-ts patterns.
|
|
26
31
|
- The task involves converting `try/catch`, null checks, callbacks, DI, or loops into functional equivalents.
|
|
27
32
|
- You need migration guidance and tradeoffs, not just isolated fp-ts examples.
|
|
28
33
|
|
|
29
|
-
## Table of Contents
|
|
30
|
-
|
|
31
|
-
1. [Converting try-catch to Either/TaskEither](#1-converting-try-catch-to-eithertaskeither)
|
|
32
|
-
2. [Converting null checks to Option](#2-converting-null-checks-to-option)
|
|
33
|
-
3. [Converting callbacks to Task](#3-converting-callbacks-to-task)
|
|
34
|
-
4. [Converting class-based DI to Reader](#4-converting-class-based-di-to-reader)
|
|
35
|
-
5. [Converting imperative loops to functional operations](#5-converting-imperative-loops-to-functional-operations)
|
|
36
|
-
6. [Migrating Promise chains to TaskEither](#6-migrating-promise-chains-to-taskeither)
|
|
37
|
-
7. [Common Pitfalls](#7-common-pitfalls)
|
|
38
|
-
8. [Gradual Adoption Strategies](#8-gradual-adoption-strategies)
|
|
39
|
-
9. [When NOT to Refactor](#9-when-not-to-refactor)
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## 1. Converting try-catch to Either/TaskEither
|
|
44
|
-
|
|
45
|
-
### The Problem with try-catch
|
|
46
|
-
|
|
47
|
-
Traditional try-catch blocks have several issues:
|
|
48
|
-
- Error handling is implicit and easy to forget
|
|
49
|
-
- The type system doesn't track which functions can throw
|
|
50
|
-
- Control flow is non-linear and harder to reason about
|
|
51
|
-
- Composing multiple fallible operations is verbose
|
|
52
|
-
|
|
53
|
-
### Pattern: Synchronous try-catch to Either
|
|
54
|
-
|
|
55
|
-
#### Before (Imperative)
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
function parseJSON(input: string): unknown {
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(input);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
throw new Error(`Invalid JSON: ${error}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function validateUser(data: unknown): User {
|
|
67
|
-
try {
|
|
68
|
-
if (!data || typeof data !== 'object') {
|
|
69
|
-
throw new Error('Data must be an object');
|
|
70
|
-
}
|
|
71
|
-
const obj = data as Record<string, unknown>;
|
|
72
|
-
if (typeof obj.name !== 'string') {
|
|
73
|
-
throw new Error('Name is required');
|
|
74
|
-
}
|
|
75
|
-
if (typeof obj.age !== 'number') {
|
|
76
|
-
throw new Error('Age must be a number');
|
|
77
|
-
}
|
|
78
|
-
return { name: obj.name, age: obj.age };
|
|
79
|
-
} catch (error) {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Usage with nested try-catch
|
|
85
|
-
function processUserInput(input: string): User | null {
|
|
86
|
-
try {
|
|
87
|
-
const data = parseJSON(input);
|
|
88
|
-
const user = validateUser(data);
|
|
89
|
-
return user;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.error('Failed to process user:', error);
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
#### After (fp-ts Either)
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
import * as E from 'fp-ts/Either';
|
|
101
|
-
import * as J from 'fp-ts/Json';
|
|
102
|
-
import { pipe } from 'fp-ts/function';
|
|
103
|
-
|
|
104
|
-
interface User {
|
|
105
|
-
name: string;
|
|
106
|
-
age: number;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Use Json.parse which returns Either<Error, Json>
|
|
110
|
-
const parseJSON = (input: string): E.Either<Error, unknown> =>
|
|
111
|
-
pipe(
|
|
112
|
-
J.parse(input),
|
|
113
|
-
E.mapLeft((e) => new Error(`Invalid JSON: ${e}`))
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
// Validation returns Either, making errors explicit in types
|
|
117
|
-
const validateUser = (data: unknown): E.Either<Error, User> => {
|
|
118
|
-
if (!data || typeof data !== 'object') {
|
|
119
|
-
return E.left(new Error('Data must be an object'));
|
|
120
|
-
}
|
|
121
|
-
const obj = data as Record<string, unknown>;
|
|
122
|
-
if (typeof obj.name !== 'string') {
|
|
123
|
-
return E.left(new Error('Name is required'));
|
|
124
|
-
}
|
|
125
|
-
if (typeof obj.age !== 'number') {
|
|
126
|
-
return E.left(new Error('Age must be a number'));
|
|
127
|
-
}
|
|
128
|
-
return E.right({ name: obj.name, age: obj.age });
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Compose with pipe and flatMap - errors propagate automatically
|
|
132
|
-
const processUserInput = (input: string): E.Either<Error, User> =>
|
|
133
|
-
pipe(
|
|
134
|
-
parseJSON(input),
|
|
135
|
-
E.flatMap(validateUser)
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// Handle both cases explicitly
|
|
139
|
-
pipe(
|
|
140
|
-
processUserInput('{"name": "Alice", "age": 30}'),
|
|
141
|
-
E.match(
|
|
142
|
-
(error) => console.error('Failed to process user:', error.message),
|
|
143
|
-
(user) => console.log('User:', user)
|
|
144
|
-
)
|
|
145
|
-
);
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Step-by-Step Refactoring Guide
|
|
149
|
-
|
|
150
|
-
1. **Identify the error type**: Determine what errors can occur and create appropriate error types
|
|
151
|
-
2. **Change return type**: From `T` to `Either<E, T>` where `E` is your error type
|
|
152
|
-
3. **Replace throw statements**: Convert `throw new Error(...)` to `E.left(new Error(...))`
|
|
153
|
-
4. **Replace return statements**: Convert `return value` to `E.right(value)`
|
|
154
|
-
5. **Remove try-catch blocks**: They're no longer needed
|
|
155
|
-
6. **Update callers**: Use `pipe` with `E.flatMap` to chain operations
|
|
156
|
-
|
|
157
|
-
### Pattern: Async try-catch to TaskEither
|
|
158
|
-
|
|
159
|
-
#### Before (Imperative)
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
async function fetchUser(id: string): Promise<User> {
|
|
163
|
-
try {
|
|
164
|
-
const response = await fetch(`/api/users/${id}`);
|
|
165
|
-
if (!response.ok) {
|
|
166
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
167
|
-
}
|
|
168
|
-
const data = await response.json();
|
|
169
|
-
return validateUser(data);
|
|
170
|
-
} catch (error) {
|
|
171
|
-
throw new Error(`Failed to fetch user: ${error}`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function fetchUserPosts(userId: string): Promise<Post[]> {
|
|
176
|
-
try {
|
|
177
|
-
const response = await fetch(`/api/users/${userId}/posts`);
|
|
178
|
-
if (!response.ok) {
|
|
179
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
180
|
-
}
|
|
181
|
-
return await response.json();
|
|
182
|
-
} catch (error) {
|
|
183
|
-
throw new Error(`Failed to fetch posts: ${error}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Complex orchestration with try-catch
|
|
188
|
-
async function getUserWithPosts(id: string): Promise<{ user: User; posts: Post[] } | null> {
|
|
189
|
-
try {
|
|
190
|
-
const user = await fetchUser(id);
|
|
191
|
-
const posts = await fetchUserPosts(id);
|
|
192
|
-
return { user, posts };
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error(error);
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
#### After (fp-ts TaskEither)
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
204
|
-
import * as E from 'fp-ts/Either';
|
|
205
|
-
import { pipe } from 'fp-ts/function';
|
|
206
|
-
|
|
207
|
-
// Wrap fetch in TaskEither
|
|
208
|
-
const fetchUser = (id: string): TE.TaskEither<Error, User> =>
|
|
209
|
-
pipe(
|
|
210
|
-
TE.tryCatch(
|
|
211
|
-
() => fetch(`/api/users/${id}`),
|
|
212
|
-
(reason) => new Error(`Network error: ${reason}`)
|
|
213
|
-
),
|
|
214
|
-
TE.flatMap((response) =>
|
|
215
|
-
response.ok
|
|
216
|
-
? TE.right(response)
|
|
217
|
-
: TE.left(new Error(`HTTP error: ${response.status}`))
|
|
218
|
-
),
|
|
219
|
-
TE.flatMap((response) =>
|
|
220
|
-
TE.tryCatch(
|
|
221
|
-
() => response.json(),
|
|
222
|
-
(reason) => new Error(`JSON parse error: ${reason}`)
|
|
223
|
-
)
|
|
224
|
-
),
|
|
225
|
-
TE.flatMap((data) => TE.fromEither(validateUser(data)))
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
const fetchUserPosts = (userId: string): TE.TaskEither<Error, Post[]> =>
|
|
229
|
-
pipe(
|
|
230
|
-
TE.tryCatch(
|
|
231
|
-
() => fetch(`/api/users/${userId}/posts`),
|
|
232
|
-
(reason) => new Error(`Network error: ${reason}`)
|
|
233
|
-
),
|
|
234
|
-
TE.flatMap((response) =>
|
|
235
|
-
response.ok
|
|
236
|
-
? TE.right(response)
|
|
237
|
-
: TE.left(new Error(`HTTP error: ${response.status}`))
|
|
238
|
-
),
|
|
239
|
-
TE.flatMap((response) =>
|
|
240
|
-
TE.tryCatch(
|
|
241
|
-
() => response.json(),
|
|
242
|
-
(reason) => new Error(`JSON parse error: ${reason}`)
|
|
243
|
-
)
|
|
244
|
-
)
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
// Clean composition with automatic error propagation
|
|
248
|
-
const getUserWithPosts = (
|
|
249
|
-
id: string
|
|
250
|
-
): TE.TaskEither<Error, { user: User; posts: Post[] }> =>
|
|
251
|
-
pipe(
|
|
252
|
-
TE.Do,
|
|
253
|
-
TE.bind('user', () => fetchUser(id)),
|
|
254
|
-
TE.bind('posts', () => fetchUserPosts(id))
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
// Execute and handle results
|
|
258
|
-
const main = async () => {
|
|
259
|
-
const result = await getUserWithPosts('123')();
|
|
260
|
-
pipe(
|
|
261
|
-
result,
|
|
262
|
-
E.match(
|
|
263
|
-
(error) => console.error('Failed:', error.message),
|
|
264
|
-
({ user, posts }) => console.log('Success:', user, posts)
|
|
265
|
-
)
|
|
266
|
-
);
|
|
267
|
-
};
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Helper: tryCatch Utility
|
|
271
|
-
|
|
272
|
-
Create a reusable wrapper for functions that might throw:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import * as E from 'fp-ts/Either';
|
|
276
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
277
|
-
|
|
278
|
-
// For sync functions
|
|
279
|
-
const tryCatchSync = <A>(f: () => A): E.Either<Error, A> =>
|
|
280
|
-
E.tryCatch(f, (e) => (e instanceof Error ? e : new Error(String(e))));
|
|
281
|
-
|
|
282
|
-
// For async functions
|
|
283
|
-
const tryCatchAsync = <A>(f: () => Promise<A>): TE.TaskEither<Error, A> =>
|
|
284
|
-
TE.tryCatch(f, (e) => (e instanceof Error ? e : new Error(String(e))));
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
## 2. Converting null checks to Option
|
|
290
|
-
|
|
291
|
-
### The Problem with null/undefined
|
|
292
|
-
|
|
293
|
-
- TypeScript's strict null checks help, but null still spreads through code
|
|
294
|
-
- Chained property access requires verbose null guards
|
|
295
|
-
- The distinction between "missing" and "present but null" is unclear
|
|
296
|
-
- Easy to forget null checks leading to runtime errors
|
|
297
|
-
|
|
298
|
-
### Pattern: Simple null checks to Option
|
|
299
|
-
|
|
300
|
-
#### Before (Imperative)
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
interface Config {
|
|
304
|
-
database?: {
|
|
305
|
-
host?: string;
|
|
306
|
-
port?: number;
|
|
307
|
-
credentials?: {
|
|
308
|
-
username?: string;
|
|
309
|
-
password?: string;
|
|
310
|
-
};
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function getDatabaseUrl(config: Config): string | null {
|
|
315
|
-
if (!config.database) {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
if (!config.database.host) {
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
const port = config.database.port ?? 5432;
|
|
322
|
-
|
|
323
|
-
let auth = '';
|
|
324
|
-
if (config.database.credentials) {
|
|
325
|
-
if (config.database.credentials.username && config.database.credentials.password) {
|
|
326
|
-
auth = `${config.database.credentials.username}:${config.database.credentials.password}@`;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return `postgres://${auth}${config.database.host}:${port}`;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Usage requires null check
|
|
334
|
-
const url = getDatabaseUrl(config);
|
|
335
|
-
if (url !== null) {
|
|
336
|
-
connectToDatabase(url);
|
|
337
|
-
} else {
|
|
338
|
-
console.error('Database URL not configured');
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
#### After (fp-ts Option)
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
import * as O from 'fp-ts/Option';
|
|
346
|
-
import { pipe } from 'fp-ts/function';
|
|
347
|
-
|
|
348
|
-
const getDatabaseUrl = (config: Config): O.Option<string> =>
|
|
349
|
-
pipe(
|
|
350
|
-
O.fromNullable(config.database),
|
|
351
|
-
O.flatMap((db) =>
|
|
352
|
-
pipe(
|
|
353
|
-
O.fromNullable(db.host),
|
|
354
|
-
O.map((host) => {
|
|
355
|
-
const port = db.port ?? 5432;
|
|
356
|
-
const auth = pipe(
|
|
357
|
-
O.fromNullable(db.credentials),
|
|
358
|
-
O.flatMap((creds) =>
|
|
359
|
-
pipe(
|
|
360
|
-
O.Do,
|
|
361
|
-
O.bind('username', () => O.fromNullable(creds.username)),
|
|
362
|
-
O.bind('password', () => O.fromNullable(creds.password)),
|
|
363
|
-
O.map(({ username, password }) => `${username}:${password}@`)
|
|
364
|
-
)
|
|
365
|
-
),
|
|
366
|
-
O.getOrElse(() => '')
|
|
367
|
-
);
|
|
368
|
-
return `postgres://${auth}${host}:${port}`;
|
|
369
|
-
})
|
|
370
|
-
)
|
|
371
|
-
)
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
// Usage is explicit about the optional nature
|
|
375
|
-
pipe(
|
|
376
|
-
getDatabaseUrl(config),
|
|
377
|
-
O.match(
|
|
378
|
-
() => console.error('Database URL not configured'),
|
|
379
|
-
(url) => connectToDatabase(url)
|
|
380
|
-
)
|
|
381
|
-
);
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
### Pattern: Array find operations
|
|
385
|
-
|
|
386
|
-
#### Before (Imperative)
|
|
387
|
-
|
|
388
|
-
```typescript
|
|
389
|
-
interface User {
|
|
390
|
-
id: string;
|
|
391
|
-
name: string;
|
|
392
|
-
email: string;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function findUserById(users: User[], id: string): User | undefined {
|
|
396
|
-
return users.find((u) => u.id === id);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function getUserEmail(users: User[], id: string): string | null {
|
|
400
|
-
const user = findUserById(users, id);
|
|
401
|
-
if (!user) {
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
return user.email;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Chained lookups get messy
|
|
408
|
-
function getManagerEmail(users: User[], employee: { managerId?: string }): string | null {
|
|
409
|
-
if (!employee.managerId) {
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
const manager = findUserById(users, employee.managerId);
|
|
413
|
-
if (!manager) {
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
return manager.email;
|
|
417
|
-
}
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
#### After (fp-ts Option)
|
|
421
|
-
|
|
422
|
-
```typescript
|
|
423
|
-
import * as O from 'fp-ts/Option';
|
|
424
|
-
import * as A from 'fp-ts/Array';
|
|
425
|
-
import { pipe } from 'fp-ts/function';
|
|
426
|
-
|
|
427
|
-
const findUserById = (users: User[], id: string): O.Option<User> =>
|
|
428
|
-
A.findFirst<User>((u) => u.id === id)(users);
|
|
429
|
-
|
|
430
|
-
const getUserEmail = (users: User[], id: string): O.Option<string> =>
|
|
431
|
-
pipe(
|
|
432
|
-
findUserById(users, id),
|
|
433
|
-
O.map((user) => user.email)
|
|
434
|
-
);
|
|
435
|
-
|
|
436
|
-
const getManagerEmail = (
|
|
437
|
-
users: User[],
|
|
438
|
-
employee: { managerId?: string }
|
|
439
|
-
): O.Option<string> =>
|
|
440
|
-
pipe(
|
|
441
|
-
O.fromNullable(employee.managerId),
|
|
442
|
-
O.flatMap((managerId) => findUserById(users, managerId)),
|
|
443
|
-
O.map((manager) => manager.email)
|
|
444
|
-
);
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Step-by-Step Refactoring Guide
|
|
448
|
-
|
|
449
|
-
1. **Identify nullable values**: Find all `T | null`, `T | undefined`, or optional properties
|
|
450
|
-
2. **Wrap with fromNullable**: Convert nullable values to Option at system boundaries
|
|
451
|
-
3. **Change return types**: From `T | null` to `Option<T>`
|
|
452
|
-
4. **Replace null checks**: Use `O.map`, `O.flatMap`, `O.filter` instead of if statements
|
|
453
|
-
5. **Handle at boundaries**: Use `O.getOrElse`, `O.match`, or `O.toNullable` when interfacing with non-fp code
|
|
454
|
-
|
|
455
|
-
### Converting Between Option and Either
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
import * as O from 'fp-ts/Option';
|
|
459
|
-
import * as E from 'fp-ts/Either';
|
|
460
|
-
import { pipe } from 'fp-ts/function';
|
|
461
|
-
|
|
462
|
-
// Option to Either: provide error for None case
|
|
463
|
-
const optionToEither = <E, A>(onNone: () => E) => (
|
|
464
|
-
option: O.Option<A>
|
|
465
|
-
): E.Either<E, A> =>
|
|
466
|
-
pipe(
|
|
467
|
-
option,
|
|
468
|
-
E.fromOption(onNone)
|
|
469
|
-
);
|
|
470
|
-
|
|
471
|
-
// Example
|
|
472
|
-
const findUser = (id: string): O.Option<User> => /* ... */;
|
|
473
|
-
|
|
474
|
-
const getUser = (id: string): E.Either<Error, User> =>
|
|
475
|
-
pipe(
|
|
476
|
-
findUser(id),
|
|
477
|
-
E.fromOption(() => new Error(`User ${id} not found`))
|
|
478
|
-
);
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
---
|
|
482
|
-
|
|
483
|
-
## 3. Converting callbacks to Task
|
|
484
|
-
|
|
485
|
-
### The Problem with Callbacks
|
|
486
|
-
|
|
487
|
-
- Callback hell makes code hard to read
|
|
488
|
-
- Error handling is inconsistent
|
|
489
|
-
- Difficult to compose and sequence
|
|
490
|
-
- No standard way to handle async operations
|
|
491
|
-
|
|
492
|
-
### Pattern: Node-style callbacks to Task
|
|
493
|
-
|
|
494
|
-
#### Before (Imperative)
|
|
495
|
-
|
|
496
|
-
```typescript
|
|
497
|
-
import * as fs from 'fs';
|
|
498
|
-
|
|
499
|
-
function readFileCallback(
|
|
500
|
-
path: string,
|
|
501
|
-
callback: (error: Error | null, data: string | null) => void
|
|
502
|
-
): void {
|
|
503
|
-
fs.readFile(path, 'utf-8', (err, data) => {
|
|
504
|
-
if (err) {
|
|
505
|
-
callback(err, null);
|
|
506
|
-
} else {
|
|
507
|
-
callback(null, data);
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function processFile(
|
|
513
|
-
inputPath: string,
|
|
514
|
-
outputPath: string,
|
|
515
|
-
callback: (error: Error | null) => void
|
|
516
|
-
): void {
|
|
517
|
-
readFileCallback(inputPath, (err, data) => {
|
|
518
|
-
if (err) {
|
|
519
|
-
callback(err);
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
const processed = data!.toUpperCase();
|
|
523
|
-
fs.writeFile(outputPath, processed, (writeErr) => {
|
|
524
|
-
if (writeErr) {
|
|
525
|
-
callback(writeErr);
|
|
526
|
-
} else {
|
|
527
|
-
callback(null);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Callback hell
|
|
534
|
-
function processMultipleFiles(
|
|
535
|
-
files: Array<{ input: string; output: string }>,
|
|
536
|
-
callback: (error: Error | null) => void
|
|
537
|
-
): void {
|
|
538
|
-
let completed = 0;
|
|
539
|
-
let hasError = false;
|
|
540
|
-
|
|
541
|
-
files.forEach(({ input, output }) => {
|
|
542
|
-
if (hasError) return;
|
|
543
|
-
processFile(input, output, (err) => {
|
|
544
|
-
if (hasError) return;
|
|
545
|
-
if (err) {
|
|
546
|
-
hasError = true;
|
|
547
|
-
callback(err);
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
completed++;
|
|
551
|
-
if (completed === files.length) {
|
|
552
|
-
callback(null);
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
#### After (fp-ts Task/TaskEither)
|
|
560
|
-
|
|
561
|
-
```typescript
|
|
562
|
-
import * as fs from 'fs/promises';
|
|
563
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
564
|
-
import * as A from 'fp-ts/Array';
|
|
565
|
-
import { pipe } from 'fp-ts/function';
|
|
566
|
-
|
|
567
|
-
// Wrap fs.promises in TaskEither
|
|
568
|
-
const readFile = (path: string): TE.TaskEither<Error, string> =>
|
|
569
|
-
TE.tryCatch(
|
|
570
|
-
() => fs.readFile(path, 'utf-8'),
|
|
571
|
-
(e) => (e instanceof Error ? e : new Error(String(e)))
|
|
572
|
-
);
|
|
573
|
-
|
|
574
|
-
const writeFile = (path: string, data: string): TE.TaskEither<Error, void> =>
|
|
575
|
-
TE.tryCatch(
|
|
576
|
-
() => fs.writeFile(path, data),
|
|
577
|
-
(e) => (e instanceof Error ? e : new Error(String(e)))
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
// Clean composition
|
|
581
|
-
const processFile = (
|
|
582
|
-
inputPath: string,
|
|
583
|
-
outputPath: string
|
|
584
|
-
): TE.TaskEither<Error, void> =>
|
|
585
|
-
pipe(
|
|
586
|
-
readFile(inputPath),
|
|
587
|
-
TE.map((data) => data.toUpperCase()),
|
|
588
|
-
TE.flatMap((processed) => writeFile(outputPath, processed))
|
|
589
|
-
);
|
|
590
|
-
|
|
591
|
-
// Process multiple files in parallel or sequence
|
|
592
|
-
const processMultipleFilesParallel = (
|
|
593
|
-
files: Array<{ input: string; output: string }>
|
|
594
|
-
): TE.TaskEither<Error, void[]> =>
|
|
595
|
-
pipe(
|
|
596
|
-
files,
|
|
597
|
-
A.traverse(TE.ApplicativePar)(({ input, output }) =>
|
|
598
|
-
processFile(input, output)
|
|
599
|
-
)
|
|
600
|
-
);
|
|
601
|
-
|
|
602
|
-
const processMultipleFilesSequential = (
|
|
603
|
-
files: Array<{ input: string; output: string }>
|
|
604
|
-
): TE.TaskEither<Error, void[]> =>
|
|
605
|
-
pipe(
|
|
606
|
-
files,
|
|
607
|
-
A.traverse(TE.ApplicativeSeq)(({ input, output }) =>
|
|
608
|
-
processFile(input, output)
|
|
609
|
-
)
|
|
610
|
-
);
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### Pattern: Converting callback-based APIs
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
617
|
-
|
|
618
|
-
// Generic callback-to-TaskEither converter
|
|
619
|
-
const fromCallback = <A>(
|
|
620
|
-
f: (callback: (error: Error | null, result: A | null) => void) => void
|
|
621
|
-
): TE.TaskEither<Error, A> =>
|
|
622
|
-
() =>
|
|
623
|
-
new Promise((resolve) => {
|
|
624
|
-
f((error, result) => {
|
|
625
|
-
if (error) {
|
|
626
|
-
resolve({ _tag: 'Left', left: error });
|
|
627
|
-
} else {
|
|
628
|
-
resolve({ _tag: 'Right', right: result as A });
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
// Usage
|
|
634
|
-
const readFileLegacy = (path: string): TE.TaskEither<Error, string> =>
|
|
635
|
-
fromCallback((cb) => fs.readFile(path, 'utf-8', cb));
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
---
|
|
639
|
-
|
|
640
|
-
## 4. Converting class-based DI to Reader
|
|
641
|
-
|
|
642
|
-
### The Problem with Class-based DI
|
|
643
|
-
|
|
644
|
-
- Tight coupling between classes and their dependencies
|
|
645
|
-
- Testing requires mocking entire class hierarchies
|
|
646
|
-
- Dependency injection containers add runtime complexity
|
|
647
|
-
- Hard to trace data flow through the application
|
|
648
|
-
|
|
649
|
-
### Pattern: Service classes to Reader
|
|
650
|
-
|
|
651
|
-
#### Before (Imperative with Classes)
|
|
652
|
-
|
|
653
|
-
```typescript
|
|
654
|
-
// Traditional class-based approach
|
|
655
|
-
interface Logger {
|
|
656
|
-
log(message: string): void;
|
|
657
|
-
error(message: string): void;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
interface UserRepository {
|
|
661
|
-
findById(id: string): Promise<User | null>;
|
|
662
|
-
save(user: User): Promise<void>;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
interface EmailService {
|
|
666
|
-
send(to: string, subject: string, body: string): Promise<void>;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
class UserService {
|
|
670
|
-
constructor(
|
|
671
|
-
private readonly logger: Logger,
|
|
672
|
-
private readonly userRepo: UserRepository,
|
|
673
|
-
private readonly emailService: EmailService
|
|
674
|
-
) {}
|
|
675
|
-
|
|
676
|
-
async updateEmail(userId: string, newEmail: string): Promise<void> {
|
|
677
|
-
this.logger.log(`Updating email for user ${userId}`);
|
|
678
|
-
|
|
679
|
-
const user = await this.userRepo.findById(userId);
|
|
680
|
-
if (!user) {
|
|
681
|
-
this.logger.error(`User ${userId} not found`);
|
|
682
|
-
throw new Error(`User ${userId} not found`);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
const oldEmail = user.email;
|
|
686
|
-
user.email = newEmail;
|
|
687
|
-
|
|
688
|
-
await this.userRepo.save(user);
|
|
689
|
-
|
|
690
|
-
await this.emailService.send(
|
|
691
|
-
oldEmail,
|
|
692
|
-
'Email Changed',
|
|
693
|
-
`Your email has been changed to ${newEmail}`
|
|
694
|
-
);
|
|
695
|
-
|
|
696
|
-
this.logger.log(`Email updated for user ${userId}`);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// Manual DI setup
|
|
701
|
-
const logger = new ConsoleLogger();
|
|
702
|
-
const userRepo = new PostgresUserRepository(dbConnection);
|
|
703
|
-
const emailService = new SmtpEmailService(smtpConfig);
|
|
704
|
-
const userService = new UserService(logger, userRepo, emailService);
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
#### After (fp-ts Reader)
|
|
708
|
-
|
|
709
|
-
```typescript
|
|
710
|
-
import * as R from 'fp-ts/Reader';
|
|
711
|
-
import * as RTE from 'fp-ts/ReaderTaskEither';
|
|
712
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
713
|
-
import { pipe } from 'fp-ts/function';
|
|
714
|
-
|
|
715
|
-
// Define the environment/dependencies as an interface
|
|
716
|
-
interface AppEnv {
|
|
717
|
-
logger: {
|
|
718
|
-
log: (message: string) => void;
|
|
719
|
-
error: (message: string) => void;
|
|
720
|
-
};
|
|
721
|
-
userRepo: {
|
|
722
|
-
findById: (id: string) => TE.TaskEither<Error, User | null>;
|
|
723
|
-
save: (user: User) => TE.TaskEither<Error, void>;
|
|
724
|
-
};
|
|
725
|
-
emailService: {
|
|
726
|
-
send: (to: string, subject: string, body: string) => TE.TaskEither<Error, void>;
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Helper to access environment
|
|
731
|
-
const ask = RTE.ask<AppEnv, Error>();
|
|
732
|
-
|
|
733
|
-
// Service functions using ReaderTaskEither
|
|
734
|
-
const logInfo = (message: string): RTE.ReaderTaskEither<AppEnv, Error, void> =>
|
|
735
|
-
pipe(
|
|
736
|
-
ask,
|
|
737
|
-
RTE.map((env) => env.logger.log(message))
|
|
738
|
-
);
|
|
739
|
-
|
|
740
|
-
const logError = (message: string): RTE.ReaderTaskEither<AppEnv, Error, void> =>
|
|
741
|
-
pipe(
|
|
742
|
-
ask,
|
|
743
|
-
RTE.map((env) => env.logger.error(message))
|
|
744
|
-
);
|
|
745
|
-
|
|
746
|
-
const findUser = (id: string): RTE.ReaderTaskEither<AppEnv, Error, User | null> =>
|
|
747
|
-
pipe(
|
|
748
|
-
ask,
|
|
749
|
-
RTE.flatMapTaskEither((env) => env.userRepo.findById(id))
|
|
750
|
-
);
|
|
751
|
-
|
|
752
|
-
const saveUser = (user: User): RTE.ReaderTaskEither<AppEnv, Error, void> =>
|
|
753
|
-
pipe(
|
|
754
|
-
ask,
|
|
755
|
-
RTE.flatMapTaskEither((env) => env.userRepo.save(user))
|
|
756
|
-
);
|
|
757
|
-
|
|
758
|
-
const sendEmail = (
|
|
759
|
-
to: string,
|
|
760
|
-
subject: string,
|
|
761
|
-
body: string
|
|
762
|
-
): RTE.ReaderTaskEither<AppEnv, Error, void> =>
|
|
763
|
-
pipe(
|
|
764
|
-
ask,
|
|
765
|
-
RTE.flatMapTaskEither((env) => env.emailService.send(to, subject, body))
|
|
766
|
-
);
|
|
767
|
-
|
|
768
|
-
// The updateEmail function using Reader composition
|
|
769
|
-
const updateEmail = (
|
|
770
|
-
userId: string,
|
|
771
|
-
newEmail: string
|
|
772
|
-
): RTE.ReaderTaskEither<AppEnv, Error, void> =>
|
|
773
|
-
pipe(
|
|
774
|
-
logInfo(`Updating email for user ${userId}`),
|
|
775
|
-
RTE.flatMap(() => findUser(userId)),
|
|
776
|
-
RTE.flatMap((user) => {
|
|
777
|
-
if (!user) {
|
|
778
|
-
return pipe(
|
|
779
|
-
logError(`User ${userId} not found`),
|
|
780
|
-
RTE.flatMap(() => RTE.left(new Error(`User ${userId} not found`)))
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
const oldEmail = user.email;
|
|
784
|
-
const updatedUser = { ...user, email: newEmail };
|
|
785
|
-
|
|
786
|
-
return pipe(
|
|
787
|
-
saveUser(updatedUser),
|
|
788
|
-
RTE.flatMap(() =>
|
|
789
|
-
sendEmail(
|
|
790
|
-
oldEmail,
|
|
791
|
-
'Email Changed',
|
|
792
|
-
`Your email has been changed to ${newEmail}`
|
|
793
|
-
)
|
|
794
|
-
),
|
|
795
|
-
RTE.flatMap(() => logInfo(`Email updated for user ${userId}`))
|
|
796
|
-
);
|
|
797
|
-
})
|
|
798
|
-
);
|
|
799
|
-
|
|
800
|
-
// Build the environment
|
|
801
|
-
const createAppEnv = (): AppEnv => ({
|
|
802
|
-
logger: {
|
|
803
|
-
log: (msg) => console.log(`[INFO] ${msg}`),
|
|
804
|
-
error: (msg) => console.error(`[ERROR] ${msg}`),
|
|
805
|
-
},
|
|
806
|
-
userRepo: {
|
|
807
|
-
findById: (id) => TE.tryCatch(
|
|
808
|
-
() => postgresClient.query('SELECT * FROM users WHERE id = $1', [id]),
|
|
809
|
-
(e) => new Error(String(e))
|
|
810
|
-
),
|
|
811
|
-
save: (user) => TE.tryCatch(
|
|
812
|
-
() => postgresClient.query('UPDATE users SET email = $1 WHERE id = $2', [user.email, user.id]),
|
|
813
|
-
(e) => new Error(String(e))
|
|
814
|
-
),
|
|
815
|
-
},
|
|
816
|
-
emailService: {
|
|
817
|
-
send: (to, subject, body) => TE.tryCatch(
|
|
818
|
-
() => smtpClient.send({ to, subject, body }),
|
|
819
|
-
(e) => new Error(String(e))
|
|
820
|
-
),
|
|
821
|
-
},
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
// Run the program
|
|
825
|
-
const main = async () => {
|
|
826
|
-
const env = createAppEnv();
|
|
827
|
-
const result = await updateEmail('user-123', 'new@email.com')(env)();
|
|
828
|
-
|
|
829
|
-
pipe(
|
|
830
|
-
result,
|
|
831
|
-
E.match(
|
|
832
|
-
(error) => console.error('Failed:', error),
|
|
833
|
-
() => console.log('Success!')
|
|
834
|
-
)
|
|
835
|
-
);
|
|
836
|
-
};
|
|
837
|
-
```
|
|
838
|
-
|
|
839
|
-
### Testing with Reader
|
|
840
|
-
|
|
841
|
-
```typescript
|
|
842
|
-
// Easy to test with mock environment
|
|
843
|
-
const createTestEnv = (): AppEnv => {
|
|
844
|
-
const logs: string[] = [];
|
|
845
|
-
const savedUsers: User[] = [];
|
|
846
|
-
const sentEmails: Array<{ to: string; subject: string; body: string }> = [];
|
|
847
|
-
|
|
848
|
-
return {
|
|
849
|
-
logger: {
|
|
850
|
-
log: (msg) => logs.push(`[INFO] ${msg}`),
|
|
851
|
-
error: (msg) => logs.push(`[ERROR] ${msg}`),
|
|
852
|
-
},
|
|
853
|
-
userRepo: {
|
|
854
|
-
findById: (id) =>
|
|
855
|
-
TE.right(id === 'existing-user' ? { id, email: 'old@email.com', name: 'Test' } : null),
|
|
856
|
-
save: (user) => {
|
|
857
|
-
savedUsers.push(user);
|
|
858
|
-
return TE.right(undefined);
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
emailService: {
|
|
862
|
-
send: (to, subject, body) => {
|
|
863
|
-
sentEmails.push({ to, subject, body });
|
|
864
|
-
return TE.right(undefined);
|
|
865
|
-
},
|
|
866
|
-
},
|
|
867
|
-
};
|
|
868
|
-
};
|
|
869
|
-
|
|
870
|
-
// Test
|
|
871
|
-
describe('updateEmail', () => {
|
|
872
|
-
it('should update email and send notification', async () => {
|
|
873
|
-
const env = createTestEnv();
|
|
874
|
-
const result = await updateEmail('existing-user', 'new@email.com')(env)();
|
|
875
|
-
|
|
876
|
-
expect(E.isRight(result)).toBe(true);
|
|
877
|
-
// Assert on captured side effects
|
|
878
|
-
});
|
|
879
|
-
});
|
|
880
|
-
```
|
|
881
|
-
|
|
882
|
-
---
|
|
883
|
-
|
|
884
|
-
## 5. Converting imperative loops to functional operations
|
|
885
|
-
|
|
886
|
-
### Pattern: for loops to map/filter/reduce
|
|
887
|
-
|
|
888
|
-
#### Before (Imperative)
|
|
889
|
-
|
|
890
|
-
```typescript
|
|
891
|
-
interface Product {
|
|
892
|
-
id: string;
|
|
893
|
-
name: string;
|
|
894
|
-
price: number;
|
|
895
|
-
category: string;
|
|
896
|
-
inStock: boolean;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
function processProducts(products: Product[]): {
|
|
900
|
-
totalValue: number;
|
|
901
|
-
categoryCounts: Record<string, number>;
|
|
902
|
-
expensiveProducts: string[];
|
|
903
|
-
} {
|
|
904
|
-
let totalValue = 0;
|
|
905
|
-
const categoryCounts: Record<string, number> = {};
|
|
906
|
-
const expensiveProducts: string[] = [];
|
|
907
|
-
|
|
908
|
-
for (let i = 0; i < products.length; i++) {
|
|
909
|
-
const product = products[i];
|
|
910
|
-
|
|
911
|
-
// Skip out of stock
|
|
912
|
-
if (!product.inStock) {
|
|
913
|
-
continue;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Sum total value
|
|
917
|
-
totalValue += product.price;
|
|
918
|
-
|
|
919
|
-
// Count categories
|
|
920
|
-
if (categoryCounts[product.category] === undefined) {
|
|
921
|
-
categoryCounts[product.category] = 0;
|
|
922
|
-
}
|
|
923
|
-
categoryCounts[product.category]++;
|
|
924
|
-
|
|
925
|
-
// Collect expensive products
|
|
926
|
-
if (product.price > 100) {
|
|
927
|
-
expensiveProducts.push(product.name);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
return { totalValue, categoryCounts, expensiveProducts };
|
|
932
|
-
}
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
#### After (fp-ts functional operations)
|
|
936
|
-
|
|
937
|
-
```typescript
|
|
938
|
-
import * as A from 'fp-ts/Array';
|
|
939
|
-
import * as R from 'fp-ts/Record';
|
|
940
|
-
import { pipe } from 'fp-ts/function';
|
|
941
|
-
import * as N from 'fp-ts/number';
|
|
942
|
-
import * as Monoid from 'fp-ts/Monoid';
|
|
943
|
-
|
|
944
|
-
const processProducts = (products: Product[]) => {
|
|
945
|
-
const inStockProducts = pipe(
|
|
946
|
-
products,
|
|
947
|
-
A.filter((p) => p.inStock)
|
|
948
|
-
);
|
|
949
|
-
|
|
950
|
-
const totalValue = pipe(
|
|
951
|
-
inStockProducts,
|
|
952
|
-
A.map((p) => p.price),
|
|
953
|
-
A.reduce(0, (acc, price) => acc + price)
|
|
954
|
-
);
|
|
955
|
-
|
|
956
|
-
const categoryCounts = pipe(
|
|
957
|
-
inStockProducts,
|
|
958
|
-
A.reduce({} as Record<string, number>, (acc, product) => ({
|
|
959
|
-
...acc,
|
|
960
|
-
[product.category]: (acc[product.category] ?? 0) + 1,
|
|
961
|
-
}))
|
|
962
|
-
);
|
|
963
|
-
|
|
964
|
-
const expensiveProducts = pipe(
|
|
965
|
-
inStockProducts,
|
|
966
|
-
A.filter((p) => p.price > 100),
|
|
967
|
-
A.map((p) => p.name)
|
|
968
|
-
);
|
|
969
|
-
|
|
970
|
-
return { totalValue, categoryCounts, expensiveProducts };
|
|
971
|
-
};
|
|
972
|
-
|
|
973
|
-
// Or using a single pass with foldMap for efficiency
|
|
974
|
-
import { Monoid as M } from 'fp-ts/Monoid';
|
|
975
|
-
|
|
976
|
-
interface ProductStats {
|
|
977
|
-
totalValue: number;
|
|
978
|
-
categoryCounts: Record<string, number>;
|
|
979
|
-
expensiveProducts: string[];
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
const productStatsMonoid: M<ProductStats> = {
|
|
983
|
-
empty: { totalValue: 0, categoryCounts: {}, expensiveProducts: [] },
|
|
984
|
-
concat: (a, b) => ({
|
|
985
|
-
totalValue: a.totalValue + b.totalValue,
|
|
986
|
-
categoryCounts: pipe(
|
|
987
|
-
a.categoryCounts,
|
|
988
|
-
R.union({ concat: (x, y) => x + y })(b.categoryCounts)
|
|
989
|
-
),
|
|
990
|
-
expensiveProducts: [...a.expensiveProducts, ...b.expensiveProducts],
|
|
991
|
-
}),
|
|
992
|
-
};
|
|
993
|
-
|
|
994
|
-
const processProductsSinglePass = (products: Product[]): ProductStats =>
|
|
995
|
-
pipe(
|
|
996
|
-
products,
|
|
997
|
-
A.filter((p) => p.inStock),
|
|
998
|
-
A.foldMap(productStatsMonoid)((product) => ({
|
|
999
|
-
totalValue: product.price,
|
|
1000
|
-
categoryCounts: { [product.category]: 1 },
|
|
1001
|
-
expensiveProducts: product.price > 100 ? [product.name] : [],
|
|
1002
|
-
}))
|
|
1003
|
-
);
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
### Pattern: Nested loops to flatMap
|
|
1007
|
-
|
|
1008
|
-
#### Before (Imperative)
|
|
1009
|
-
|
|
1010
|
-
```typescript
|
|
1011
|
-
interface Order {
|
|
1012
|
-
id: string;
|
|
1013
|
-
items: OrderItem[];
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
interface OrderItem {
|
|
1017
|
-
productId: string;
|
|
1018
|
-
quantity: number;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
function getAllProductIds(orders: Order[]): string[] {
|
|
1022
|
-
const productIds: string[] = [];
|
|
1023
|
-
|
|
1024
|
-
for (const order of orders) {
|
|
1025
|
-
for (const item of order.items) {
|
|
1026
|
-
if (!productIds.includes(item.productId)) {
|
|
1027
|
-
productIds.push(item.productId);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
return productIds;
|
|
1033
|
-
}
|
|
1034
|
-
```
|
|
1035
|
-
|
|
1036
|
-
#### After (fp-ts)
|
|
1037
|
-
|
|
1038
|
-
```typescript
|
|
1039
|
-
import * as A from 'fp-ts/Array';
|
|
1040
|
-
import { pipe } from 'fp-ts/function';
|
|
1041
|
-
import * as S from 'fp-ts/Set';
|
|
1042
|
-
import * as Str from 'fp-ts/string';
|
|
1043
|
-
|
|
1044
|
-
const getAllProductIds = (orders: Order[]): string[] =>
|
|
1045
|
-
pipe(
|
|
1046
|
-
orders,
|
|
1047
|
-
A.flatMap((order) => order.items),
|
|
1048
|
-
A.map((item) => item.productId),
|
|
1049
|
-
A.uniq(Str.Eq)
|
|
1050
|
-
);
|
|
1051
|
-
|
|
1052
|
-
// Or using Set for better performance with large datasets
|
|
1053
|
-
const getAllProductIdsSet = (orders: Order[]): Set<string> =>
|
|
1054
|
-
pipe(
|
|
1055
|
-
orders,
|
|
1056
|
-
A.flatMap((order) => order.items),
|
|
1057
|
-
A.map((item) => item.productId),
|
|
1058
|
-
(ids) => new Set(ids)
|
|
1059
|
-
);
|
|
1060
|
-
```
|
|
1061
|
-
|
|
1062
|
-
### Pattern: while loops to recursion/unfold
|
|
1063
|
-
|
|
1064
|
-
#### Before (Imperative)
|
|
1065
|
-
|
|
1066
|
-
```typescript
|
|
1067
|
-
function paginate<T>(
|
|
1068
|
-
fetchPage: (cursor: string | null) => Promise<{ items: T[]; nextCursor: string | null }>
|
|
1069
|
-
): Promise<T[]> {
|
|
1070
|
-
const allItems: T[] = [];
|
|
1071
|
-
let cursor: string | null = null;
|
|
1072
|
-
|
|
1073
|
-
while (true) {
|
|
1074
|
-
const { items, nextCursor } = await fetchPage(cursor);
|
|
1075
|
-
allItems.push(...items);
|
|
1076
|
-
|
|
1077
|
-
if (nextCursor === null) {
|
|
1078
|
-
break;
|
|
1079
|
-
}
|
|
1080
|
-
cursor = nextCursor;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
return allItems;
|
|
1084
|
-
}
|
|
1085
|
-
```
|
|
1086
|
-
|
|
1087
|
-
#### After (fp-ts)
|
|
1088
|
-
|
|
1089
|
-
```typescript
|
|
1090
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
1091
|
-
import * as A from 'fp-ts/Array';
|
|
1092
|
-
import { pipe } from 'fp-ts/function';
|
|
1093
|
-
|
|
1094
|
-
interface Page<T> {
|
|
1095
|
-
items: T[];
|
|
1096
|
-
nextCursor: string | null;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
const paginate = <T>(
|
|
1100
|
-
fetchPage: (cursor: string | null) => TE.TaskEither<Error, Page<T>>
|
|
1101
|
-
): TE.TaskEither<Error, T[]> => {
|
|
1102
|
-
const go = (
|
|
1103
|
-
cursor: string | null,
|
|
1104
|
-
accumulated: T[]
|
|
1105
|
-
): TE.TaskEither<Error, T[]> =>
|
|
1106
|
-
pipe(
|
|
1107
|
-
fetchPage(cursor),
|
|
1108
|
-
TE.flatMap(({ items, nextCursor }) => {
|
|
1109
|
-
const newAccumulated = [...accumulated, ...items];
|
|
1110
|
-
return nextCursor === null
|
|
1111
|
-
? TE.right(newAccumulated)
|
|
1112
|
-
: go(nextCursor, newAccumulated);
|
|
1113
|
-
})
|
|
1114
|
-
);
|
|
1115
|
-
|
|
1116
|
-
return go(null, []);
|
|
1117
|
-
};
|
|
1118
|
-
|
|
1119
|
-
// Using unfold for generating sequences
|
|
1120
|
-
import * as RA from 'fp-ts/ReadonlyArray';
|
|
1121
|
-
|
|
1122
|
-
const range = (start: number, end: number): readonly number[] =>
|
|
1123
|
-
RA.unfold(start, (n) => (n <= end ? O.some([n, n + 1]) : O.none));
|
|
1124
|
-
```
|
|
1125
|
-
|
|
1126
|
-
---
|
|
1127
|
-
|
|
1128
|
-
## 6. Migrating Promise chains to TaskEither
|
|
1129
|
-
|
|
1130
|
-
### Pattern: Promise.then chains to pipe
|
|
1131
|
-
|
|
1132
|
-
#### Before (Imperative)
|
|
1133
|
-
|
|
1134
|
-
```typescript
|
|
1135
|
-
function fetchUserData(userId: string): Promise<UserProfile> {
|
|
1136
|
-
return fetch(`/api/users/${userId}`)
|
|
1137
|
-
.then((response) => {
|
|
1138
|
-
if (!response.ok) {
|
|
1139
|
-
throw new Error(`HTTP ${response.status}`);
|
|
1140
|
-
}
|
|
1141
|
-
return response.json();
|
|
1142
|
-
})
|
|
1143
|
-
.then((data) => validateUserData(data))
|
|
1144
|
-
.then((validData) => enrichUserProfile(validData))
|
|
1145
|
-
.catch((error) => {
|
|
1146
|
-
console.error('Failed to fetch user data:', error);
|
|
1147
|
-
throw error;
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
// Chained promises with conditionals
|
|
1152
|
-
function processOrder(orderId: string): Promise<OrderResult> {
|
|
1153
|
-
return getOrder(orderId)
|
|
1154
|
-
.then((order) => {
|
|
1155
|
-
if (order.status === 'cancelled') {
|
|
1156
|
-
throw new Error('Order is cancelled');
|
|
1157
|
-
}
|
|
1158
|
-
return order;
|
|
1159
|
-
})
|
|
1160
|
-
.then((order) => validateInventory(order))
|
|
1161
|
-
.then((validOrder) => processPayment(validOrder))
|
|
1162
|
-
.then((paidOrder) => shipOrder(paidOrder))
|
|
1163
|
-
.catch((error) => {
|
|
1164
|
-
logError(error);
|
|
1165
|
-
return { success: false, error: error.message };
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
```
|
|
1169
|
-
|
|
1170
|
-
#### After (fp-ts TaskEither)
|
|
1171
|
-
|
|
1172
|
-
```typescript
|
|
1173
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
1174
|
-
import * as E from 'fp-ts/Either';
|
|
1175
|
-
import { pipe } from 'fp-ts/function';
|
|
1176
|
-
|
|
1177
|
-
const fetchUserData = (userId: string): TE.TaskEither<Error, UserProfile> =>
|
|
1178
|
-
pipe(
|
|
1179
|
-
TE.tryCatch(
|
|
1180
|
-
() => fetch(`/api/users/${userId}`),
|
|
1181
|
-
(e) => new Error(`Network error: ${e}`)
|
|
1182
|
-
),
|
|
1183
|
-
TE.flatMap((response) =>
|
|
1184
|
-
response.ok
|
|
1185
|
-
? TE.tryCatch(
|
|
1186
|
-
() => response.json(),
|
|
1187
|
-
(e) => new Error(`Parse error: ${e}`)
|
|
1188
|
-
)
|
|
1189
|
-
: TE.left(new Error(`HTTP ${response.status}`))
|
|
1190
|
-
),
|
|
1191
|
-
TE.flatMap((data) => TE.fromEither(validateUserData(data))),
|
|
1192
|
-
TE.flatMap((validData) => enrichUserProfile(validData))
|
|
1193
|
-
);
|
|
1194
|
-
|
|
1195
|
-
// Conditionals are explicit
|
|
1196
|
-
const processOrder = (orderId: string): TE.TaskEither<Error, OrderResult> =>
|
|
1197
|
-
pipe(
|
|
1198
|
-
getOrder(orderId),
|
|
1199
|
-
TE.filterOrElse(
|
|
1200
|
-
(order) => order.status !== 'cancelled',
|
|
1201
|
-
() => new Error('Order is cancelled')
|
|
1202
|
-
),
|
|
1203
|
-
TE.flatMap(validateInventory),
|
|
1204
|
-
TE.flatMap(processPayment),
|
|
1205
|
-
TE.flatMap(shipOrder),
|
|
1206
|
-
TE.map((shipped) => ({ success: true, order: shipped })),
|
|
1207
|
-
TE.orElse((error) =>
|
|
1208
|
-
pipe(
|
|
1209
|
-
TE.fromIO(() => logError(error)),
|
|
1210
|
-
TE.map(() => ({ success: false, error: error.message }))
|
|
1211
|
-
)
|
|
1212
|
-
)
|
|
1213
|
-
);
|
|
1214
|
-
```
|
|
1215
|
-
|
|
1216
|
-
### Pattern: Promise.all to traverse
|
|
1217
|
-
|
|
1218
|
-
#### Before (Imperative)
|
|
1219
|
-
|
|
1220
|
-
```typescript
|
|
1221
|
-
async function fetchAllUsers(ids: string[]): Promise<User[]> {
|
|
1222
|
-
const promises = ids.map((id) => fetchUser(id));
|
|
1223
|
-
return Promise.all(promises);
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
// With error handling for individual items
|
|
1227
|
-
async function fetchUsersWithFallback(ids: string[]): Promise<Array<User | null>> {
|
|
1228
|
-
const promises = ids.map(async (id) => {
|
|
1229
|
-
try {
|
|
1230
|
-
return await fetchUser(id);
|
|
1231
|
-
} catch {
|
|
1232
|
-
return null;
|
|
1233
|
-
}
|
|
1234
|
-
});
|
|
1235
|
-
return Promise.all(promises);
|
|
1236
|
-
}
|
|
1237
|
-
```
|
|
1238
|
-
|
|
1239
|
-
#### After (fp-ts)
|
|
1240
|
-
|
|
1241
|
-
```typescript
|
|
1242
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
1243
|
-
import * as A from 'fp-ts/Array';
|
|
1244
|
-
import * as T from 'fp-ts/Task';
|
|
1245
|
-
import { pipe } from 'fp-ts/function';
|
|
1246
|
-
|
|
1247
|
-
// Parallel execution - fails fast on first error
|
|
1248
|
-
const fetchAllUsers = (ids: string[]): TE.TaskEither<Error, User[]> =>
|
|
1249
|
-
pipe(
|
|
1250
|
-
ids,
|
|
1251
|
-
A.traverse(TE.ApplicativePar)(fetchUser)
|
|
1252
|
-
);
|
|
1253
|
-
|
|
1254
|
-
// Sequential execution
|
|
1255
|
-
const fetchAllUsersSequential = (ids: string[]): TE.TaskEither<Error, User[]> =>
|
|
1256
|
-
pipe(
|
|
1257
|
-
ids,
|
|
1258
|
-
A.traverse(TE.ApplicativeSeq)(fetchUser)
|
|
1259
|
-
);
|
|
1260
|
-
|
|
1261
|
-
// Collect successes, ignore failures (using Task instead of TaskEither)
|
|
1262
|
-
const fetchUsersWithFallback = (ids: string[]): T.Task<Array<User | null>> =>
|
|
1263
|
-
pipe(
|
|
1264
|
-
ids,
|
|
1265
|
-
A.traverse(T.ApplicativePar)((id) =>
|
|
1266
|
-
pipe(
|
|
1267
|
-
fetchUser(id),
|
|
1268
|
-
TE.match(
|
|
1269
|
-
() => null,
|
|
1270
|
-
(user) => user
|
|
1271
|
-
)
|
|
1272
|
-
)
|
|
1273
|
-
)
|
|
1274
|
-
);
|
|
1275
|
-
|
|
1276
|
-
// Or keep track of which failed
|
|
1277
|
-
const fetchUsersPartitioned = (
|
|
1278
|
-
ids: string[]
|
|
1279
|
-
): T.Task<{ successes: User[]; failures: Array<{ id: string; error: Error }> }> =>
|
|
1280
|
-
pipe(
|
|
1281
|
-
ids,
|
|
1282
|
-
A.traverse(T.ApplicativePar)((id) =>
|
|
1283
|
-
pipe(
|
|
1284
|
-
fetchUser(id),
|
|
1285
|
-
TE.bimap(
|
|
1286
|
-
(error) => ({ id, error }),
|
|
1287
|
-
(user) => user
|
|
1288
|
-
),
|
|
1289
|
-
(te) => te
|
|
1290
|
-
)
|
|
1291
|
-
),
|
|
1292
|
-
T.map(A.separate),
|
|
1293
|
-
T.map(({ left: failures, right: successes }) => ({ successes, failures }))
|
|
1294
|
-
);
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
|
-
### Pattern: Promise.race to alternative
|
|
1298
|
-
|
|
1299
|
-
```typescript
|
|
1300
|
-
import * as TE from 'fp-ts/TaskEither';
|
|
1301
|
-
import * as T from 'fp-ts/Task';
|
|
1302
|
-
import { pipe } from 'fp-ts/function';
|
|
1303
|
-
|
|
1304
|
-
// Race - first to complete wins
|
|
1305
|
-
const raceTaskEithers = <E, A>(
|
|
1306
|
-
tasks: Array<TE.TaskEither<E, A>>
|
|
1307
|
-
): TE.TaskEither<E, A> =>
|
|
1308
|
-
() => Promise.race(tasks.map((te) => te()));
|
|
1309
|
-
|
|
1310
|
-
// Try alternatives on failure (like Promise.any but typed)
|
|
1311
|
-
const tryAlternatives = <E, A>(
|
|
1312
|
-
primary: TE.TaskEither<E, A>,
|
|
1313
|
-
fallback: TE.TaskEither<E, A>
|
|
1314
|
-
): TE.TaskEither<E, A> =>
|
|
1315
|
-
pipe(
|
|
1316
|
-
primary,
|
|
1317
|
-
TE.orElse(() => fallback)
|
|
1318
|
-
);
|
|
1319
|
-
|
|
1320
|
-
// Chain of fallbacks
|
|
1321
|
-
const withFallbacks = <E, A>(
|
|
1322
|
-
tasks: Array<TE.TaskEither<E, A>>
|
|
1323
|
-
): TE.TaskEither<E, A> =>
|
|
1324
|
-
tasks.reduce((acc, task) => pipe(acc, TE.orElse(() => task)));
|
|
1325
|
-
```
|
|
1326
|
-
|
|
1327
|
-
---
|
|
1328
|
-
|
|
1329
|
-
## 7. Common Pitfalls
|
|
1330
|
-
|
|
1331
|
-
### Pitfall 1: Forgetting to run Tasks
|
|
1332
|
-
|
|
1333
|
-
```typescript
|
|
1334
|
-
// WRONG: Task is not executed
|
|
1335
|
-
const fetchData = (): TE.TaskEither<Error, Data> => /* ... */;
|
|
1336
|
-
const result = fetchData(); // This is still a Task, not the result!
|
|
1337
|
-
|
|
1338
|
-
// CORRECT: Execute the Task
|
|
1339
|
-
const result = await fetchData()(); // Note the double invocation
|
|
1340
|
-
```
|
|
1341
|
-
|
|
1342
|
-
### Pitfall 2: Mixing async/await with fp-ts incorrectly
|
|
1343
|
-
|
|
1344
|
-
```typescript
|
|
1345
|
-
// WRONG: Breaking out of the fp-ts ecosystem
|
|
1346
|
-
const processData = async (input: string): Promise<Result> => {
|
|
1347
|
-
const parsed = parseInput(input); // Returns Either
|
|
1348
|
-
if (E.isLeft(parsed)) {
|
|
1349
|
-
throw new Error(parsed.left.message); // Don't do this!
|
|
1350
|
-
}
|
|
1351
|
-
return await fetchData(parsed.right)();
|
|
1352
|
-
};
|
|
1353
|
-
|
|
1354
|
-
// CORRECT: Stay in the ecosystem
|
|
1355
|
-
const processData = (input: string): TE.TaskEither<Error, Result> =>
|
|
1356
|
-
pipe(
|
|
1357
|
-
parseInput(input),
|
|
1358
|
-
TE.fromEither,
|
|
1359
|
-
TE.flatMap(fetchData)
|
|
1360
|
-
);
|
|
1361
|
-
```
|
|
1362
|
-
|
|
1363
|
-
### Pitfall 3: Using map when flatMap is needed
|
|
1364
|
-
|
|
1365
|
-
```typescript
|
|
1366
|
-
// WRONG: Results in nested Either
|
|
1367
|
-
const result: E.Either<Error, E.Either<Error, User>> = pipe(
|
|
1368
|
-
parseUserId(input), // E.Either<Error, string>
|
|
1369
|
-
E.map(fetchUser) // Returns E.Either<Error, User>, so we get nested Either
|
|
1370
|
-
);
|
|
1371
|
-
|
|
1372
|
-
// CORRECT: Use flatMap to flatten
|
|
1373
|
-
const result: E.Either<Error, User> = pipe(
|
|
1374
|
-
parseUserId(input),
|
|
1375
|
-
E.flatMap(fetchUser)
|
|
1376
|
-
);
|
|
1377
|
-
```
|
|
1378
|
-
|
|
1379
|
-
### Pitfall 4: Losing error information
|
|
1380
|
-
|
|
1381
|
-
```typescript
|
|
1382
|
-
// WRONG: Original error context is lost
|
|
1383
|
-
const fetchData = (): TE.TaskEither<Error, Data> =>
|
|
1384
|
-
pipe(
|
|
1385
|
-
TE.tryCatch(
|
|
1386
|
-
() => fetch('/api/data'),
|
|
1387
|
-
() => new Error('Failed') // Lost the original error!
|
|
1388
|
-
)
|
|
1389
|
-
);
|
|
1390
|
-
|
|
1391
|
-
// CORRECT: Preserve error context
|
|
1392
|
-
const fetchData = (): TE.TaskEither<Error, Data> =>
|
|
1393
|
-
pipe(
|
|
1394
|
-
TE.tryCatch(
|
|
1395
|
-
() => fetch('/api/data'),
|
|
1396
|
-
(reason) => new Error(`Network request failed: ${reason}`)
|
|
1397
|
-
)
|
|
1398
|
-
);
|
|
1399
|
-
|
|
1400
|
-
// BETTER: Use typed errors
|
|
1401
|
-
type FetchError =
|
|
1402
|
-
| { _tag: 'NetworkError'; cause: unknown }
|
|
1403
|
-
| { _tag: 'ParseError'; cause: unknown }
|
|
1404
|
-
| { _tag: 'ValidationError'; message: string };
|
|
1405
|
-
|
|
1406
|
-
const fetchData = (): TE.TaskEither<FetchError, Data> =>
|
|
1407
|
-
pipe(
|
|
1408
|
-
TE.tryCatch(
|
|
1409
|
-
() => fetch('/api/data'),
|
|
1410
|
-
(cause): FetchError => ({ _tag: 'NetworkError', cause })
|
|
1411
|
-
),
|
|
1412
|
-
TE.flatMap((response) =>
|
|
1413
|
-
TE.tryCatch(
|
|
1414
|
-
() => response.json(),
|
|
1415
|
-
(cause): FetchError => ({ _tag: 'ParseError', cause })
|
|
1416
|
-
)
|
|
1417
|
-
)
|
|
1418
|
-
);
|
|
1419
|
-
```
|
|
1420
|
-
|
|
1421
|
-
### Pitfall 5: Overusing fromNullable
|
|
1422
|
-
|
|
1423
|
-
```typescript
|
|
1424
|
-
// WRONG: Unnecessary wrapping and unwrapping
|
|
1425
|
-
const getName = (user: User | null): string => {
|
|
1426
|
-
const optUser = O.fromNullable(user);
|
|
1427
|
-
const name = pipe(optUser, O.map(u => u.name), O.toNullable);
|
|
1428
|
-
return name ?? 'Unknown';
|
|
1429
|
-
};
|
|
1430
|
-
|
|
1431
|
-
// CORRECT: Use Option only when you need its composition benefits
|
|
1432
|
-
const getName = (user: User | null): string => user?.name ?? 'Unknown';
|
|
1433
|
-
|
|
1434
|
-
// BETTER: Use Option when chaining multiple operations
|
|
1435
|
-
const getManagerName = (user: User | null): O.Option<string> =>
|
|
1436
|
-
pipe(
|
|
1437
|
-
O.fromNullable(user),
|
|
1438
|
-
O.flatMap(u => O.fromNullable(u.manager)),
|
|
1439
|
-
O.map(m => m.name)
|
|
1440
|
-
);
|
|
1441
|
-
```
|
|
1442
|
-
|
|
1443
|
-
### Pitfall 6: Not handling the left case
|
|
1444
|
-
|
|
1445
|
-
```typescript
|
|
1446
|
-
// WRONG: Ignoring potential errors
|
|
1447
|
-
const processUser = (input: string): User => {
|
|
1448
|
-
const result = parseUser(input); // E.Either<Error, User>
|
|
1449
|
-
return (result as E.Right<User>).right; // Unsafe cast!
|
|
1450
|
-
};
|
|
1451
|
-
|
|
1452
|
-
// CORRECT: Always handle both cases
|
|
1453
|
-
const processUser = (input: string): User =>
|
|
1454
|
-
pipe(
|
|
1455
|
-
parseUser(input),
|
|
1456
|
-
E.getOrElse((error) => {
|
|
1457
|
-
console.error('Parse failed:', error);
|
|
1458
|
-
return defaultUser;
|
|
1459
|
-
})
|
|
1460
|
-
);
|
|
1461
|
-
```
|
|
1462
|
-
|
|
1463
|
-
---
|
|
1464
|
-
|
|
1465
|
-
## 8. Gradual Adoption Strategies
|
|
1466
|
-
|
|
1467
|
-
### Strategy 1: Start at the Boundaries
|
|
1468
|
-
|
|
1469
|
-
Begin by converting functions at the edges of your system:
|
|
1470
|
-
- API response handlers
|
|
1471
|
-
- Database query results
|
|
1472
|
-
- File system operations
|
|
1473
|
-
- User input validation
|
|
1474
|
-
|
|
1475
|
-
```typescript
|
|
1476
|
-
// Wrap external API calls first
|
|
1477
|
-
const fetchUserApi = (id: string): TE.TaskEither<ApiError, UserDto> =>
|
|
1478
|
-
pipe(
|
|
1479
|
-
TE.tryCatch(
|
|
1480
|
-
() => externalApiClient.getUser(id),
|
|
1481
|
-
(e) => ({ type: 'api_error' as const, cause: e })
|
|
1482
|
-
)
|
|
1483
|
-
);
|
|
1484
|
-
|
|
1485
|
-
// Internal code can stay imperative initially
|
|
1486
|
-
async function handleUserRequest(userId: string) {
|
|
1487
|
-
const result = await fetchUserApi(userId)();
|
|
1488
|
-
if (E.isRight(result)) {
|
|
1489
|
-
// Process user with existing code
|
|
1490
|
-
return processUser(result.right);
|
|
1491
|
-
} else {
|
|
1492
|
-
throw new Error(`API error: ${result.left.type}`);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
```
|
|
1496
|
-
|
|
1497
|
-
### Strategy 2: Create Bridge Functions
|
|
1498
|
-
|
|
1499
|
-
Build helpers to convert between fp-ts and imperative code:
|
|
1500
|
-
|
|
1501
|
-
```typescript
|
|
1502
|
-
// Bridge from Either to thrown errors
|
|
1503
|
-
const unsafeUnwrap = <E, A>(either: E.Either<E, A>): A =>
|
|
1504
|
-
pipe(
|
|
1505
|
-
either,
|
|
1506
|
-
E.getOrElseW((e) => {
|
|
1507
|
-
throw e instanceof Error ? e : new Error(String(e));
|
|
1508
|
-
})
|
|
1509
|
-
);
|
|
1510
|
-
|
|
1511
|
-
// Bridge from thrown errors to Either
|
|
1512
|
-
const catchSync = <A>(f: () => A): E.Either<Error, A> =>
|
|
1513
|
-
E.tryCatch(f, (e) => (e instanceof Error ? e : new Error(String(e))));
|
|
1514
|
-
|
|
1515
|
-
// Bridge from Promise to TaskEither
|
|
1516
|
-
const fromPromise = <A>(p: Promise<A>): TE.TaskEither<Error, A> =>
|
|
1517
|
-
TE.tryCatch(() => p, (e) => (e instanceof Error ? e : new Error(String(e))));
|
|
1518
|
-
|
|
1519
|
-
// Bridge from TaskEither to Promise (throws on Left)
|
|
1520
|
-
const toPromise = <E, A>(te: TE.TaskEither<E, A>): Promise<A> =>
|
|
1521
|
-
te().then(E.getOrElseW((e) => { throw e; }));
|
|
1522
|
-
```
|
|
1523
|
-
|
|
1524
|
-
### Strategy 3: Module-by-Module Migration
|
|
1525
|
-
|
|
1526
|
-
1. **Pick a module** with clear boundaries
|
|
1527
|
-
2. **Add fp-ts types** to internal functions
|
|
1528
|
-
3. **Keep external API unchanged** initially
|
|
1529
|
-
4. **Test thoroughly** before moving on
|
|
1530
|
-
5. **Update external API** once internals are stable
|
|
1531
|
-
|
|
1532
|
-
```typescript
|
|
1533
|
-
// Phase 1: Internal functions use fp-ts
|
|
1534
|
-
// File: user-service.internal.ts
|
|
1535
|
-
export const validateUser = (data: unknown): E.Either<ValidationError, User> => /* ... */;
|
|
1536
|
-
export const enrichUser = (user: User): TE.TaskEither<Error, EnrichedUser> => /* ... */;
|
|
1537
|
-
|
|
1538
|
-
// File: user-service.ts (public API unchanged)
|
|
1539
|
-
export async function getUser(id: string): Promise<User> {
|
|
1540
|
-
const result = await pipe(
|
|
1541
|
-
fetchUser(id),
|
|
1542
|
-
TE.flatMap(validateUser >>> TE.fromEither),
|
|
1543
|
-
TE.flatMap(enrichUser)
|
|
1544
|
-
)();
|
|
1545
|
-
|
|
1546
|
-
if (E.isLeft(result)) {
|
|
1547
|
-
throw result.left;
|
|
1548
|
-
}
|
|
1549
|
-
return result.right;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
// Phase 2: Update public API
|
|
1553
|
-
// File: user-service.ts
|
|
1554
|
-
export const getUser = (id: string): TE.TaskEither<UserError, User> =>
|
|
1555
|
-
pipe(
|
|
1556
|
-
fetchUser(id),
|
|
1557
|
-
TE.flatMap(validateUser >>> TE.fromEither),
|
|
1558
|
-
TE.flatMap(enrichUser)
|
|
1559
|
-
);
|
|
1560
|
-
```
|
|
1561
|
-
|
|
1562
|
-
### Strategy 4: Type-Driven Development
|
|
1563
|
-
|
|
1564
|
-
Use TypeScript's type system to guide the migration:
|
|
1565
|
-
|
|
1566
|
-
```typescript
|
|
1567
|
-
// Step 1: Change type signature first
|
|
1568
|
-
type OldGetUser = (id: string) => Promise<User | null>;
|
|
1569
|
-
type NewGetUser = (id: string) => TE.TaskEither<UserError, User>;
|
|
1570
|
-
|
|
1571
|
-
// Step 2: Compiler will show all call sites that need updating
|
|
1572
|
-
const getUser: NewGetUser = (id) => /* implement */;
|
|
1573
|
-
|
|
1574
|
-
// Step 3: Update call sites one by one
|
|
1575
|
-
// The compiler ensures you handle all cases
|
|
1576
|
-
```
|
|
1577
|
-
|
|
1578
|
-
### Strategy 5: Testing as Documentation
|
|
1579
|
-
|
|
1580
|
-
Write tests that demonstrate the expected behavior:
|
|
1581
|
-
|
|
1582
|
-
```typescript
|
|
1583
|
-
describe('UserService', () => {
|
|
1584
|
-
describe('getUser (fp-ts)', () => {
|
|
1585
|
-
it('returns Right with user on success', async () => {
|
|
1586
|
-
const result = await getUser('valid-id')();
|
|
1587
|
-
expect(E.isRight(result)).toBe(true);
|
|
1588
|
-
if (E.isRight(result)) {
|
|
1589
|
-
expect(result.right.id).toBe('valid-id');
|
|
1590
|
-
}
|
|
1591
|
-
});
|
|
1592
|
-
|
|
1593
|
-
it('returns Left with NotFound error for unknown id', async () => {
|
|
1594
|
-
const result = await getUser('unknown')();
|
|
1595
|
-
expect(E.isLeft(result)).toBe(true);
|
|
1596
|
-
if (E.isLeft(result)) {
|
|
1597
|
-
expect(result.left._tag).toBe('NotFound');
|
|
1598
|
-
}
|
|
1599
|
-
});
|
|
1600
|
-
});
|
|
1601
|
-
});
|
|
1602
|
-
```
|
|
1603
|
-
|
|
1604
|
-
---
|
|
1605
|
-
|
|
1606
|
-
## 9. When NOT to Refactor
|
|
1607
|
-
|
|
1608
|
-
### Simple Synchronous Code
|
|
1609
|
-
|
|
1610
|
-
Don't refactor straightforward code that doesn't benefit from fp-ts:
|
|
1611
|
-
|
|
1612
|
-
```typescript
|
|
1613
|
-
// This is fine as-is
|
|
1614
|
-
function formatName(first: string, last: string): string {
|
|
1615
|
-
return `${first} ${last}`;
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
// Don't do this - it adds complexity without benefit
|
|
1619
|
-
const formatName = (first: string, last: string): string =>
|
|
1620
|
-
pipe(
|
|
1621
|
-
first,
|
|
1622
|
-
(f) => `${f} ${last}`
|
|
1623
|
-
);
|
|
1624
|
-
```
|
|
1625
|
-
|
|
1626
|
-
### Performance-Critical Loops
|
|
1627
|
-
|
|
1628
|
-
fp-ts operations create intermediate arrays. For hot paths, keep imperative code:
|
|
1629
|
-
|
|
1630
|
-
```typescript
|
|
1631
|
-
// Keep this for performance-critical code processing millions of items
|
|
1632
|
-
function sumLargeArray(numbers: number[]): number {
|
|
1633
|
-
let sum = 0;
|
|
1634
|
-
for (let i = 0; i < numbers.length; i++) {
|
|
1635
|
-
sum += numbers[i];
|
|
1636
|
-
}
|
|
1637
|
-
return sum;
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
// This creates intermediate arrays
|
|
1641
|
-
const sumWithFpts = (numbers: number[]): number =>
|
|
1642
|
-
pipe(numbers, A.reduce(0, (acc, n) => acc + n));
|
|
1643
|
-
```
|
|
1644
|
-
|
|
1645
|
-
### Third-Party Library Interfaces
|
|
1646
|
-
|
|
1647
|
-
When working with libraries that expect specific patterns:
|
|
1648
|
-
|
|
1649
|
-
```typescript
|
|
1650
|
-
// Express middleware must match Express's interface
|
|
1651
|
-
app.get('/users/:id', async (req, res) => {
|
|
1652
|
-
// Keep imperative here, convert at boundaries
|
|
1653
|
-
const result = await getUser(req.params.id)();
|
|
1654
|
-
|
|
1655
|
-
if (E.isLeft(result)) {
|
|
1656
|
-
res.status(404).json({ error: result.left.message });
|
|
1657
|
-
} else {
|
|
1658
|
-
res.json(result.right);
|
|
1659
|
-
}
|
|
1660
|
-
});
|
|
1661
|
-
```
|
|
1662
|
-
|
|
1663
|
-
### Code Touched by Non-FP Team Members
|
|
1664
|
-
|
|
1665
|
-
If your team isn't familiar with fp-ts, forced adoption will hurt productivity:
|
|
1666
|
-
|
|
1667
|
-
```typescript
|
|
1668
|
-
// If team doesn't know fp-ts, this is harder to maintain
|
|
1669
|
-
const processOrder = (order: Order): TE.TaskEither<Error, Result> =>
|
|
1670
|
-
pipe(
|
|
1671
|
-
validateOrder(order),
|
|
1672
|
-
TE.fromEither,
|
|
1673
|
-
TE.flatMap(enrichOrder),
|
|
1674
|
-
TE.flatMap(submitOrder)
|
|
1675
|
-
);
|
|
1676
|
-
|
|
1677
|
-
// Familiar to all TypeScript developers
|
|
1678
|
-
async function processOrder(order: Order): Promise<Result> {
|
|
1679
|
-
const validated = validateOrder(order);
|
|
1680
|
-
if (!validated.success) {
|
|
1681
|
-
throw new Error(validated.error);
|
|
1682
|
-
}
|
|
1683
|
-
const enriched = await enrichOrder(validated.data);
|
|
1684
|
-
return await submitOrder(enriched);
|
|
1685
|
-
}
|
|
1686
|
-
```
|
|
1687
|
-
|
|
1688
|
-
### Trivial Null Checks
|
|
1689
|
-
|
|
1690
|
-
Don't use Option for simple, one-off null checks:
|
|
1691
|
-
|
|
1692
|
-
```typescript
|
|
1693
|
-
// This is fine
|
|
1694
|
-
const name = user?.name ?? 'Anonymous';
|
|
1695
|
-
|
|
1696
|
-
// Overkill for simple cases
|
|
1697
|
-
const name = pipe(
|
|
1698
|
-
O.fromNullable(user),
|
|
1699
|
-
O.map((u) => u.name),
|
|
1700
|
-
O.getOrElse(() => 'Anonymous')
|
|
1701
|
-
);
|
|
1702
|
-
```
|
|
1703
|
-
|
|
1704
|
-
### When the Error Type Doesn't Matter
|
|
1705
|
-
|
|
1706
|
-
If you're going to throw/log anyway and don't need error composition:
|
|
1707
|
-
|
|
1708
|
-
```typescript
|
|
1709
|
-
// If this is your error handling anyway...
|
|
1710
|
-
try {
|
|
1711
|
-
await doSomething();
|
|
1712
|
-
} catch (e) {
|
|
1713
|
-
logger.error(e);
|
|
1714
|
-
throw e;
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
// ...then Either doesn't add much value
|
|
1718
|
-
const result = await doSomethingTE()();
|
|
1719
|
-
if (E.isLeft(result)) {
|
|
1720
|
-
logger.error(result.left);
|
|
1721
|
-
throw result.left;
|
|
1722
|
-
}
|
|
1723
|
-
```
|
|
1724
|
-
|
|
1725
|
-
### Test Code
|
|
1726
|
-
|
|
1727
|
-
Test code should be readable, not necessarily functional:
|
|
1728
|
-
|
|
1729
|
-
```typescript
|
|
1730
|
-
// Clear test code
|
|
1731
|
-
describe('UserService', () => {
|
|
1732
|
-
it('creates a user', async () => {
|
|
1733
|
-
const user = await createUser({ name: 'Alice' });
|
|
1734
|
-
expect(user.name).toBe('Alice');
|
|
1735
|
-
});
|
|
1736
|
-
});
|
|
1737
|
-
|
|
1738
|
-
// Unnecessarily complex
|
|
1739
|
-
describe('UserService', () => {
|
|
1740
|
-
it('creates a user', async () => {
|
|
1741
|
-
await pipe(
|
|
1742
|
-
createUser({ name: 'Alice' }),
|
|
1743
|
-
TE.map((user) => expect(user.name).toBe('Alice')),
|
|
1744
|
-
TE.getOrElse(() => T.of(fail('Should not fail')))
|
|
1745
|
-
)();
|
|
1746
|
-
});
|
|
1747
|
-
});
|
|
1748
|
-
```
|
|
1749
|
-
|
|
1750
|
-
---
|
|
1751
|
-
|
|
1752
|
-
## Quick Reference: Imperative to fp-ts Mapping
|
|
1753
|
-
|
|
1754
|
-
| Imperative Pattern | fp-ts Equivalent |
|
|
1755
|
-
|-------------------|------------------|
|
|
1756
|
-
| `try { } catch { }` | `E.tryCatch()`, `TE.tryCatch()` |
|
|
1757
|
-
| `throw new Error()` | `E.left()`, `TE.left()` |
|
|
1758
|
-
| `return value` | `E.right()`, `TE.right()` |
|
|
1759
|
-
| `if (x === null)` | `O.fromNullable()`, `O.isNone()` |
|
|
1760
|
-
| `x ?? defaultValue` | `O.getOrElse()` |
|
|
1761
|
-
| `x?.property` | `O.map()`, `O.flatMap()` |
|
|
1762
|
-
| `array.map()` | `A.map()` |
|
|
1763
|
-
| `array.filter()` | `A.filter()` |
|
|
1764
|
-
| `array.reduce()` | `A.reduce()`, `A.foldMap()` |
|
|
1765
|
-
| `array.find()` | `A.findFirst()` |
|
|
1766
|
-
| `array.flatMap()` | `A.flatMap()` |
|
|
1767
|
-
| `Promise.then()` | `TE.map()`, `TE.flatMap()` |
|
|
1768
|
-
| `Promise.catch()` | `TE.orElse()`, `TE.mapLeft()` |
|
|
1769
|
-
| `Promise.all()` | `A.traverse(TE.ApplicativePar)` |
|
|
1770
|
-
| `async/await` | `TE.flatMap()` chain |
|
|
1771
|
-
| `new Class(deps)` | `R.asks()`, `RTE.ask()` |
|
|
1772
|
-
| `for...of` | `A.map()`, `A.reduce()` |
|
|
1773
|
-
| `while` | Recursion, `unfold()` |
|
|
1774
|
-
|
|
1775
|
-
---
|
|
1776
|
-
|
|
1777
|
-
## Summary
|
|
1778
|
-
|
|
1779
|
-
Migrating to fp-ts is a journey, not a destination. Key principles:
|
|
1780
|
-
|
|
1781
|
-
1. **Start small**: Convert individual functions, not entire codebases
|
|
1782
|
-
2. **Be pragmatic**: Not everything needs to be functional
|
|
1783
|
-
3. **Type-driven**: Let the compiler guide your refactoring
|
|
1784
|
-
4. **Test thoroughly**: Each conversion should be verified
|
|
1785
|
-
5. **Document patterns**: Create team-specific guides for your codebase
|
|
1786
|
-
6. **Review benefits**: Ensure the added complexity provides value
|
|
1787
|
-
|
|
1788
|
-
The goal is more maintainable, type-safe code—not functional programming for its own sake.
|
|
1789
|
-
|
|
1790
34
|
## Limitations
|
|
1791
35
|
- Use this skill only when the task clearly matches the scope described above.
|
|
1792
36
|
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|