buildanything 1.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +9 -3
- package/CHANGELOG.md +57 -0
- package/README.md +2 -2
- package/agents/a11y-architect.md +166 -0
- package/agents/business-model.md +80 -29
- package/agents/code-architect.md +75 -0
- package/agents/code-reviewer.md +255 -0
- package/agents/code-simplifier.md +64 -0
- package/agents/design-brand-guardian.md +293 -53
- package/agents/design-critic.md +139 -0
- package/agents/design-inclusive-visuals-specialist.md +6 -19
- package/agents/design-ui-designer.md +335 -56
- package/agents/design-ux-architect.md +403 -55
- package/agents/design-ux-researcher.md +264 -49
- package/agents/engineering-ai-engineer.md +26 -36
- package/agents/engineering-backend-architect.md +185 -36
- package/agents/engineering-data-engineer.md +225 -43
- package/agents/engineering-devops-automator.md +227 -74
- package/agents/engineering-frontend-developer.md +210 -34
- package/agents/engineering-mobile-app-builder.md +6 -1
- package/agents/engineering-rapid-prototyper.md +30 -9
- package/agents/engineering-security-engineer.md +263 -61
- package/agents/engineering-senior-developer.md +128 -19
- package/agents/engineering-sre.md +84 -0
- package/agents/engineering-technical-writer.md +285 -41
- package/agents/feature-intel.md +110 -0
- package/agents/ios-app-review-guardian.md +19 -2
- package/agents/ios-foundation-models-specialist.md +20 -2
- package/agents/ios-storekit-specialist.md +9 -2
- package/agents/ios-swift-architect.md +28 -1
- package/agents/ios-swift-search.md +8 -1
- package/agents/ios-swift-ui-design.md +33 -1
- package/agents/marketing-app-store-optimizer.md +246 -64
- package/agents/planner.md +216 -0
- package/agents/pr-test-analyzer.md +63 -0
- package/agents/product-feedback-synthesizer.md +8 -2
- package/agents/refactor-cleaner.md +102 -0
- package/agents/security-reviewer.md +128 -0
- package/agents/silent-failure-hunter.md +54 -0
- package/agents/swift-build-resolver.md +119 -0
- package/agents/swift-reviewer.md +112 -0
- package/agents/tech-feasibility.md +21 -1
- package/agents/testing-api-tester.md +236 -59
- package/agents/testing-evidence-collector.md +26 -1
- package/agents/testing-performance-benchmarker.md +21 -1
- package/agents/testing-reality-checker.md +6 -1
- package/agents/visual-research.md +116 -0
- package/bin/adapters/cycle-counter-tool.ts +155 -0
- package/bin/adapters/scribe-tool.ts +71 -0
- package/bin/adapters/state-save-tool.ts +130 -0
- package/bin/adapters/write-lease-tool.ts +127 -0
- package/bin/buildanything-runtime.js +15 -0
- package/bin/buildanything-runtime.ts +328 -0
- package/bin/setup.js +83 -8
- package/commands/add-feature.md +2 -0
- package/commands/build.md +782 -266
- package/commands/fix.md +1 -1
- package/commands/self-check.md +121 -0
- package/commands/setup.md +50 -9
- package/commands/ux-review.md +2 -2
- package/commands/verify.md +6 -9
- package/docs/migration/agents.yaml +729 -0
- package/docs/migration/phase-graph.yaml +1088 -0
- package/docs/migration/sdk-host-compat.md +18 -0
- package/hooks/compile-writer-owner-cache.ts +171 -0
- package/hooks/hooks.json +36 -0
- package/hooks/pre-tool-use +19 -0
- package/hooks/pre-tool-use.ts +776 -0
- package/hooks/record-mode-transitions.ts +178 -0
- package/hooks/session-start +71 -1
- package/hooks/subagent-start +17 -0
- package/hooks/subagent-start.ts +471 -0
- package/hooks/subagent-stop +17 -0
- package/hooks/subagent-stop.ts +153 -0
- package/package.json +24 -4
- package/protocols/architecture-schema.md +171 -0
- package/protocols/decision-log.md +131 -0
- package/protocols/ios-context.md +10 -11
- package/protocols/ios-phase-branches.md +208 -33
- package/protocols/launch-readiness.md +258 -0
- package/protocols/metric-loop.md +62 -2
- package/protocols/smoke-test.md +9 -1
- package/protocols/state-schema.json +388 -0
- package/protocols/state-schema.md +172 -0
- package/protocols/verify.md +62 -2
- package/protocols/visual-dna.md +185 -0
- package/protocols/web-phase-branches.md +222 -72
- package/skills/ios/_VENDORED.md +2 -0
- package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
- package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
- package/skills/ios/hig-components-content/SKILL.md +86 -0
- package/skills/ios/hig-components-content/references/activity-views.md +79 -0
- package/skills/ios/hig-components-content/references/charts.md +180 -0
- package/skills/ios/hig-components-content/references/collections.md +48 -0
- package/skills/ios/hig-components-content/references/color-wells.md +42 -0
- package/skills/ios/hig-components-content/references/image-views.md +82 -0
- package/skills/ios/hig-components-content/references/image-wells.md +34 -0
- package/skills/ios/hig-components-content/references/lockups.md +78 -0
- package/skills/ios/hig-components-content/references/web-views.md +36 -0
- package/skills/ios/hig-components-controls/SKILL.md +88 -0
- package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
- package/skills/ios/hig-components-controls/references/controls.md +112 -0
- package/skills/ios/hig-components-controls/references/gauges.md +74 -0
- package/skills/ios/hig-components-controls/references/labels.md +92 -0
- package/skills/ios/hig-components-controls/references/pickers.md +128 -0
- package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
- package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
- package/skills/ios/hig-components-controls/references/sliders.md +92 -0
- package/skills/ios/hig-components-controls/references/steppers.md +40 -0
- package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
- package/skills/ios/hig-components-controls/references/text-views.md +56 -0
- package/skills/ios/hig-components-controls/references/toggles.md +127 -0
- package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
- package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
- package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
- package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
- package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
- package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
- package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
- package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
- package/skills/ios/hig-components-layout/SKILL.md +99 -0
- package/skills/ios/hig-components-layout/references/boxes.md +48 -0
- package/skills/ios/hig-components-layout/references/column-views.md +44 -0
- package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
- package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
- package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
- package/skills/ios/hig-components-layout/references/panels.md +75 -0
- package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
- package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
- package/skills/ios/hig-components-layout/references/split-views.md +110 -0
- package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
- package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
- package/skills/ios/hig-components-layout/references/windows.md +188 -0
- package/skills/ios/hig-components-menus/SKILL.md +81 -0
- package/skills/ios/hig-components-menus/references/action-button.md +61 -0
- package/skills/ios/hig-components-menus/references/buttons.md +261 -0
- package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
- package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
- package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
- package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
- package/skills/ios/hig-components-menus/references/menus.md +171 -0
- package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
- package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
- package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
- package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
- package/skills/ios/hig-components-search/SKILL.md +68 -0
- package/skills/ios/hig-components-search/references/page-controls.md +120 -0
- package/skills/ios/hig-components-search/references/path-controls.md +40 -0
- package/skills/ios/hig-components-search/references/search-fields.md +189 -0
- package/skills/ios/hig-components-status/SKILL.md +80 -0
- package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
- package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
- package/skills/ios/hig-components-status/references/status-bars.md +38 -0
- package/skills/ios/hig-components-system/SKILL.md +88 -0
- package/skills/ios/hig-components-system/references/app-clips.md +387 -0
- package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
- package/skills/ios/hig-components-system/references/complications.md +425 -0
- package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
- package/skills/ios/hig-components-system/references/live-activities.md +442 -0
- package/skills/ios/hig-components-system/references/notifications.md +153 -0
- package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
- package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
- package/skills/ios/hig-components-system/references/widgets.md +517 -0
- package/skills/ios/hig-foundations/SKILL.md +98 -0
- package/skills/ios/hig-foundations/references/accessibility.md +291 -0
- package/skills/ios/hig-foundations/references/app-icons.md +210 -0
- package/skills/ios/hig-foundations/references/branding.md +44 -0
- package/skills/ios/hig-foundations/references/color.md +274 -0
- package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
- package/skills/ios/hig-foundations/references/icons.md +263 -0
- package/skills/ios/hig-foundations/references/images.md +176 -0
- package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
- package/skills/ios/hig-foundations/references/inclusion.md +189 -0
- package/skills/ios/hig-foundations/references/layout.md +425 -0
- package/skills/ios/hig-foundations/references/materials.md +238 -0
- package/skills/ios/hig-foundations/references/motion.md +103 -0
- package/skills/ios/hig-foundations/references/privacy.md +231 -0
- package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
- package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
- package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
- package/skills/ios/hig-foundations/references/typography.md +1146 -0
- package/skills/ios/hig-foundations/references/writing.md +91 -0
- package/skills/ios/hig-inputs/SKILL.md +94 -0
- package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
- package/skills/ios/hig-inputs/references/camera-control.md +107 -0
- package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
- package/skills/ios/hig-inputs/references/eyes.md +120 -0
- package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
- package/skills/ios/hig-inputs/references/game-controls.md +156 -0
- package/skills/ios/hig-inputs/references/gestures.md +208 -0
- package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
- package/skills/ios/hig-inputs/references/keyboards.md +234 -0
- package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
- package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
- package/skills/ios/hig-inputs/references/remotes.md +67 -0
- package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
- package/skills/ios/hig-patterns/SKILL.md +104 -0
- package/skills/ios/hig-patterns/references/charting-data.md +81 -0
- package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
- package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
- package/skills/ios/hig-patterns/references/entering-data.md +69 -0
- package/skills/ios/hig-patterns/references/feedback.md +67 -0
- package/skills/ios/hig-patterns/references/file-management.md +135 -0
- package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
- package/skills/ios/hig-patterns/references/launching.md +81 -0
- package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
- package/skills/ios/hig-patterns/references/loading.md +59 -0
- package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
- package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
- package/skills/ios/hig-patterns/references/modality.md +82 -0
- package/skills/ios/hig-patterns/references/multitasking.md +131 -0
- package/skills/ios/hig-patterns/references/offering-help.md +117 -0
- package/skills/ios/hig-patterns/references/onboarding.md +69 -0
- package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
- package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
- package/skills/ios/hig-patterns/references/playing-video.md +180 -0
- package/skills/ios/hig-patterns/references/printing.md +50 -0
- package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
- package/skills/ios/hig-patterns/references/searching.md +70 -0
- package/skills/ios/hig-patterns/references/settings.md +84 -0
- package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
- package/skills/ios/hig-patterns/references/workouts.md +76 -0
- package/skills/ios/hig-platforms/SKILL.md +84 -0
- package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
- package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
- package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
- package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
- package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
- package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
- package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
- package/skills/ios/hig-project-context/SKILL.md +133 -0
- package/skills/ios/hig-technologies/SKILL.md +107 -0
- package/skills/ios/hig-technologies/references/airplay.md +125 -0
- package/skills/ios/hig-technologies/references/always-on.md +62 -0
- package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
- package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
- package/skills/ios/hig-technologies/references/carekit.md +224 -0
- package/skills/ios/hig-technologies/references/carplay.md +119 -0
- package/skills/ios/hig-technologies/references/game-center.md +343 -0
- package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
- package/skills/ios/hig-technologies/references/healthkit.md +120 -0
- package/skills/ios/hig-technologies/references/homekit.md +343 -0
- package/skills/ios/hig-technologies/references/icloud.md +52 -0
- package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
- package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
- package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
- package/skills/ios/hig-technologies/references/live-photos.md +54 -0
- package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
- package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
- package/skills/ios/hig-technologies/references/maps.md +221 -0
- package/skills/ios/hig-technologies/references/nfc.md +51 -0
- package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
- package/skills/ios/hig-technologies/references/researchkit.md +134 -0
- package/skills/ios/hig-technologies/references/shareplay.md +142 -0
- package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
- package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
- package/skills/ios/hig-technologies/references/siri.md +523 -0
- package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
- package/skills/ios/hig-technologies/references/voiceover.md +90 -0
- package/skills/ios/hig-technologies/references/wallet.md +420 -0
- package/skills/ios/ios-bootstrap/SKILL.md +16 -7
- package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
- package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
- package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
- package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
- package/skills/web/accessibility/SKILL.md +146 -0
- package/skills/web/aceternity-ui/SKILL.md +719 -0
- package/skills/web/aceternity-ui/metadata.json +10 -0
- package/skills/web/api-design/SKILL.md +523 -0
- package/skills/web/chart-accessibility/SKILL.md +332 -0
- package/skills/web/composition-patterns/AGENTS.md +946 -0
- package/skills/web/composition-patterns/README.md +60 -0
- package/skills/web/composition-patterns/SKILL.md +89 -0
- package/skills/web/composition-patterns/metadata.json +11 -0
- package/skills/web/composition-patterns/rules/_sections.md +29 -0
- package/skills/web/composition-patterns/rules/_template.md +24 -0
- package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/skills/web/database-migrations/SKILL.md +429 -0
- package/skills/web/deployment-patterns/SKILL.md +427 -0
- package/skills/web/docker-patterns/SKILL.md +364 -0
- package/skills/web/e2e-testing/SKILL.md +326 -0
- package/skills/web/lighthouse-ci/SKILL.md +361 -0
- package/skills/web/mcp-server-patterns/SKILL.md +69 -0
- package/skills/web/next-best-practices/SKILL.md +153 -0
- package/skills/web/next-best-practices/async-patterns.md +87 -0
- package/skills/web/next-best-practices/bundling.md +180 -0
- package/skills/web/next-best-practices/data-patterns.md +297 -0
- package/skills/web/next-best-practices/debug-tricks.md +105 -0
- package/skills/web/next-best-practices/directives.md +73 -0
- package/skills/web/next-best-practices/error-handling.md +227 -0
- package/skills/web/next-best-practices/file-conventions.md +140 -0
- package/skills/web/next-best-practices/font.md +245 -0
- package/skills/web/next-best-practices/functions.md +108 -0
- package/skills/web/next-best-practices/hydration-error.md +91 -0
- package/skills/web/next-best-practices/image.md +173 -0
- package/skills/web/next-best-practices/metadata.md +301 -0
- package/skills/web/next-best-practices/parallel-routes.md +287 -0
- package/skills/web/next-best-practices/route-handlers.md +146 -0
- package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
- package/skills/web/next-best-practices/runtime-selection.md +39 -0
- package/skills/web/next-best-practices/scripts.md +141 -0
- package/skills/web/next-best-practices/self-hosting.md +371 -0
- package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
- package/skills/web/next-cache-components/SKILL.md +411 -0
- package/skills/web/postgres-best-practices/SKILL.md +14 -0
- package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
- package/skills/web/react-best-practices/AGENTS.md +3810 -0
- package/skills/web/react-best-practices/README.md +123 -0
- package/skills/web/react-best-practices/SKILL.md +149 -0
- package/skills/web/react-best-practices/metadata.json +15 -0
- package/skills/web/react-best-practices/rules/_sections.md +46 -0
- package/skills/web/react-best-practices/rules/_template.md +28 -0
- package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
- package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/web/seo/SKILL.md +154 -0
- package/skills/web/web-design-guidelines/SKILL.md +39 -0
- package/skills/web/zap-scan-config/SKILL.md +444 -0
- package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
- package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
- package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
- package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
- package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
- package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
- package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
- package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
- package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
- package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
- package/src/lrr/aggregator.ts +80 -0
- package/src/orchestrator/hooks/context-header.ts +95 -0
- package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
- package/src/orchestrator/hooks/token-accounting.ts +101 -0
- package/src/orchestrator/mcp/cycle-counter.ts +129 -0
- package/src/orchestrator/mcp/scribe.ts +283 -0
- package/src/orchestrator/mcp/state-save.ts +149 -0
- package/src/orchestrator/mcp/write-lease.ts +167 -0
- package/src/orchestrator/phase4-shared-context.ts +41 -0
- package/src/orchestrator/schemas/backward-edge.ts +46 -0
- package/agents/agentic-identity-trust.md +0 -121
- package/agents/data-consolidation-agent.md +0 -39
- package/agents/design-image-prompt-engineer.md +0 -105
- package/agents/design-visual-storyteller.md +0 -147
- package/agents/design-whimsy-injector.md +0 -89
- package/agents/engineering-autonomous-optimization-architect.md +0 -105
- package/agents/market-intel.md +0 -35
- package/agents/marketing-instagram-curator.md +0 -111
- package/agents/marketing-reddit-community-builder.md +0 -121
- package/agents/marketing-social-media-strategist.md +0 -74
- package/agents/marketing-tiktok-strategist.md +0 -123
- package/agents/marketing-twitter-engager.md +0 -124
- package/agents/marketing-wechat-official-account.md +0 -143
- package/agents/marketing-xiaohongshu-specialist.md +0 -136
- package/agents/marketing-zhihu-strategist.md +0 -160
- package/agents/product-behavioral-nudge-engine.md +0 -78
- package/agents/project-management-experiment-tracker.md +0 -102
- package/agents/report-distribution-agent.md +0 -43
- package/agents/risk-analysis.md +0 -45
- package/agents/sales-data-extraction-agent.md +0 -46
- package/agents/specialized-cultural-intelligence-strategist.md +0 -65
- package/agents/specialized-developer-advocate.md +0 -146
- package/agents/support-analytics-reporter.md +0 -133
- package/agents/support-executive-summary-generator.md +0 -64
- package/agents/support-finance-tracker.md +0 -145
- package/agents/support-legal-compliance-checker.md +0 -129
- package/agents/support-support-responder.md +0 -91
- package/agents/testing-accessibility-auditor.md +0 -110
- package/agents/testing-test-results-analyzer.md +0 -97
- package/agents/testing-tool-evaluator.md +0 -76
- package/agents/testing-workflow-optimizer.md +0 -99
- package/agents/user-research.md +0 -40
- package/protocols/brainstorm.md +0 -99
- package/protocols/design.md +0 -269
- package/protocols/planning.md +0 -87
- package/skills/ios/ios-hig/SKILL.md +0 -41
- package/skills/ios/ios-hig/references/accessibility.md +0 -81
- package/skills/ios/ios-hig/references/content.md +0 -142
- package/skills/ios/ios-hig/references/feedback.md +0 -123
- package/skills/ios/ios-hig/references/interaction.md +0 -199
- package/skills/ios/ios-hig/references/performance-platform.md +0 -129
- package/skills/ios/ios-hig/references/privacy-permissions.md +0 -181
- package/skills/ios/ios-hig/references/visual-design.md +0 -84
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Don't Define Components Inside Components
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: prevents remount on every render
|
|
5
|
+
tags: rerender, components, remount, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Don't Define Components Inside Components
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (prevents remount on every render)**
|
|
11
|
+
|
|
12
|
+
Defining a component inside another component creates a new component type on every render. React sees a different component each time and fully remounts it, destroying all state and DOM.
|
|
13
|
+
|
|
14
|
+
A common reason developers do this is to access parent variables without passing props. Always pass props instead.
|
|
15
|
+
|
|
16
|
+
**Incorrect (remounts on every render):**
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
function UserProfile({ user, theme }) {
|
|
20
|
+
// Defined inside to access `theme` - BAD
|
|
21
|
+
const Avatar = () => (
|
|
22
|
+
<img
|
|
23
|
+
src={user.avatarUrl}
|
|
24
|
+
className={theme === 'dark' ? 'avatar-dark' : 'avatar-light'}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// Defined inside to access `user` - BAD
|
|
29
|
+
const Stats = () => (
|
|
30
|
+
<div>
|
|
31
|
+
<span>{user.followers} followers</span>
|
|
32
|
+
<span>{user.posts} posts</span>
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
<Avatar />
|
|
39
|
+
<Stats />
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every time `UserProfile` renders, `Avatar` and `Stats` are new component types. React unmounts the old instances and mounts new ones, losing any internal state, running effects again, and recreating DOM nodes.
|
|
46
|
+
|
|
47
|
+
**Correct (pass props instead):**
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Avatar({ src, theme }: { src: string; theme: string }) {
|
|
51
|
+
return (
|
|
52
|
+
<img
|
|
53
|
+
src={src}
|
|
54
|
+
className={theme === 'dark' ? 'avatar-dark' : 'avatar-light'}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Stats({ followers, posts }: { followers: number; posts: number }) {
|
|
60
|
+
return (
|
|
61
|
+
<div>
|
|
62
|
+
<span>{followers} followers</span>
|
|
63
|
+
<span>{posts} posts</span>
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function UserProfile({ user, theme }) {
|
|
69
|
+
return (
|
|
70
|
+
<div>
|
|
71
|
+
<Avatar src={user.avatarUrl} theme={theme} />
|
|
72
|
+
<Stats followers={user.followers} posts={user.posts} />
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Symptoms of this bug:**
|
|
79
|
+
- Input fields lose focus on every keystroke
|
|
80
|
+
- Animations restart unexpectedly
|
|
81
|
+
- `useEffect` cleanup/setup runs on every parent render
|
|
82
|
+
- Scroll position resets inside the component
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Do not wrap a simple expression with a primitive result type in useMemo
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: wasted computation on every render
|
|
5
|
+
tags: rerender, useMemo, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Do not wrap a simple expression with a primitive result type in useMemo
|
|
9
|
+
|
|
10
|
+
When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`.
|
|
11
|
+
Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.
|
|
12
|
+
|
|
13
|
+
**Incorrect:**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
function Header({ user, notifications }: Props) {
|
|
17
|
+
const isLoading = useMemo(() => {
|
|
18
|
+
return user.isLoading || notifications.isLoading
|
|
19
|
+
}, [user.isLoading, notifications.isLoading])
|
|
20
|
+
|
|
21
|
+
if (isLoading) return <Skeleton />
|
|
22
|
+
// return some markup
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct:**
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
function Header({ user, notifications }: Props) {
|
|
30
|
+
const isLoading = user.isLoading || notifications.isLoading
|
|
31
|
+
|
|
32
|
+
if (isLoading) return <Skeleton />
|
|
33
|
+
// return some markup
|
|
34
|
+
}
|
|
35
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Split Combined Hook Computations
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: avoids recomputing independent steps
|
|
5
|
+
tags: rerender, useMemo, useEffect, dependencies, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Split Combined Hook Computations
|
|
9
|
+
|
|
10
|
+
When a hook contains multiple independent tasks with different dependencies, split them into separate hooks. A combined hook reruns all tasks when any dependency changes, even if some tasks don't use the changed value.
|
|
11
|
+
|
|
12
|
+
**Incorrect (changing `sortOrder` recomputes filtering):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
const sortedProducts = useMemo(() => {
|
|
16
|
+
const filtered = products.filter((p) => p.category === category)
|
|
17
|
+
const sorted = filtered.toSorted((a, b) =>
|
|
18
|
+
sortOrder === "asc" ? a.price - b.price : b.price - a.price
|
|
19
|
+
)
|
|
20
|
+
return sorted
|
|
21
|
+
}, [products, category, sortOrder])
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Correct (filtering only recomputes when products or category change):**
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
const filteredProducts = useMemo(
|
|
28
|
+
() => products.filter((p) => p.category === category),
|
|
29
|
+
[products, category]
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const sortedProducts = useMemo(
|
|
33
|
+
() =>
|
|
34
|
+
filteredProducts.toSorted((a, b) =>
|
|
35
|
+
sortOrder === "asc" ? a.price - b.price : b.price - a.price
|
|
36
|
+
),
|
|
37
|
+
[filteredProducts, sortOrder]
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This pattern also applies to `useEffect` when combining unrelated side effects:
|
|
42
|
+
|
|
43
|
+
**Incorrect (both effects run when either dependency changes):**
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
analytics.trackPageView(pathname)
|
|
48
|
+
document.title = `${pageTitle} | My App`
|
|
49
|
+
}, [pathname, pageTitle])
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Correct (effects run independently):**
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
analytics.trackPageView(pathname)
|
|
57
|
+
}, [pathname])
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
document.title = `${pageTitle} | My App`
|
|
61
|
+
}, [pageTitle])
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, it automatically optimizes dependency tracking and may handle some of these cases for you.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Transitions for Non-Urgent Updates
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: maintains UI responsiveness
|
|
5
|
+
tags: rerender, transitions, startTransition, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Transitions for Non-Urgent Updates
|
|
9
|
+
|
|
10
|
+
Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.
|
|
11
|
+
|
|
12
|
+
**Incorrect (blocks UI on every scroll):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function ScrollTracker() {
|
|
16
|
+
const [scrollY, setScrollY] = useState(0)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handler = () => setScrollY(window.scrollY)
|
|
19
|
+
window.addEventListener('scroll', handler, { passive: true })
|
|
20
|
+
return () => window.removeEventListener('scroll', handler)
|
|
21
|
+
}, [])
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (non-blocking updates):**
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { startTransition } from 'react'
|
|
29
|
+
|
|
30
|
+
function ScrollTracker() {
|
|
31
|
+
const [scrollY, setScrollY] = useState(0)
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const handler = () => {
|
|
34
|
+
startTransition(() => setScrollY(window.scrollY))
|
|
35
|
+
}
|
|
36
|
+
window.addEventListener('scroll', handler, { passive: true })
|
|
37
|
+
return () => window.removeEventListener('scroll', handler)
|
|
38
|
+
}, [])
|
|
39
|
+
}
|
|
40
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use useDeferredValue for Expensive Derived Renders
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: keeps input responsive during heavy computation
|
|
5
|
+
tags: rerender, useDeferredValue, optimization, concurrent
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use useDeferredValue for Expensive Derived Renders
|
|
9
|
+
|
|
10
|
+
When user input triggers expensive computations or renders, use `useDeferredValue` to keep the input responsive. The deferred value lags behind, allowing React to prioritize the input update and render the expensive result when idle.
|
|
11
|
+
|
|
12
|
+
**Incorrect (input feels laggy while filtering):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Search({ items }: { items: Item[] }) {
|
|
16
|
+
const [query, setQuery] = useState('')
|
|
17
|
+
const filtered = items.filter(item => fuzzyMatch(item, query))
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
22
|
+
<ResultsList results={filtered} />
|
|
23
|
+
</>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (input stays snappy, results render when ready):**
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
function Search({ items }: { items: Item[] }) {
|
|
32
|
+
const [query, setQuery] = useState('')
|
|
33
|
+
const deferredQuery = useDeferredValue(query)
|
|
34
|
+
const filtered = useMemo(
|
|
35
|
+
() => items.filter(item => fuzzyMatch(item, deferredQuery)),
|
|
36
|
+
[items, deferredQuery]
|
|
37
|
+
)
|
|
38
|
+
const isStale = query !== deferredQuery
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
43
|
+
<div style={{ opacity: isStale ? 0.7 : 1 }}>
|
|
44
|
+
<ResultsList results={filtered} />
|
|
45
|
+
</div>
|
|
46
|
+
</>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**When to use:**
|
|
52
|
+
|
|
53
|
+
- Filtering/searching large lists
|
|
54
|
+
- Expensive visualizations (charts, graphs) reacting to input
|
|
55
|
+
- Any derived state that causes noticeable render delays
|
|
56
|
+
|
|
57
|
+
**Note:** Wrap the expensive computation in `useMemo` with the deferred value as a dependency, otherwise it still runs on every render.
|
|
58
|
+
|
|
59
|
+
Reference: [React useDeferredValue](https://react.dev/reference/react/useDeferredValue)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use useRef for Transient Values
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: avoids unnecessary re-renders on frequent updates
|
|
5
|
+
tags: rerender, useref, state, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use useRef for Transient Values
|
|
9
|
+
|
|
10
|
+
When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.
|
|
11
|
+
|
|
12
|
+
**Incorrect (renders every update):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Tracker() {
|
|
16
|
+
const [lastX, setLastX] = useState(0)
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const onMove = (e: MouseEvent) => setLastX(e.clientX)
|
|
20
|
+
window.addEventListener('mousemove', onMove)
|
|
21
|
+
return () => window.removeEventListener('mousemove', onMove)
|
|
22
|
+
}, [])
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
style={{
|
|
27
|
+
position: 'fixed',
|
|
28
|
+
top: 0,
|
|
29
|
+
left: lastX,
|
|
30
|
+
width: 8,
|
|
31
|
+
height: 8,
|
|
32
|
+
background: 'black',
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Correct (no re-render for tracking):**
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
function Tracker() {
|
|
43
|
+
const lastXRef = useRef(0)
|
|
44
|
+
const dotRef = useRef<HTMLDivElement>(null)
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const onMove = (e: MouseEvent) => {
|
|
48
|
+
lastXRef.current = e.clientX
|
|
49
|
+
const node = dotRef.current
|
|
50
|
+
if (node) {
|
|
51
|
+
node.style.transform = `translateX(${e.clientX}px)`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
window.addEventListener('mousemove', onMove)
|
|
55
|
+
return () => window.removeEventListener('mousemove', onMove)
|
|
56
|
+
}, [])
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
ref={dotRef}
|
|
61
|
+
style={{
|
|
62
|
+
position: 'fixed',
|
|
63
|
+
top: 0,
|
|
64
|
+
left: 0,
|
|
65
|
+
width: 8,
|
|
66
|
+
height: 8,
|
|
67
|
+
background: 'black',
|
|
68
|
+
transform: 'translateX(0px)',
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use after() for Non-Blocking Operations
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: faster response times
|
|
5
|
+
tags: server, async, logging, analytics, side-effects
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use after() for Non-Blocking Operations
|
|
9
|
+
|
|
10
|
+
Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.
|
|
11
|
+
|
|
12
|
+
**Incorrect (blocks response):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { logUserAction } from '@/app/utils'
|
|
16
|
+
|
|
17
|
+
export async function POST(request: Request) {
|
|
18
|
+
// Perform mutation
|
|
19
|
+
await updateDatabase(request)
|
|
20
|
+
|
|
21
|
+
// Logging blocks the response
|
|
22
|
+
const userAgent = request.headers.get('user-agent') || 'unknown'
|
|
23
|
+
await logUserAction({ userAgent })
|
|
24
|
+
|
|
25
|
+
return new Response(JSON.stringify({ status: 'success' }), {
|
|
26
|
+
status: 200,
|
|
27
|
+
headers: { 'Content-Type': 'application/json' }
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Correct (non-blocking):**
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { after } from 'next/server'
|
|
36
|
+
import { headers, cookies } from 'next/headers'
|
|
37
|
+
import { logUserAction } from '@/app/utils'
|
|
38
|
+
|
|
39
|
+
export async function POST(request: Request) {
|
|
40
|
+
// Perform mutation
|
|
41
|
+
await updateDatabase(request)
|
|
42
|
+
|
|
43
|
+
// Log after response is sent
|
|
44
|
+
after(async () => {
|
|
45
|
+
const userAgent = (await headers()).get('user-agent') || 'unknown'
|
|
46
|
+
const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'
|
|
47
|
+
|
|
48
|
+
logUserAction({ sessionCookie, userAgent })
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return new Response(JSON.stringify({ status: 'success' }), {
|
|
52
|
+
status: 200,
|
|
53
|
+
headers: { 'Content-Type': 'application/json' }
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The response is sent immediately while logging happens in the background.
|
|
59
|
+
|
|
60
|
+
**Common use cases:**
|
|
61
|
+
|
|
62
|
+
- Analytics tracking
|
|
63
|
+
- Audit logging
|
|
64
|
+
- Sending notifications
|
|
65
|
+
- Cache invalidation
|
|
66
|
+
- Cleanup tasks
|
|
67
|
+
|
|
68
|
+
**Important notes:**
|
|
69
|
+
|
|
70
|
+
- `after()` runs even if the response fails or redirects
|
|
71
|
+
- Works in Server Actions, Route Handlers, and Server Components
|
|
72
|
+
|
|
73
|
+
Reference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Authenticate Server Actions Like API Routes
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: prevents unauthorized access to server mutations
|
|
5
|
+
tags: server, server-actions, authentication, security, authorization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Authenticate Server Actions Like API Routes
|
|
9
|
+
|
|
10
|
+
**Impact: CRITICAL (prevents unauthorized access to server mutations)**
|
|
11
|
+
|
|
12
|
+
Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.
|
|
13
|
+
|
|
14
|
+
Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation."
|
|
15
|
+
|
|
16
|
+
**Incorrect (no authentication check):**
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
'use server'
|
|
20
|
+
|
|
21
|
+
export async function deleteUser(userId: string) {
|
|
22
|
+
// Anyone can call this! No auth check
|
|
23
|
+
await db.user.delete({ where: { id: userId } })
|
|
24
|
+
return { success: true }
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (authentication inside the action):**
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
'use server'
|
|
32
|
+
|
|
33
|
+
import { verifySession } from '@/lib/auth'
|
|
34
|
+
import { unauthorized } from '@/lib/errors'
|
|
35
|
+
|
|
36
|
+
export async function deleteUser(userId: string) {
|
|
37
|
+
// Always check auth inside the action
|
|
38
|
+
const session = await verifySession()
|
|
39
|
+
|
|
40
|
+
if (!session) {
|
|
41
|
+
throw unauthorized('Must be logged in')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check authorization too
|
|
45
|
+
if (session.user.role !== 'admin' && session.user.id !== userId) {
|
|
46
|
+
throw unauthorized('Cannot delete other users')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await db.user.delete({ where: { id: userId } })
|
|
50
|
+
return { success: true }
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**With input validation:**
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
'use server'
|
|
58
|
+
|
|
59
|
+
import { verifySession } from '@/lib/auth'
|
|
60
|
+
import { z } from 'zod'
|
|
61
|
+
|
|
62
|
+
const updateProfileSchema = z.object({
|
|
63
|
+
userId: z.string().uuid(),
|
|
64
|
+
name: z.string().min(1).max(100),
|
|
65
|
+
email: z.string().email()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
export async function updateProfile(data: unknown) {
|
|
69
|
+
// Validate input first
|
|
70
|
+
const validated = updateProfileSchema.parse(data)
|
|
71
|
+
|
|
72
|
+
// Then authenticate
|
|
73
|
+
const session = await verifySession()
|
|
74
|
+
if (!session) {
|
|
75
|
+
throw new Error('Unauthorized')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Then authorize
|
|
79
|
+
if (session.user.id !== validated.userId) {
|
|
80
|
+
throw new Error('Can only update own profile')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Finally perform the mutation
|
|
84
|
+
await db.user.update({
|
|
85
|
+
where: { id: validated.userId },
|
|
86
|
+
data: {
|
|
87
|
+
name: validated.name,
|
|
88
|
+
email: validated.email
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
return { success: true }
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cross-Request LRU Caching
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: caches across requests
|
|
5
|
+
tags: server, cache, lru, cross-request
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Cross-Request LRU Caching
|
|
9
|
+
|
|
10
|
+
`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.
|
|
11
|
+
|
|
12
|
+
**Implementation:**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { LRUCache } from 'lru-cache'
|
|
16
|
+
|
|
17
|
+
const cache = new LRUCache<string, any>({
|
|
18
|
+
max: 1000,
|
|
19
|
+
ttl: 5 * 60 * 1000 // 5 minutes
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export async function getUser(id: string) {
|
|
23
|
+
const cached = cache.get(id)
|
|
24
|
+
if (cached) return cached
|
|
25
|
+
|
|
26
|
+
const user = await db.user.findUnique({ where: { id } })
|
|
27
|
+
cache.set(id, user)
|
|
28
|
+
return user
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Request 1: DB query, result cached
|
|
32
|
+
// Request 2: cache hit, no DB query
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use when sequential user actions hit multiple endpoints needing the same data within seconds.
|
|
36
|
+
|
|
37
|
+
**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.
|
|
38
|
+
|
|
39
|
+
**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.
|
|
40
|
+
|
|
41
|
+
Reference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Per-Request Deduplication with React.cache()
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: deduplicates within request
|
|
5
|
+
tags: server, cache, react-cache, deduplication
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Per-Request Deduplication with React.cache()
|
|
9
|
+
|
|
10
|
+
Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.
|
|
11
|
+
|
|
12
|
+
**Usage:**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { cache } from 'react'
|
|
16
|
+
|
|
17
|
+
export const getCurrentUser = cache(async () => {
|
|
18
|
+
const session = await auth()
|
|
19
|
+
if (!session?.user?.id) return null
|
|
20
|
+
return await db.user.findUnique({
|
|
21
|
+
where: { id: session.user.id }
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Within a single request, multiple calls to `getCurrentUser()` execute the query only once.
|
|
27
|
+
|
|
28
|
+
**Avoid inline objects as arguments:**
|
|
29
|
+
|
|
30
|
+
`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.
|
|
31
|
+
|
|
32
|
+
**Incorrect (always cache miss):**
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
const getUser = cache(async (params: { uid: number }) => {
|
|
36
|
+
return await db.user.findUnique({ where: { id: params.uid } })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Each call creates new object, never hits cache
|
|
40
|
+
getUser({ uid: 1 })
|
|
41
|
+
getUser({ uid: 1 }) // Cache miss, runs query again
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Correct (cache hit):**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const getUser = cache(async (uid: number) => {
|
|
48
|
+
return await db.user.findUnique({ where: { id: uid } })
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Primitive args use value equality
|
|
52
|
+
getUser(1)
|
|
53
|
+
getUser(1) // Cache hit, returns cached result
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If you must pass objects, pass the same reference:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const params = { uid: 1 }
|
|
60
|
+
getUser(params) // Query runs
|
|
61
|
+
getUser(params) // Cache hit (same reference)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Next.js-Specific Note:**
|
|
65
|
+
|
|
66
|
+
In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:
|
|
67
|
+
|
|
68
|
+
- Database queries (Prisma, Drizzle, etc.)
|
|
69
|
+
- Heavy computations
|
|
70
|
+
- Authentication checks
|
|
71
|
+
- File system operations
|
|
72
|
+
- Any non-fetch async work
|
|
73
|
+
|
|
74
|
+
Use `React.cache()` to deduplicate these operations across your component tree.
|
|
75
|
+
|
|
76
|
+
Reference: [React.cache documentation](https://react.dev/reference/react/cache)
|