@mytechtoday/augment-extensions 1.7.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +240 -19
- package/augment-extensions/coding-standards/bash/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/bash/metadata.json +21 -0
- package/augment-extensions/coding-standards/c/metadata.json +50 -0
- package/augment-extensions/coding-standards/css/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/css/metadata.json +30 -0
- package/augment-extensions/coding-standards/go/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/go/metadata.json +57 -0
- package/augment-extensions/coding-standards/html/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/html/metadata.json +25 -0
- package/augment-extensions/coding-standards/html-css-js/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/html-css-js/metadata.json +40 -0
- package/augment-extensions/coding-standards/js/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/js/metadata.json +37 -0
- package/augment-extensions/coding-standards/perl/README.md +173 -0
- package/augment-extensions/coding-standards/php/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/php/metadata.json +35 -0
- package/augment-extensions/coding-standards/powershell/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/powershell/metadata.json +33 -0
- package/augment-extensions/coding-standards/python/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/python/metadata.json +38 -0
- package/augment-extensions/coding-standards/react/CHANGELOG.md +51 -0
- package/augment-extensions/coding-standards/react/metadata.json +31 -0
- package/augment-extensions/coding-standards/typescript/CHANGELOG.md +39 -0
- package/augment-extensions/coding-standards/typescript/README.md +248 -19
- package/augment-extensions/coding-standards/typescript/VERSION +2 -0
- package/augment-extensions/coding-standards/typescript/examples/biome.json +199 -0
- package/augment-extensions/coding-standards/typescript/examples/eslint-flat-config.js +166 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/README.md +47 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/package.json +37 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/pnpm-workspace.yaml +5 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/tsconfig.base.json +50 -0
- package/augment-extensions/coding-standards/typescript/examples/monorepo-config/turbo.json +47 -0
- package/augment-extensions/coding-standards/typescript/examples/vitest-setup.ts +198 -0
- package/augment-extensions/coding-standards/typescript/metadata.json +46 -0
- package/augment-extensions/coding-standards/typescript/module.json +25 -7
- package/augment-extensions/coding-standards/typescript/rules/architecture.md +1096 -0
- package/augment-extensions/coding-standards/typescript/rules/error-handling.md +1174 -0
- package/augment-extensions/coding-standards/typescript/rules/modern-features.md +625 -0
- package/augment-extensions/coding-standards/typescript/rules/monorepo.md +745 -0
- package/augment-extensions/coding-standards/typescript/rules/security-performance.md +850 -0
- package/augment-extensions/coding-standards/typescript/rules/testing.md +918 -0
- package/augment-extensions/coding-standards/typescript/rules/tooling.md +1071 -0
- package/augment-extensions/coding-standards/typescript/rules/type-patterns.md +750 -0
- package/augment-extensions/domain-rules/api-design/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/api-design/metadata.json +32 -0
- package/augment-extensions/domain-rules/database/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/database/metadata.json +50 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/dracula/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/high-contrast/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/monokai/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/nord/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/one-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/one-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-dark/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/solarized-light/metadata.json +18 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/design/color/themes/tokyo-night/metadata.json +18 -0
- package/augment-extensions/domain-rules/mcp/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/mcp/metadata.json +42 -0
- package/augment-extensions/domain-rules/security/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/security/metadata.json +33 -0
- package/augment-extensions/domain-rules/seo-sales-marketing/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/seo-sales-marketing/metadata.json +40 -0
- package/augment-extensions/domain-rules/software-architecture/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/software-architecture/metadata.json +55 -0
- package/augment-extensions/domain-rules/wordpress/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/wordpress/metadata.json +42 -0
- package/augment-extensions/domain-rules/wordpress-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/domain-rules/wordpress-plugin/metadata.json +70 -0
- package/augment-extensions/examples/design-patterns/CHANGELOG.md +51 -0
- package/augment-extensions/examples/design-patterns/metadata.json +29 -0
- package/augment-extensions/examples/gutenberg-block-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/examples/gutenberg-block-plugin/metadata.json +26 -0
- package/augment-extensions/examples/rest-api-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/examples/rest-api-plugin/metadata.json +27 -0
- package/augment-extensions/examples/woocommerce-extension/CHANGELOG.md +51 -0
- package/augment-extensions/examples/woocommerce-extension/metadata.json +27 -0
- package/augment-extensions/visual-design/metadata.json +34 -0
- package/augment-extensions/workflows/adr-support/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/adr-support/metadata.json +41 -0
- package/augment-extensions/workflows/beads/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/beads/metadata.json +23 -0
- package/augment-extensions/workflows/beads-integration/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/beads-integration/metadata.json +30 -0
- package/augment-extensions/workflows/database/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/database/metadata.json +30 -0
- package/augment-extensions/workflows/wordpress-plugin/CHANGELOG.md +51 -0
- package/augment-extensions/workflows/wordpress-plugin/metadata.json +41 -0
- package/augment-extensions/writing-standards/literature/shakespeare/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/literature/shakespeare/metadata.json +60 -0
- package/augment-extensions/writing-standards/screenplay/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/monty-python/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/monty-python/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/saturday-night-live/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/comedy-formats/saturday-night-live/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/alfred-hitchcock/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/alfred-hitchcock/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/ari-aster/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/ari-aster/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brad-bird/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brad-bird/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brian-de-palma/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/brian-de-palma/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/buster-keaton/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/buster-keaton/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/christopher-nolan/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/christopher-nolan/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/clint-eastwood/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/clint-eastwood/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/coen-brothers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/coen-brothers/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/darren-aronofsky/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/darren-aronofsky/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-fincher/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-fincher/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-lynch/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/david-lynch/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/denis-villeneuve/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/denis-villeneuve/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/francis-ford-coppola/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/francis-ford-coppola/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gary-marshall/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gary-marshall/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-a-romero/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-a-romero/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-lucas/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/george-lucas/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/guillermo-del-toro/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/guillermo-del-toro/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gus-van-sant/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/gus-van-sant/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/james-ivory-ismail-merchant/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/james-ivory-ismail-merchant/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jim-jarmusch/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jim-jarmusch/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-carpenter/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-carpenter/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-ford/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-ford/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-huston/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-huston/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-landis/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/john-landis/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jonathan-demme/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/jonathan-demme/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/joseph-l-mankiewicz/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/joseph-l-mankiewicz/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kathryn-bigelow/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kathryn-bigelow/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kelly-reichardt/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kelly-reichardt/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kevin-smith/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/kevin-smith/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/linda-shayne/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/linda-shayne/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/martin-scorsese/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/martin-scorsese/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mel-brooks/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mel-brooks/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-curtiz/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-curtiz/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-mann/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/michael-mann/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mike-nichols/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/mike-nichols/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/orson-welles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/orson-welles/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/park-chan-wook/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/park-chan-wook/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/paul-thomas-anderson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/paul-thomas-anderson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/penny-marshall/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/penny-marshall/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/peter-bogdanovich/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/peter-bogdanovich/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/quentin-tarantino/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/quentin-tarantino/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/richard-linklater/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/richard-linklater/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/rob-reiner/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/rob-reiner/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-altman/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-altman/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-eggers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-eggers/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-zemeckis/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/robert-zemeckis/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sam-peckinpah/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sam-peckinpah/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sidney-lumet/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sidney-lumet/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/spike-lee/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/spike-lee/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-donen-gene-kelly/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-donen-gene-kelly/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-kubrick/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/stanley-kubrick/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steve-martin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steve-martin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steven-spielberg/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/steven-spielberg/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sydney-pollack/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/sydney-pollack/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/terry-gilliam/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/terry-gilliam/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tim-burton/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tim-burton/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tobe-hooper/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/tobe-hooper/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/wes-anderson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/wes-anderson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/william-friedkin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/directors/william-friedkin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/blue-ruin/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/blue-ruin/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/films/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/fast-and-furious/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/fast-and-furious/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/harry-potter/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/harry-potter/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/james-bond/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/james-bond/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/john-wick/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/john-wick/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/lord-of-the-rings/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/lord-of-the-rings/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/mcu/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/mcu/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/metadata.json +25 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-trek/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-trek/metadata.json +33 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-wars/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/franchises/star-wars/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/metadata.json +50 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/joseph-campbell/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/joseph-campbell/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/narrative-theory/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/bruckheimer-and-simpson/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/bruckheimer-and-simpson/metadata.json +24 -0
- package/augment-extensions/writing-standards/screenplay/cinematic-styles/producers/metadata.json +23 -0
- package/augment-extensions/writing-standards/screenplay/genres/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/genres/metadata.json +43 -0
- package/augment-extensions/writing-standards/screenplay/metadata.json +65 -0
- package/augment-extensions/writing-standards/screenplay/styles/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/styles/metadata.json +38 -0
- package/augment-extensions/writing-standards/screenplay/themes/CHANGELOG.md +51 -0
- package/augment-extensions/writing-standards/screenplay/themes/metadata.json +47 -0
- package/cli/dist/__test_health-checker.d.ts +2 -0
- package/cli/dist/__test_health-checker.d.ts.map +1 -0
- package/cli/dist/__test_health-checker.js +4 -0
- package/cli/dist/__test_health-checker.js.map +1 -0
- package/cli/dist/__test_template-engine.d.ts +2 -0
- package/cli/dist/__test_template-engine.d.ts.map +1 -0
- package/cli/dist/__test_template-engine.js +4 -0
- package/cli/dist/__test_template-engine.js.map +1 -0
- package/cli/dist/cli.js +25 -0
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/formatter/json-formatter.d.ts +1 -0
- package/cli/dist/commands/generate-shot-list/formatter/json-formatter.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/formatter/json-formatter.js +6 -0
- package/cli/dist/commands/generate-shot-list/formatter/json-formatter.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/formatter/jsonl-formatter.d.ts +1 -0
- package/cli/dist/commands/generate-shot-list/formatter/jsonl-formatter.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/formatter/jsonl-formatter.js +6 -0
- package/cli/dist/commands/generate-shot-list/formatter/jsonl-formatter.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/formatter/markdown-formatter.d.ts +14 -0
- package/cli/dist/commands/generate-shot-list/formatter/markdown-formatter.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/formatter/markdown-formatter.js +46 -3
- package/cli/dist/commands/generate-shot-list/formatter/markdown-formatter.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/context-builder.d.ts +29 -1
- package/cli/dist/commands/generate-shot-list/generator/context-builder.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/context-builder.js +310 -27
- package/cli/dist/commands/generate-shot-list/generator/context-builder.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/index.d.ts +26 -2
- package/cli/dist/commands/generate-shot-list/generator/index.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/index.js +162 -10
- package/cli/dist/commands/generate-shot-list/generator/index.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/metadata-extractor.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/metadata-extractor.js +1 -0
- package/cli/dist/commands/generate-shot-list/generator/metadata-extractor.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/generator/types.d.ts +12 -2
- package/cli/dist/commands/generate-shot-list/generator/types.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.d.ts +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/help-text.js +48 -5
- package/cli/dist/commands/generate-shot-list/help-text.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.d.ts +11 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.js +61 -1
- package/cli/dist/commands/generate-shot-list/logger/jsonl-writer.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.d.ts +29 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.js +111 -0
- package/cli/dist/commands/generate-shot-list/parser/docx-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.d.ts +18 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.js +184 -0
- package/cli/dist/commands/generate-shot-list/parser/finaldraft-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/index.d.ts +4 -3
- package/cli/dist/commands/generate-shot-list/parser/index.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/index.js +54 -3
- package/cli/dist/commands/generate-shot-list/parser/index.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.d.ts +24 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.js +84 -0
- package/cli/dist/commands/generate-shot-list/parser/pdf-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.d.ts +28 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.js +143 -0
- package/cli/dist/commands/generate-shot-list/parser/rtf-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/parser/types.d.ts +9 -6
- package/cli/dist/commands/generate-shot-list/parser/types.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list/parser/types.js.map +1 -1
- package/cli/dist/commands/generate-shot-list/schema/shot-list.schema.json +341 -0
- package/cli/dist/commands/generate-shot-list/schema/validator.d.ts +55 -0
- package/cli/dist/commands/generate-shot-list/schema/validator.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/schema/validator.js +180 -0
- package/cli/dist/commands/generate-shot-list/schema/validator.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.d.ts +36 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.js +108 -0
- package/cli/dist/commands/generate-shot-list/style/guideline-parser.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/index.d.ts +39 -0
- package/cli/dist/commands/generate-shot-list/style/index.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/index.js +85 -0
- package/cli/dist/commands/generate-shot-list/style/index.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.d.ts +35 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.js +178 -0
- package/cli/dist/commands/generate-shot-list/style/style-loader.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.d.ts +36 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.js +123 -0
- package/cli/dist/commands/generate-shot-list/style/style-merger.js.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/types.d.ts +105 -0
- package/cli/dist/commands/generate-shot-list/style/types.d.ts.map +1 -0
- package/cli/dist/commands/generate-shot-list/style/types.js +8 -0
- package/cli/dist/commands/generate-shot-list/style/types.js.map +1 -0
- package/cli/dist/commands/generate-shot-list.d.ts +1 -0
- package/cli/dist/commands/generate-shot-list.d.ts.map +1 -1
- package/cli/dist/commands/generate-shot-list.js +79 -25
- package/cli/dist/commands/generate-shot-list.js.map +1 -1
- package/cli/dist/commands/list.d.ts +1 -0
- package/cli/dist/commands/list.d.ts.map +1 -1
- package/cli/dist/commands/list.js +21 -5
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/upgrade.d.ts +15 -0
- package/cli/dist/commands/upgrade.d.ts.map +1 -0
- package/cli/dist/commands/upgrade.js +229 -0
- package/cli/dist/commands/upgrade.js.map +1 -0
- package/cli/dist/commands/use.d.ts +15 -0
- package/cli/dist/commands/use.d.ts.map +1 -0
- package/cli/dist/commands/use.js +180 -0
- package/cli/dist/commands/use.js.map +1 -0
- package/cli/dist/commands/version-info.d.ts +15 -0
- package/cli/dist/commands/version-info.d.ts.map +1 -0
- package/cli/dist/commands/version-info.js +194 -0
- package/cli/dist/commands/version-info.js.map +1 -0
- package/cli/dist/core/compatibility-checker.d.ts +73 -0
- package/cli/dist/core/compatibility-checker.d.ts.map +1 -0
- package/cli/dist/core/compatibility-checker.js +188 -0
- package/cli/dist/core/compatibility-checker.js.map +1 -0
- package/cli/dist/core/index.d.ts +9 -0
- package/cli/dist/core/index.d.ts.map +1 -0
- package/cli/dist/core/index.js +16 -0
- package/cli/dist/core/index.js.map +1 -0
- package/cli/dist/core/module-loader.d.ts +68 -0
- package/cli/dist/core/module-loader.d.ts.map +1 -0
- package/cli/dist/core/module-loader.js +137 -0
- package/cli/dist/core/module-loader.js.map +1 -0
- package/cli/dist/core/version-manager.d.ts +62 -0
- package/cli/dist/core/version-manager.d.ts.map +1 -0
- package/cli/dist/core/version-manager.js +157 -0
- package/cli/dist/core/version-manager.js.map +1 -0
- package/cli/dist/core/version-resolver.d.ts +63 -0
- package/cli/dist/core/version-resolver.d.ts.map +1 -0
- package/cli/dist/core/version-resolver.js +163 -0
- package/cli/dist/core/version-resolver.js.map +1 -0
- package/cli/dist/gui/app.d.ts +17 -0
- package/cli/dist/gui/app.d.ts.map +1 -0
- package/cli/dist/gui/app.js +79 -0
- package/cli/dist/gui/app.js.map +1 -0
- package/cli/dist/gui/components/preview-pane.d.ts +40 -0
- package/cli/dist/gui/components/preview-pane.d.ts.map +1 -0
- package/cli/dist/gui/components/preview-pane.js +90 -0
- package/cli/dist/gui/components/preview-pane.js.map +1 -0
- package/cli/dist/gui/components/search-filter.d.ts +26 -0
- package/cli/dist/gui/components/search-filter.d.ts.map +1 -0
- package/cli/dist/gui/components/search-filter.js +168 -0
- package/cli/dist/gui/components/search-filter.js.map +1 -0
- package/cli/dist/gui/components/status-bar.d.ts +22 -0
- package/cli/dist/gui/components/status-bar.d.ts.map +1 -0
- package/cli/dist/gui/components/status-bar.js +58 -0
- package/cli/dist/gui/components/status-bar.js.map +1 -0
- package/cli/dist/gui/components/tree-navigator.d.ts +30 -0
- package/cli/dist/gui/components/tree-navigator.d.ts.map +1 -0
- package/cli/dist/gui/components/tree-navigator.js +133 -0
- package/cli/dist/gui/components/tree-navigator.js.map +1 -0
- package/cli/dist/gui/components/version-selector.d.ts +27 -0
- package/cli/dist/gui/components/version-selector.d.ts.map +1 -0
- package/cli/dist/gui/components/version-selector.js +110 -0
- package/cli/dist/gui/components/version-selector.js.map +1 -0
- package/cli/dist/gui/hooks/use-version-loader.d.ts +25 -0
- package/cli/dist/gui/hooks/use-version-loader.d.ts.map +1 -0
- package/cli/dist/gui/hooks/use-version-loader.js +116 -0
- package/cli/dist/gui/hooks/use-version-loader.js.map +1 -0
- package/cli/dist/gui/index.d.ts +15 -0
- package/cli/dist/gui/index.d.ts.map +1 -0
- package/cli/dist/gui/index.js +39 -0
- package/cli/dist/gui/index.js.map +1 -0
- package/cli/dist/gui/layouts/main-layout.d.ts +14 -0
- package/cli/dist/gui/layouts/main-layout.d.ts.map +1 -0
- package/cli/dist/gui/layouts/main-layout.js +205 -0
- package/cli/dist/gui/layouts/main-layout.js.map +1 -0
- package/cli/dist/gui/state/navigation-state.d.ts +39 -0
- package/cli/dist/gui/state/navigation-state.d.ts.map +1 -0
- package/cli/dist/gui/state/navigation-state.js +151 -0
- package/cli/dist/gui/state/navigation-state.js.map +1 -0
- package/cli/dist/gui/state/selection-state.d.ts +36 -0
- package/cli/dist/gui/state/selection-state.d.ts.map +1 -0
- package/cli/dist/gui/state/selection-state.js +132 -0
- package/cli/dist/gui/state/selection-state.js.map +1 -0
- package/cli/dist/gui/theme.d.ts +50 -0
- package/cli/dist/gui/theme.d.ts.map +1 -0
- package/cli/dist/gui/theme.js +73 -0
- package/cli/dist/gui/theme.js.map +1 -0
- package/cli/dist/utils/help.d.ts +13 -0
- package/cli/dist/utils/help.d.ts.map +1 -0
- package/cli/dist/utils/help.js +137 -0
- package/cli/dist/utils/help.js.map +1 -0
- package/cli/dist/utils/module-system.d.ts +8 -0
- package/cli/dist/utils/module-system.d.ts.map +1 -1
- package/cli/dist/utils/module-system.js +38 -3
- package/cli/dist/utils/module-system.js.map +1 -1
- package/cli/dist/utils/validate-versioning.d.ts +43 -0
- package/cli/dist/utils/validate-versioning.d.ts.map +1 -0
- package/cli/dist/utils/validate-versioning.js +258 -0
- package/cli/dist/utils/validate-versioning.js.map +1 -0
- package/package.json +11 -1
|
@@ -0,0 +1,1174 @@
|
|
|
1
|
+
# TypeScript Error Handling Strategies
|
|
2
|
+
|
|
3
|
+
Comprehensive guide to error handling in TypeScript applications covering traditional approaches and modern functional patterns.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Traditional Try/Catch](#traditional-trycatch)
|
|
7
|
+
- [Result Types](#result-types)
|
|
8
|
+
- [Neverthrow Pattern](#neverthrow-pattern)
|
|
9
|
+
- [Effect-TS Introduction](#effect-ts-introduction)
|
|
10
|
+
- [Error Boundary Patterns](#error-boundary-patterns)
|
|
11
|
+
- [Best Practices](#best-practices)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Traditional Try/Catch
|
|
16
|
+
|
|
17
|
+
The standard JavaScript/TypeScript error handling approach.
|
|
18
|
+
|
|
19
|
+
### Basic Try/Catch
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Basic error handling
|
|
23
|
+
async function fetchUser(id: string): Promise<User> {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(`/api/users/${id}`);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
28
|
+
}
|
|
29
|
+
return await response.json();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Failed to fetch user:', error);
|
|
32
|
+
throw error; // Re-throw or handle
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Custom Error Classes
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// Custom error hierarchy
|
|
41
|
+
class AppError extends Error {
|
|
42
|
+
constructor(
|
|
43
|
+
message: string,
|
|
44
|
+
public readonly code: string,
|
|
45
|
+
public readonly statusCode: number = 500
|
|
46
|
+
) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = this.constructor.name;
|
|
49
|
+
Error.captureStackTrace(this, this.constructor);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class ValidationError extends AppError {
|
|
54
|
+
constructor(message: string, public readonly fields: string[]) {
|
|
55
|
+
super(message, 'VALIDATION_ERROR', 400);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class NotFoundError extends AppError {
|
|
60
|
+
constructor(resource: string, id: string) {
|
|
61
|
+
super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class UnauthorizedError extends AppError {
|
|
66
|
+
constructor(message: string = 'Unauthorized') {
|
|
67
|
+
super(message, 'UNAUTHORIZED', 401);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Usage
|
|
72
|
+
async function getUser(id: string): Promise<User> {
|
|
73
|
+
const user = await userRepository.findById(id);
|
|
74
|
+
if (!user) {
|
|
75
|
+
throw new NotFoundError('User', id);
|
|
76
|
+
}
|
|
77
|
+
return user;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function createUser(data: CreateUserDto): Promise<User> {
|
|
81
|
+
const errors: string[] = [];
|
|
82
|
+
|
|
83
|
+
if (!data.email) errors.push('email');
|
|
84
|
+
if (!data.name) errors.push('name');
|
|
85
|
+
|
|
86
|
+
if (errors.length > 0) {
|
|
87
|
+
throw new ValidationError('Missing required fields', errors);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return await userRepository.save(data);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Error Handling Middleware (Express)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// error-handler.middleware.ts
|
|
98
|
+
import { Request, Response, NextFunction } from 'express';
|
|
99
|
+
|
|
100
|
+
export function errorHandler(
|
|
101
|
+
error: Error,
|
|
102
|
+
req: Request,
|
|
103
|
+
res: Response,
|
|
104
|
+
next: NextFunction
|
|
105
|
+
) {
|
|
106
|
+
// Log error
|
|
107
|
+
console.error('Error:', error);
|
|
108
|
+
|
|
109
|
+
// Handle known errors
|
|
110
|
+
if (error instanceof AppError) {
|
|
111
|
+
return res.status(error.statusCode).json({
|
|
112
|
+
error: {
|
|
113
|
+
code: error.code,
|
|
114
|
+
message: error.message,
|
|
115
|
+
...(error instanceof ValidationError && { fields: error.fields })
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle unknown errors
|
|
121
|
+
res.status(500).json({
|
|
122
|
+
error: {
|
|
123
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
124
|
+
message: 'An unexpected error occurred'
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Usage in Express app
|
|
130
|
+
app.use(errorHandler);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Async Error Handling
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Wrapper for async route handlers
|
|
137
|
+
function asyncHandler(
|
|
138
|
+
fn: (req: Request, res: Response, next: NextFunction) => Promise<any>
|
|
139
|
+
) {
|
|
140
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
141
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Usage
|
|
146
|
+
app.get('/users/:id', asyncHandler(async (req, res) => {
|
|
147
|
+
const user = await getUser(req.params.id); // Errors automatically caught
|
|
148
|
+
res.json(user);
|
|
149
|
+
}));
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Pros and Cons of Try/Catch
|
|
153
|
+
|
|
154
|
+
**Pros:**
|
|
155
|
+
- ✅ Familiar to most developers
|
|
156
|
+
- ✅ Built into JavaScript/TypeScript
|
|
157
|
+
- ✅ Works with async/await
|
|
158
|
+
- ✅ Stack traces included
|
|
159
|
+
|
|
160
|
+
**Cons:**
|
|
161
|
+
- ❌ Errors not visible in type signatures
|
|
162
|
+
- ❌ Easy to forget error handling
|
|
163
|
+
- ❌ Can't enforce error handling at compile time
|
|
164
|
+
- ❌ Difficult to compose error-prone operations
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Result Types
|
|
169
|
+
|
|
170
|
+
Explicit error handling using discriminated unions.
|
|
171
|
+
|
|
172
|
+
### Basic Result Type
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// result.ts
|
|
176
|
+
type Result<T, E = Error> =
|
|
177
|
+
| { success: true; value: T }
|
|
178
|
+
| { success: false; error: E };
|
|
179
|
+
|
|
180
|
+
// Helper functions
|
|
181
|
+
function ok<T>(value: T): Result<T, never> {
|
|
182
|
+
return { success: true, value };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function err<E>(error: E): Result<never, E> {
|
|
186
|
+
return { success: false, error };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Usage
|
|
190
|
+
function divide(a: number, b: number): Result<number, string> {
|
|
191
|
+
if (b === 0) {
|
|
192
|
+
return err('Division by zero');
|
|
193
|
+
}
|
|
194
|
+
return ok(a / b);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Pattern matching
|
|
198
|
+
const result = divide(10, 2);
|
|
199
|
+
if (result.success) {
|
|
200
|
+
console.log('Result:', result.value); // TypeScript knows value exists
|
|
201
|
+
} else {
|
|
202
|
+
console.error('Error:', result.error); // TypeScript knows error exists
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Advanced Result Type
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// result.ts
|
|
210
|
+
export class Result<T, E = Error> {
|
|
211
|
+
private constructor(
|
|
212
|
+
private readonly _success: boolean,
|
|
213
|
+
private readonly _value?: T,
|
|
214
|
+
private readonly _error?: E
|
|
215
|
+
) {}
|
|
216
|
+
|
|
217
|
+
static ok<T>(value: T): Result<T, never> {
|
|
218
|
+
return new Result(true, value);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static err<E>(error: E): Result<never, E> {
|
|
222
|
+
return new Result(false, undefined, error);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
isOk(): this is Result<T, never> {
|
|
226
|
+
return this._success;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
isErr(): this is Result<never, E> {
|
|
230
|
+
return !this._success;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
unwrap(): T {
|
|
234
|
+
if (!this._success) {
|
|
235
|
+
throw new Error('Called unwrap on an Err value');
|
|
236
|
+
}
|
|
237
|
+
return this._value!;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
unwrapOr(defaultValue: T): T {
|
|
241
|
+
return this._success ? this._value! : defaultValue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
map<U>(fn: (value: T) => U): Result<U, E> {
|
|
245
|
+
return this._success
|
|
246
|
+
? Result.ok(fn(this._value!))
|
|
247
|
+
: Result.err(this._error!);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
mapErr<F>(fn: (error: E) => F): Result<T, F> {
|
|
251
|
+
return this._success
|
|
252
|
+
? Result.ok(this._value!)
|
|
253
|
+
: Result.err(fn(this._error!));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
andThen<U>(fn: (value: T) => Result<U, E>): Result<U, E> {
|
|
257
|
+
return this._success ? fn(this._value!) : Result.err(this._error!);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
match<U>(onOk: (value: T) => U, onErr: (error: E) => U): U {
|
|
261
|
+
return this._success ? onOk(this._value!) : onErr(this._error!);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Usage examples
|
|
266
|
+
function parseNumber(input: string): Result<number, string> {
|
|
267
|
+
const num = Number(input);
|
|
268
|
+
return isNaN(num)
|
|
269
|
+
? Result.err('Invalid number')
|
|
270
|
+
: Result.ok(num);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function divide(a: number, b: number): Result<number, string> {
|
|
274
|
+
return b === 0
|
|
275
|
+
? Result.err('Division by zero')
|
|
276
|
+
: Result.ok(a / b);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Chaining operations
|
|
280
|
+
const result = parseNumber('10')
|
|
281
|
+
.andThen(num => divide(num, 2))
|
|
282
|
+
.map(result => result * 2);
|
|
283
|
+
|
|
284
|
+
result.match(
|
|
285
|
+
value => console.log('Success:', value),
|
|
286
|
+
error => console.error('Error:', error)
|
|
287
|
+
);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Neverthrow Pattern
|
|
293
|
+
|
|
294
|
+
Neverthrow is a popular library for Result types in TypeScript.
|
|
295
|
+
|
|
296
|
+
### Installation
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
npm install neverthrow
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Basic Usage
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { ok, err, Result } from 'neverthrow';
|
|
306
|
+
|
|
307
|
+
// Define error types
|
|
308
|
+
type DivisionError = 'DIVISION_BY_ZERO';
|
|
309
|
+
type ParseError = 'INVALID_NUMBER';
|
|
310
|
+
|
|
311
|
+
function parseNumber(input: string): Result<number, ParseError> {
|
|
312
|
+
const num = Number(input);
|
|
313
|
+
return isNaN(num) ? err('INVALID_NUMBER') : ok(num);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function divide(a: number, b: number): Result<number, DivisionError> {
|
|
317
|
+
return b === 0 ? err('DIVISION_BY_ZERO') : ok(a / b);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Chaining with andThen
|
|
321
|
+
const result = parseNumber('10')
|
|
322
|
+
.andThen(num => divide(num, 2));
|
|
323
|
+
|
|
324
|
+
if (result.isOk()) {
|
|
325
|
+
console.log('Result:', result.value);
|
|
326
|
+
} else {
|
|
327
|
+
console.error('Error:', result.error);
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Async Operations with ResultAsync
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { okAsync, errAsync, ResultAsync } from 'neverthrow';
|
|
335
|
+
|
|
336
|
+
type UserNotFoundError = { type: 'USER_NOT_FOUND'; id: string };
|
|
337
|
+
type DatabaseError = { type: 'DATABASE_ERROR'; message: string };
|
|
338
|
+
|
|
339
|
+
async function fetchUser(id: string): ResultAsync<User, UserNotFoundError | DatabaseError> {
|
|
340
|
+
try {
|
|
341
|
+
const response = await fetch(`/api/users/${id}`);
|
|
342
|
+
|
|
343
|
+
if (response.status === 404) {
|
|
344
|
+
return errAsync({ type: 'USER_NOT_FOUND', id });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!response.ok) {
|
|
348
|
+
return errAsync({
|
|
349
|
+
type: 'DATABASE_ERROR',
|
|
350
|
+
message: `HTTP ${response.status}`
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const user = await response.json();
|
|
355
|
+
return okAsync(user);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
return errAsync({
|
|
358
|
+
type: 'DATABASE_ERROR',
|
|
359
|
+
message: error.message
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Usage
|
|
365
|
+
const userResult = await fetchUser('123');
|
|
366
|
+
|
|
367
|
+
userResult.match(
|
|
368
|
+
user => console.log('User:', user),
|
|
369
|
+
error => {
|
|
370
|
+
if (error.type === 'USER_NOT_FOUND') {
|
|
371
|
+
console.error('User not found:', error.id);
|
|
372
|
+
} else {
|
|
373
|
+
console.error('Database error:', error.message);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Combining Multiple Results
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
import { Result, combine } from 'neverthrow';
|
|
383
|
+
|
|
384
|
+
type ValidationError = { field: string; message: string };
|
|
385
|
+
|
|
386
|
+
function validateEmail(email: string): Result<string, ValidationError> {
|
|
387
|
+
return email.includes('@')
|
|
388
|
+
? ok(email)
|
|
389
|
+
: err({ field: 'email', message: 'Invalid email format' });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function validateAge(age: number): Result<number, ValidationError> {
|
|
393
|
+
return age >= 18
|
|
394
|
+
? ok(age)
|
|
395
|
+
: err({ field: 'age', message: 'Must be 18 or older' });
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function validateName(name: string): Result<string, ValidationError> {
|
|
399
|
+
return name.length >= 2
|
|
400
|
+
? ok(name)
|
|
401
|
+
: err({ field: 'name', message: 'Name too short' });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Combine all validations
|
|
405
|
+
function validateUser(data: {
|
|
406
|
+
email: string;
|
|
407
|
+
age: number;
|
|
408
|
+
name: string;
|
|
409
|
+
}): Result<{ email: string; age: number; name: string }, ValidationError[]> {
|
|
410
|
+
const results = [
|
|
411
|
+
validateEmail(data.email),
|
|
412
|
+
validateAge(data.age),
|
|
413
|
+
validateName(data.name)
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
return combine(results).map(([email, age, name]) => ({
|
|
417
|
+
email,
|
|
418
|
+
age,
|
|
419
|
+
name
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Usage
|
|
424
|
+
const result = validateUser({
|
|
425
|
+
email: 'invalid-email',
|
|
426
|
+
age: 16,
|
|
427
|
+
name: 'J'
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
if (result.isErr()) {
|
|
431
|
+
console.error('Validation errors:', result.error);
|
|
432
|
+
// [
|
|
433
|
+
// { field: 'email', message: 'Invalid email format' },
|
|
434
|
+
// { field: 'age', message: 'Must be 18 or older' },
|
|
435
|
+
// { field: 'name', message: 'Name too short' }
|
|
436
|
+
// ]
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Service Layer with Neverthrow
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import { Result, ok, err, ResultAsync } from 'neverthrow';
|
|
444
|
+
|
|
445
|
+
type UserError =
|
|
446
|
+
| { type: 'NOT_FOUND'; id: string }
|
|
447
|
+
| { type: 'ALREADY_EXISTS'; email: string }
|
|
448
|
+
| { type: 'VALIDATION_ERROR'; errors: ValidationError[] }
|
|
449
|
+
| { type: 'DATABASE_ERROR'; message: string };
|
|
450
|
+
|
|
451
|
+
class UserService {
|
|
452
|
+
constructor(
|
|
453
|
+
private repository: UserRepository,
|
|
454
|
+
private emailService: EmailService
|
|
455
|
+
) {}
|
|
456
|
+
|
|
457
|
+
async createUser(
|
|
458
|
+
data: CreateUserDto
|
|
459
|
+
): ResultAsync<User, UserError> {
|
|
460
|
+
// Validate input
|
|
461
|
+
const validationResult = this.validateUserData(data);
|
|
462
|
+
if (validationResult.isErr()) {
|
|
463
|
+
return errAsync({
|
|
464
|
+
type: 'VALIDATION_ERROR',
|
|
465
|
+
errors: validationResult.error
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Check if user exists
|
|
470
|
+
const existingUser = await this.repository.findByEmail(data.email);
|
|
471
|
+
if (existingUser.isOk() && existingUser.value !== null) {
|
|
472
|
+
return errAsync({
|
|
473
|
+
type: 'ALREADY_EXISTS',
|
|
474
|
+
email: data.email
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Create user
|
|
479
|
+
const user = User.create(data);
|
|
480
|
+
const saveResult = await this.repository.save(user);
|
|
481
|
+
|
|
482
|
+
if (saveResult.isErr()) {
|
|
483
|
+
return errAsync({
|
|
484
|
+
type: 'DATABASE_ERROR',
|
|
485
|
+
message: saveResult.error
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Send welcome email (don't fail if email fails)
|
|
490
|
+
await this.emailService.sendWelcome(user.email);
|
|
491
|
+
|
|
492
|
+
return okAsync(user);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async getUser(id: string): ResultAsync<User, UserError> {
|
|
496
|
+
const result = await this.repository.findById(id);
|
|
497
|
+
|
|
498
|
+
return result.andThen(user =>
|
|
499
|
+
user === null
|
|
500
|
+
? err({ type: 'NOT_FOUND', id })
|
|
501
|
+
: ok(user)
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
private validateUserData(
|
|
506
|
+
data: CreateUserDto
|
|
507
|
+
): Result<void, ValidationError[]> {
|
|
508
|
+
const errors: ValidationError[] = [];
|
|
509
|
+
|
|
510
|
+
if (!data.email || !data.email.includes('@')) {
|
|
511
|
+
errors.push({ field: 'email', message: 'Invalid email' });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (!data.name || data.name.length < 2) {
|
|
515
|
+
errors.push({ field: 'name', message: 'Name too short' });
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return errors.length > 0 ? err(errors) : ok(undefined);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Controller with Neverthrow
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
import { Request, Response } from 'express';
|
|
527
|
+
|
|
528
|
+
class UserController {
|
|
529
|
+
constructor(private userService: UserService) {}
|
|
530
|
+
|
|
531
|
+
async create(req: Request, res: Response): Promise<void> {
|
|
532
|
+
const result = await this.userService.createUser(req.body);
|
|
533
|
+
|
|
534
|
+
result.match(
|
|
535
|
+
user => res.status(201).json(user),
|
|
536
|
+
error => {
|
|
537
|
+
switch (error.type) {
|
|
538
|
+
case 'VALIDATION_ERROR':
|
|
539
|
+
res.status(400).json({ errors: error.errors });
|
|
540
|
+
break;
|
|
541
|
+
case 'ALREADY_EXISTS':
|
|
542
|
+
res.status(409).json({ message: `Email ${error.email} already exists` });
|
|
543
|
+
break;
|
|
544
|
+
case 'DATABASE_ERROR':
|
|
545
|
+
res.status(500).json({ message: 'Internal server error' });
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async get(req: Request, res: Response): Promise<void> {
|
|
553
|
+
const result = await this.userService.getUser(req.params.id);
|
|
554
|
+
|
|
555
|
+
result.match(
|
|
556
|
+
user => res.json(user),
|
|
557
|
+
error => {
|
|
558
|
+
if (error.type === 'NOT_FOUND') {
|
|
559
|
+
res.status(404).json({ message: `User ${error.id} not found` });
|
|
560
|
+
} else {
|
|
561
|
+
res.status(500).json({ message: 'Internal server error' });
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Effect-TS Introduction
|
|
572
|
+
|
|
573
|
+
Effect-TS is a powerful library for functional error handling and effect management.
|
|
574
|
+
|
|
575
|
+
### Installation
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
npm install effect
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Basic Effect Usage
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
import { Effect } from 'effect';
|
|
585
|
+
|
|
586
|
+
// Define error types
|
|
587
|
+
class DivisionByZeroError {
|
|
588
|
+
readonly _tag = 'DivisionByZeroError';
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
class InvalidInputError {
|
|
592
|
+
readonly _tag = 'InvalidInputError';
|
|
593
|
+
constructor(readonly input: string) {}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Create effects
|
|
597
|
+
const divide = (a: number, b: number): Effect.Effect<number, DivisionByZeroError> =>
|
|
598
|
+
b === 0
|
|
599
|
+
? Effect.fail(new DivisionByZeroError())
|
|
600
|
+
: Effect.succeed(a / b);
|
|
601
|
+
|
|
602
|
+
const parseNumber = (input: string): Effect.Effect<number, InvalidInputError> => {
|
|
603
|
+
const num = Number(input);
|
|
604
|
+
return isNaN(num)
|
|
605
|
+
? Effect.fail(new InvalidInputError(input))
|
|
606
|
+
: Effect.succeed(num);
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// Compose effects
|
|
610
|
+
const program = Effect.gen(function* (_) {
|
|
611
|
+
const a = yield* _(parseNumber('10'));
|
|
612
|
+
const b = yield* _(parseNumber('2'));
|
|
613
|
+
const result = yield* _(divide(a, b));
|
|
614
|
+
return result;
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// Run effect
|
|
618
|
+
Effect.runPromise(program)
|
|
619
|
+
.then(result => console.log('Result:', result))
|
|
620
|
+
.catch(error => console.error('Error:', error));
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Effect with Dependencies
|
|
624
|
+
|
|
625
|
+
```typescript
|
|
626
|
+
import { Effect, Context } from 'effect';
|
|
627
|
+
|
|
628
|
+
// Define service interfaces
|
|
629
|
+
class Database extends Context.Tag('Database')<
|
|
630
|
+
Database,
|
|
631
|
+
{
|
|
632
|
+
query: (sql: string) => Effect.Effect<any[], DatabaseError>;
|
|
633
|
+
}
|
|
634
|
+
>() {}
|
|
635
|
+
|
|
636
|
+
class Logger extends Context.Tag('Logger')<
|
|
637
|
+
Logger,
|
|
638
|
+
{
|
|
639
|
+
log: (message: string) => Effect.Effect<void>;
|
|
640
|
+
}
|
|
641
|
+
>() {}
|
|
642
|
+
|
|
643
|
+
// Define errors
|
|
644
|
+
class DatabaseError {
|
|
645
|
+
readonly _tag = 'DatabaseError';
|
|
646
|
+
constructor(readonly message: string) {}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
class UserNotFoundError {
|
|
650
|
+
readonly _tag = 'UserNotFoundError';
|
|
651
|
+
constructor(readonly id: string) {}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Use services in effects
|
|
655
|
+
const getUser = (id: string): Effect.Effect<
|
|
656
|
+
User,
|
|
657
|
+
DatabaseError | UserNotFoundError,
|
|
658
|
+
Database | Logger
|
|
659
|
+
> =>
|
|
660
|
+
Effect.gen(function* (_) {
|
|
661
|
+
const db = yield* _(Database);
|
|
662
|
+
const logger = yield* _(Logger);
|
|
663
|
+
|
|
664
|
+
yield* _(logger.log(`Fetching user ${id}`));
|
|
665
|
+
|
|
666
|
+
const rows = yield* _(db.query(`SELECT * FROM users WHERE id = '${id}'`));
|
|
667
|
+
|
|
668
|
+
if (rows.length === 0) {
|
|
669
|
+
return yield* _(Effect.fail(new UserNotFoundError(id)));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return rows[0] as User;
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
// Provide implementations
|
|
676
|
+
const DatabaseLive = Database.of({
|
|
677
|
+
query: (sql: string) =>
|
|
678
|
+
Effect.tryPromise({
|
|
679
|
+
try: () => database.query(sql),
|
|
680
|
+
catch: (error) => new DatabaseError(String(error))
|
|
681
|
+
})
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
const LoggerLive = Logger.of({
|
|
685
|
+
log: (message: string) =>
|
|
686
|
+
Effect.sync(() => console.log(message))
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// Run with dependencies
|
|
690
|
+
const program = getUser('123').pipe(
|
|
691
|
+
Effect.provide(DatabaseLive),
|
|
692
|
+
Effect.provide(LoggerLive)
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
Effect.runPromise(program);
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## Error Boundary Patterns
|
|
701
|
+
|
|
702
|
+
Error boundaries catch errors in React component trees.
|
|
703
|
+
|
|
704
|
+
### React Error Boundary
|
|
705
|
+
|
|
706
|
+
```typescript
|
|
707
|
+
// error-boundary.tsx
|
|
708
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
709
|
+
|
|
710
|
+
interface Props {
|
|
711
|
+
children: ReactNode;
|
|
712
|
+
fallback?: ReactNode;
|
|
713
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
interface State {
|
|
717
|
+
hasError: boolean;
|
|
718
|
+
error?: Error;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
722
|
+
constructor(props: Props) {
|
|
723
|
+
super(props);
|
|
724
|
+
this.state = { hasError: false };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
static getDerivedStateFromError(error: Error): State {
|
|
728
|
+
return { hasError: true, error };
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
|
732
|
+
console.error('Error caught by boundary:', error, errorInfo);
|
|
733
|
+
this.props.onError?.(error, errorInfo);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
render() {
|
|
737
|
+
if (this.state.hasError) {
|
|
738
|
+
return this.props.fallback || (
|
|
739
|
+
<div>
|
|
740
|
+
<h1>Something went wrong</h1>
|
|
741
|
+
<p>{this.state.error?.message}</p>
|
|
742
|
+
</div>
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return this.props.children;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Usage
|
|
751
|
+
function App() {
|
|
752
|
+
return (
|
|
753
|
+
<ErrorBoundary
|
|
754
|
+
fallback={<ErrorFallback />}
|
|
755
|
+
onError={(error, errorInfo) => {
|
|
756
|
+
// Log to error tracking service
|
|
757
|
+
logErrorToService(error, errorInfo);
|
|
758
|
+
}}
|
|
759
|
+
>
|
|
760
|
+
<MyComponent />
|
|
761
|
+
</ErrorBoundary>
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### Advanced Error Boundary with Reset
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
// resettable-error-boundary.tsx
|
|
770
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
771
|
+
|
|
772
|
+
interface Props {
|
|
773
|
+
children: ReactNode;
|
|
774
|
+
fallback: (error: Error, reset: () => void) => ReactNode;
|
|
775
|
+
onError?: (error: Error, errorInfo: ErrorInfo) => void;
|
|
776
|
+
onReset?: () => void;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
interface State {
|
|
780
|
+
hasError: boolean;
|
|
781
|
+
error?: Error;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
export class ResettableErrorBoundary extends Component<Props, State> {
|
|
785
|
+
constructor(props: Props) {
|
|
786
|
+
super(props);
|
|
787
|
+
this.state = { hasError: false };
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
static getDerivedStateFromError(error: Error): State {
|
|
791
|
+
return { hasError: true, error };
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
|
795
|
+
this.props.onError?.(error, errorInfo);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
reset = (): void => {
|
|
799
|
+
this.props.onReset?.();
|
|
800
|
+
this.setState({ hasError: false, error: undefined });
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
render() {
|
|
804
|
+
if (this.state.hasError && this.state.error) {
|
|
805
|
+
return this.props.fallback(this.state.error, this.reset);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return this.props.children;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Usage
|
|
813
|
+
function App() {
|
|
814
|
+
return (
|
|
815
|
+
<ResettableErrorBoundary
|
|
816
|
+
fallback={(error, reset) => (
|
|
817
|
+
<div>
|
|
818
|
+
<h1>Error: {error.message}</h1>
|
|
819
|
+
<button onClick={reset}>Try Again</button>
|
|
820
|
+
</div>
|
|
821
|
+
)}
|
|
822
|
+
onReset={() => {
|
|
823
|
+
// Clear any error state
|
|
824
|
+
window.location.reload();
|
|
825
|
+
}}
|
|
826
|
+
>
|
|
827
|
+
<MyComponent />
|
|
828
|
+
</ResettableErrorBoundary>
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
### Error Boundary with React Query
|
|
834
|
+
|
|
835
|
+
```typescript
|
|
836
|
+
// query-error-boundary.tsx
|
|
837
|
+
import { QueryErrorResetBoundary } from '@tanstack/react-query';
|
|
838
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
839
|
+
|
|
840
|
+
function QueryErrorBoundary({ children }: { children: ReactNode }) {
|
|
841
|
+
return (
|
|
842
|
+
<QueryErrorResetBoundary>
|
|
843
|
+
{({ reset }) => (
|
|
844
|
+
<ErrorBoundary
|
|
845
|
+
onReset={reset}
|
|
846
|
+
fallbackRender={({ error, resetErrorBoundary }) => (
|
|
847
|
+
<div>
|
|
848
|
+
<h1>Error loading data</h1>
|
|
849
|
+
<p>{error.message}</p>
|
|
850
|
+
<button onClick={resetErrorBoundary}>Retry</button>
|
|
851
|
+
</div>
|
|
852
|
+
)}
|
|
853
|
+
>
|
|
854
|
+
{children}
|
|
855
|
+
</ErrorBoundary>
|
|
856
|
+
)}
|
|
857
|
+
</QueryErrorResetBoundary>
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Usage
|
|
862
|
+
function App() {
|
|
863
|
+
return (
|
|
864
|
+
<QueryErrorBoundary>
|
|
865
|
+
<UserProfile userId="123" />
|
|
866
|
+
</QueryErrorBoundary>
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
### Multiple Error Boundaries
|
|
872
|
+
|
|
873
|
+
```typescript
|
|
874
|
+
// app.tsx
|
|
875
|
+
function App() {
|
|
876
|
+
return (
|
|
877
|
+
<ErrorBoundary fallback={<AppErrorFallback />}>
|
|
878
|
+
<Header />
|
|
879
|
+
|
|
880
|
+
<ErrorBoundary fallback={<SidebarErrorFallback />}>
|
|
881
|
+
<Sidebar />
|
|
882
|
+
</ErrorBoundary>
|
|
883
|
+
|
|
884
|
+
<main>
|
|
885
|
+
<ErrorBoundary fallback={<ContentErrorFallback />}>
|
|
886
|
+
<Content />
|
|
887
|
+
</ErrorBoundary>
|
|
888
|
+
</main>
|
|
889
|
+
|
|
890
|
+
<Footer />
|
|
891
|
+
</ErrorBoundary>
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### Error Boundary Best Practices
|
|
897
|
+
|
|
898
|
+
```typescript
|
|
899
|
+
// ✅ Good - Granular error boundaries
|
|
900
|
+
function Dashboard() {
|
|
901
|
+
return (
|
|
902
|
+
<div>
|
|
903
|
+
<ErrorBoundary fallback={<WidgetError />}>
|
|
904
|
+
<UserWidget />
|
|
905
|
+
</ErrorBoundary>
|
|
906
|
+
|
|
907
|
+
<ErrorBoundary fallback={<WidgetError />}>
|
|
908
|
+
<StatsWidget />
|
|
909
|
+
</ErrorBoundary>
|
|
910
|
+
|
|
911
|
+
<ErrorBoundary fallback={<WidgetError />}>
|
|
912
|
+
<ChartWidget />
|
|
913
|
+
</ErrorBoundary>
|
|
914
|
+
</div>
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// ❌ Bad - Single error boundary for entire app
|
|
919
|
+
function App() {
|
|
920
|
+
return (
|
|
921
|
+
<ErrorBoundary fallback={<div>Error</div>}>
|
|
922
|
+
<Dashboard />
|
|
923
|
+
</ErrorBoundary>
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
---
|
|
929
|
+
|
|
930
|
+
## Best Practices
|
|
931
|
+
|
|
932
|
+
### DO ✅
|
|
933
|
+
|
|
934
|
+
**Use custom error classes**
|
|
935
|
+
```typescript
|
|
936
|
+
// ✅ Good - Custom error hierarchy
|
|
937
|
+
class AppError extends Error {
|
|
938
|
+
constructor(message: string, public code: string) {
|
|
939
|
+
super(message);
|
|
940
|
+
this.name = this.constructor.name;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
class ValidationError extends AppError {
|
|
945
|
+
constructor(message: string, public fields: string[]) {
|
|
946
|
+
super(message, 'VALIDATION_ERROR');
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// ❌ Bad - Generic errors
|
|
951
|
+
throw new Error('Something went wrong');
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
**Make errors explicit in types (Result pattern)**
|
|
955
|
+
```typescript
|
|
956
|
+
// ✅ Good - Error in type signature
|
|
957
|
+
function divide(a: number, b: number): Result<number, DivisionError> {
|
|
958
|
+
return b === 0 ? err('DIVISION_BY_ZERO') : ok(a / b);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// ❌ Bad - Hidden error
|
|
962
|
+
function divide(a: number, b: number): number {
|
|
963
|
+
if (b === 0) throw new Error('Division by zero'); // Not visible in type
|
|
964
|
+
return a / b;
|
|
965
|
+
}
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
**Handle errors at appropriate level**
|
|
969
|
+
```typescript
|
|
970
|
+
// ✅ Good - Handle at controller level
|
|
971
|
+
class UserController {
|
|
972
|
+
async create(req: Request, res: Response) {
|
|
973
|
+
const result = await this.userService.createUser(req.body);
|
|
974
|
+
|
|
975
|
+
result.match(
|
|
976
|
+
user => res.status(201).json(user),
|
|
977
|
+
error => res.status(400).json({ error: error.message })
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// ❌ Bad - Swallow errors
|
|
983
|
+
class UserService {
|
|
984
|
+
async createUser(data: CreateUserDto) {
|
|
985
|
+
try {
|
|
986
|
+
return await this.repository.save(data);
|
|
987
|
+
} catch (error) {
|
|
988
|
+
console.log(error); // Just logging, not handling
|
|
989
|
+
return null; // Hiding the error
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
**Provide context in errors**
|
|
996
|
+
```typescript
|
|
997
|
+
// ✅ Good - Detailed error context
|
|
998
|
+
class UserNotFoundError extends AppError {
|
|
999
|
+
constructor(
|
|
1000
|
+
public readonly userId: string,
|
|
1001
|
+
public readonly searchedBy: 'id' | 'email'
|
|
1002
|
+
) {
|
|
1003
|
+
super(
|
|
1004
|
+
`User not found: ${searchedBy}=${userId}`,
|
|
1005
|
+
'USER_NOT_FOUND'
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// ❌ Bad - Generic error
|
|
1011
|
+
throw new Error('User not found');
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
**Use error boundaries in React**
|
|
1015
|
+
```typescript
|
|
1016
|
+
// ✅ Good - Error boundary for each major section
|
|
1017
|
+
function App() {
|
|
1018
|
+
return (
|
|
1019
|
+
<>
|
|
1020
|
+
<ErrorBoundary fallback={<HeaderError />}>
|
|
1021
|
+
<Header />
|
|
1022
|
+
</ErrorBoundary>
|
|
1023
|
+
|
|
1024
|
+
<ErrorBoundary fallback={<ContentError />}>
|
|
1025
|
+
<Content />
|
|
1026
|
+
</ErrorBoundary>
|
|
1027
|
+
</>
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// ❌ Bad - No error boundaries
|
|
1032
|
+
function App() {
|
|
1033
|
+
return (
|
|
1034
|
+
<>
|
|
1035
|
+
<Header />
|
|
1036
|
+
<Content />
|
|
1037
|
+
</>
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
### DON'T ❌
|
|
1043
|
+
|
|
1044
|
+
**Don't swallow errors**
|
|
1045
|
+
```typescript
|
|
1046
|
+
// ❌ Bad - Swallowing errors
|
|
1047
|
+
try {
|
|
1048
|
+
await riskyOperation();
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
// Silent failure
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// ✅ Good - Handle or re-throw
|
|
1054
|
+
try {
|
|
1055
|
+
await riskyOperation();
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
logger.error('Operation failed:', error);
|
|
1058
|
+
throw error; // Re-throw if can't handle
|
|
1059
|
+
}
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**Don't use errors for control flow**
|
|
1063
|
+
```typescript
|
|
1064
|
+
// ❌ Bad - Using exceptions for control flow
|
|
1065
|
+
function findUser(id: string): User {
|
|
1066
|
+
try {
|
|
1067
|
+
return database.query(`SELECT * FROM users WHERE id = ${id}`)[0];
|
|
1068
|
+
} catch {
|
|
1069
|
+
return null; // Using exception for "not found"
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// ✅ Good - Return null or Result type
|
|
1074
|
+
function findUser(id: string): User | null {
|
|
1075
|
+
const users = database.query(`SELECT * FROM users WHERE id = ${id}`);
|
|
1076
|
+
return users[0] || null;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// ✅ Better - Use Result type
|
|
1080
|
+
function findUser(id: string): Result<User, UserNotFoundError> {
|
|
1081
|
+
const users = database.query(`SELECT * FROM users WHERE id = ${id}`);
|
|
1082
|
+
return users[0] ? ok(users[0]) : err(new UserNotFoundError(id));
|
|
1083
|
+
}
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
**Don't catch errors you can't handle**
|
|
1087
|
+
```typescript
|
|
1088
|
+
// ❌ Bad - Catching everything
|
|
1089
|
+
try {
|
|
1090
|
+
await complexOperation();
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
console.log('Error:', error); // Can't actually handle it
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// ✅ Good - Only catch what you can handle
|
|
1096
|
+
try {
|
|
1097
|
+
await complexOperation();
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
if (error instanceof ValidationError) {
|
|
1100
|
+
// Handle validation errors
|
|
1101
|
+
return { errors: error.fields };
|
|
1102
|
+
}
|
|
1103
|
+
throw error; // Re-throw unknown errors
|
|
1104
|
+
}
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
**Don't lose error context**
|
|
1108
|
+
```typescript
|
|
1109
|
+
// ❌ Bad - Losing original error
|
|
1110
|
+
try {
|
|
1111
|
+
await fetchData();
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
throw new Error('Failed to fetch'); // Lost original error
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// ✅ Good - Preserve error context
|
|
1117
|
+
try {
|
|
1118
|
+
await fetchData();
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
throw new Error(`Failed to fetch: ${error.message}`, { cause: error });
|
|
1121
|
+
}
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
---
|
|
1125
|
+
|
|
1126
|
+
## Summary
|
|
1127
|
+
|
|
1128
|
+
**Key Takeaways:**
|
|
1129
|
+
|
|
1130
|
+
1. **Choose the right approach** - Try/catch for simple cases, Result types for explicit error handling
|
|
1131
|
+
2. **Make errors explicit** - Use Result types or custom error classes
|
|
1132
|
+
3. **Use custom error classes** - Create error hierarchies for different error types
|
|
1133
|
+
4. **Handle errors at appropriate level** - Don't swallow errors, handle where you can
|
|
1134
|
+
5. **Provide error context** - Include relevant information in error messages
|
|
1135
|
+
6. **Use error boundaries in React** - Prevent entire app crashes
|
|
1136
|
+
7. **Consider Neverthrow** - For functional error handling with Result types
|
|
1137
|
+
8. **Consider Effect-TS** - For advanced effect management and dependency injection
|
|
1138
|
+
9. **Don't use errors for control flow** - Use return values or Result types
|
|
1139
|
+
10. **Log errors appropriately** - Include context and stack traces
|
|
1140
|
+
|
|
1141
|
+
**Error Handling Checklist:**
|
|
1142
|
+
|
|
1143
|
+
- [ ] Custom error classes defined
|
|
1144
|
+
- [ ] Errors include context (IDs, parameters, etc.)
|
|
1145
|
+
- [ ] Error handling at appropriate level
|
|
1146
|
+
- [ ] No swallowed errors
|
|
1147
|
+
- [ ] Error boundaries in React components
|
|
1148
|
+
- [ ] Errors logged with sufficient detail
|
|
1149
|
+
- [ ] Type signatures reflect possible errors (Result types)
|
|
1150
|
+
- [ ] Error messages are user-friendly
|
|
1151
|
+
- [ ] Stack traces preserved
|
|
1152
|
+
- [ ] Error tracking service integrated
|
|
1153
|
+
|
|
1154
|
+
**When to Use Each Approach:**
|
|
1155
|
+
|
|
1156
|
+
| Approach | Use When |
|
|
1157
|
+
|----------|----------|
|
|
1158
|
+
| Try/Catch | Simple error handling, async/await, familiar to team |
|
|
1159
|
+
| Result Types | Want explicit errors in types, functional style, composable operations |
|
|
1160
|
+
| Neverthrow | Need Result types with good TypeScript support |
|
|
1161
|
+
| Effect-TS | Complex effect management, dependency injection, advanced functional programming |
|
|
1162
|
+
| Error Boundaries | React applications, prevent UI crashes |
|
|
1163
|
+
|
|
1164
|
+
---
|
|
1165
|
+
|
|
1166
|
+
## References
|
|
1167
|
+
|
|
1168
|
+
- [Neverthrow Documentation](https://github.com/supermacro/neverthrow)
|
|
1169
|
+
- [Effect-TS Documentation](https://effect.website/)
|
|
1170
|
+
- [React Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)
|
|
1171
|
+
- [TypeScript Error Handling Best Practices](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
|
|
1172
|
+
- [Functional Error Handling](https://fsharpforfunandprofit.com/posts/recipe-part2/)
|
|
1173
|
+
|
|
1174
|
+
|