@salesforce/b2c-dx-mcp 3.2.1 → 3.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/bin/run.js +5 -9
- package/content/guidance/b2c/b2c-hooks/references/ORDER-HOOK-LIFECYCLE.md +1 -1
- package/content/guidance/b2c/b2c-onboarding/SKILL.md +4 -4
- package/content/guidance/b2c/b2c-ordering/SKILL.md +2 -2
- package/content/guidance/b2c/b2c-querying-data/SKILL.md +2 -2
- package/content/guidance/b2c/b2c-scapi-admin/SKILL.md +3 -3
- package/content/guidance/b2c/b2c-scapi-admin/references/CLIENT-EXAMPLES.md +4 -4
- package/content/guidance/b2c/b2c-scapi-shopper/SKILL.md +6 -6
- package/content/guidance/b2c/b2c-scapi-shopper/references/CLIENT-EXAMPLES.md +8 -9
- package/content/guidance/b2c-cli/b2c-cip/SKILL.md +3 -3
- package/content/guidance/b2c-cli/b2c-cip/references/SALES_ANALYSIS.md +1 -1
- package/content/guidance/b2c-cli/b2c-config/SKILL.md +31 -21
- package/content/guidance/b2c-cli/b2c-content/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-debug/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-docs/SKILL.md +23 -2
- package/content/guidance/b2c-cli/b2c-ecdn/references/ADVANCED.md +1 -1
- package/content/guidance/b2c-cli/b2c-job/references/IMPORT.md +1 -1
- package/content/guidance/b2c-cli/b2c-metrics/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-mrt/SKILL.md +3 -3
- package/content/guidance/b2c-cli/b2c-scapi-custom/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-scapi-schemas/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-site-import-export/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-sites/SKILL.md +1 -1
- package/content/guidance/b2c-cli/b2c-slas/SKILL.md +1 -1
- package/content/guidance/b2c-ops/b2c-checkout-triage/SKILL.md +2 -2
- package/content/guidance/b2c-ops/b2c-job-health/SKILL.md +2 -2
- package/content/guidance/b2c-ops/b2c-order-failure-triage/SKILL.md +1 -1
- package/content/guidance/b2c-ops/b2c-order-failure-triage/references/order-evidence.md +2 -2
- package/content/guidance/b2c-ops/b2c-production-triage/SKILL.md +4 -4
- package/content/guidance/index.json +935 -258
- package/content/guidance/mcp/{cip → b2c-mcp-cip}/SKILL.md +10 -8
- package/content/guidance/mcp/{b2c-config → b2c-mcp-config}/SKILL.md +10 -9
- package/content/guidance/mcp/{debugger → b2c-mcp-debugger}/SKILL.md +9 -8
- package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/SKILL.md +81 -10
- package/content/guidance/mcp/{server → b2c-mcp-server}/SKILL.md +49 -25
- package/content/guidance/storefront-next/sfnext-accessibility/SKILL.md +103 -0
- package/content/guidance/storefront-next/sfnext-accessibility/references/checklist.md +64 -0
- package/content/guidance/storefront-next/sfnext-analytics-consent/SKILL.md +91 -0
- package/content/guidance/storefront-next/sfnext-analytics-consent/references/CUSTOM-ADAPTER.md +49 -0
- package/content/guidance/storefront-next/sfnext-authentication/SKILL.md +86 -79
- package/content/guidance/storefront-next/sfnext-authentication/references/COOKIES.md +39 -0
- package/content/guidance/storefront-next/sfnext-authentication/references/LOGIN-FLOWS.md +59 -0
- package/content/guidance/storefront-next/sfnext-commerce-features/SKILL.md +67 -0
- package/content/guidance/storefront-next/sfnext-commerce-features/references/FEATURE-PREREQUISITES.md +37 -0
- package/content/guidance/storefront-next/sfnext-components/SKILL.md +98 -136
- package/content/guidance/storefront-next/sfnext-components/references/COMPONENT-AUTHORING.md +118 -0
- package/content/guidance/storefront-next/sfnext-components/references/SHAPE-TOKENS.md +51 -0
- package/content/guidance/storefront-next/sfnext-components/references/STORYBOOK.md +54 -0
- package/content/guidance/storefront-next/sfnext-components/references/TOKEN-SYSTEM.md +53 -0
- package/content/guidance/storefront-next/sfnext-components/references/TROUBLESHOOTING.md +19 -0
- package/content/guidance/storefront-next/sfnext-configuration/SKILL.md +116 -113
- package/content/guidance/storefront-next/sfnext-configuration/references/ENV-VARIABLES.md +55 -0
- package/content/guidance/storefront-next/sfnext-configuration/references/MULTI-SITE-URLS.md +137 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/SKILL.md +100 -150
- package/content/guidance/storefront-next/sfnext-data-fetching/references/ACTIONS.md +53 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/API-CLIENTS.md +34 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/LOADERS.md +73 -0
- package/content/guidance/storefront-next/sfnext-data-fetching/references/SCAPI-FETCHER.md +23 -77
- package/content/guidance/storefront-next/sfnext-deployment/SKILL.md +83 -97
- package/content/guidance/storefront-next/sfnext-deployment/references/MRT-DEPLOYMENT.md +37 -68
- package/content/guidance/storefront-next/sfnext-extensions/SKILL.md +73 -97
- package/content/guidance/storefront-next/sfnext-extensions/references/ACTION-HOOKS.md +57 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/BASE-AUDIT.md +47 -44
- package/content/guidance/storefront-next/sfnext-extensions/references/CLI-AND-INSTALL.md +58 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/EXTENSION-EXAMPLES.md +49 -86
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/SKILL.md +59 -114
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/references/HYBRID-PROXY-CONFIG.md +53 -90
- package/content/guidance/storefront-next/sfnext-i18n/SKILL.md +96 -131
- package/content/guidance/storefront-next/sfnext-i18n/references/locale-config.md +35 -0
- package/content/guidance/storefront-next/sfnext-overview/SKILL.md +101 -0
- package/content/guidance/storefront-next/sfnext-page-designer/SKILL.md +152 -131
- package/content/guidance/storefront-next/sfnext-page-designer/references/BUSINESS-MANAGER.md +46 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/COMPONENT-REGISTRY.md +86 -53
- package/content/guidance/storefront-next/sfnext-page-designer/references/DECORATOR-PATTERNS.md +119 -131
- package/content/guidance/storefront-next/sfnext-page-designer/references/REVIEW-CHECKLIST.md +83 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/TROUBLESHOOTING.md +35 -0
- package/content/guidance/storefront-next/sfnext-performance/SKILL.md +75 -105
- package/content/guidance/storefront-next/sfnext-performance/references/PERFORMANCE-REVIEW-CHECKLIST.md +85 -0
- package/content/guidance/storefront-next/sfnext-performance/references/SUSPENSE-AND-STREAMING.md +58 -0
- package/content/guidance/storefront-next/sfnext-project-setup/SKILL.md +103 -116
- package/content/guidance/storefront-next/sfnext-project-setup/references/PROJECT-STRUCTURE.md +61 -99
- package/content/guidance/storefront-next/sfnext-project-setup/references/SCRIPTS.md +47 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/SFNEXT-CLI.md +63 -0
- package/content/guidance/storefront-next/sfnext-quality-gates/SKILL.md +70 -0
- package/content/guidance/storefront-next/sfnext-quality-gates/references/lint-and-budgets.md +39 -0
- package/content/guidance/storefront-next/sfnext-revalidation/SKILL.md +121 -0
- package/content/guidance/storefront-next/sfnext-revalidation/references/POLICIES-AND-TAGS.md +55 -0
- package/content/guidance/storefront-next/sfnext-routing/SKILL.md +86 -143
- package/content/guidance/storefront-next/sfnext-routing/references/ROUTE-CONVENTIONS.md +51 -51
- package/content/guidance/storefront-next/sfnext-routing/references/URLS-AND-SEO-ROUTES.md +22 -0
- package/content/guidance/storefront-next/sfnext-scapi/SKILL.md +126 -0
- package/content/guidance/storefront-next/sfnext-scapi/references/WORKED-EXAMPLE.md +39 -0
- package/content/guidance/storefront-next/sfnext-security/SKILL.md +129 -0
- package/content/guidance/storefront-next/sfnext-security/references/COOKIE-DOMAIN.md +39 -0
- package/content/guidance/storefront-next/sfnext-security/references/TURNSTILE.md +43 -0
- package/content/guidance/storefront-next/sfnext-seo/SKILL.md +95 -0
- package/content/guidance/storefront-next/sfnext-seo/references/MULTI-DOMAIN-BASE-PATH.md +17 -0
- package/content/guidance/storefront-next/sfnext-seo/references/SEO-ROUTES.md +31 -0
- package/content/guidance/storefront-next/sfnext-state-management/SKILL.md +80 -89
- package/content/guidance/storefront-next/sfnext-state-management/references/PATTERNS.md +45 -0
- package/content/guidance/storefront-next/sfnext-testing/SKILL.md +105 -138
- package/content/guidance/storefront-next/sfnext-testing/references/E2E.md +38 -0
- package/content/guidance/storefront-next/sfnext-testing/references/STORYBOOK-PATTERNS.md +73 -94
- package/content/guidance/storefront-next/sfnext-testing/references/UNIT-AND-ROUTE-TESTS.md +60 -0
- package/content/guidance/storefront-next/sfnext-theming/SKILL.md +104 -0
- package/content/guidance/storefront-next/sfnext-theming/references/REBRAND-CHECKLIST.md +29 -0
- package/dist/commands/mcp.d.ts +8 -2
- package/dist/commands/mcp.js +54 -20
- package/dist/guidance.d.ts +4 -1
- package/dist/guidance.js +23 -5
- package/dist/registry.d.ts +2 -1
- package/dist/registry.js +3 -3
- package/dist/server.d.ts +10 -0
- package/dist/server.js +21 -0
- package/dist/skill-references.js +5 -5
- package/dist/tools/cartridges/index.js +1 -0
- package/dist/tools/cip/index.js +6 -3
- package/dist/tools/diagnostics/debug-capture-at-breakpoint.js +1 -1
- package/dist/tools/diagnostics/debug-set-breakpoints.js +1 -1
- package/dist/tools/diagnostics/debug-start-session.js +1 -1
- package/dist/tools/scapi/index.d.ts +4 -1
- package/dist/tools/scapi/index.js +4 -2
- package/dist/tools/scapi/scapi-code.d.ts +2 -2
- package/dist/tools/scapi/scapi-code.js +17 -61
- package/dist/utils/types.d.ts +6 -0
- package/node_modules/@salesforce/b2c-agent-plugins/CHANGELOG.md +20 -0
- package/node_modules/@salesforce/b2c-agent-plugins/README.md +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/.codex-plugin/plugin.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/plugin.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-hooks/references/ORDER-HOOK-LIFECYCLE.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-onboarding/SKILL.md +4 -4
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-ordering/SKILL.md +2 -2
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-querying-data/SKILL.md +2 -2
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-scapi-admin/SKILL.md +3 -3
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-scapi-admin/references/CLIENT-EXAMPLES.md +4 -4
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-scapi-shopper/SKILL.md +6 -6
- package/node_modules/@salesforce/b2c-agent-plugins/b2c/skills/b2c-scapi-shopper/references/CLIENT-EXAMPLES.md +8 -9
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/.codex-plugin/plugin.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/plugin.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-cip/SKILL.md +3 -3
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-cip/references/SALES_ANALYSIS.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-config/SKILL.md +31 -21
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-content/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-debug/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-docs/SKILL.md +23 -2
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-ecdn/references/ADVANCED.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-job/references/IMPORT.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-metrics/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-mrt/SKILL.md +3 -3
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-scapi-custom/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-scapi-schemas/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-site-import-export/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-sites/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-cli/skills/b2c-slas/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/.codex-plugin/plugin.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/plugin.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/skills/b2c-checkout-triage/SKILL.md +2 -2
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/skills/b2c-job-health/SKILL.md +2 -2
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/skills/b2c-order-failure-triage/SKILL.md +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/skills/b2c-order-failure-triage/references/order-evidence.md +2 -2
- package/node_modules/@salesforce/b2c-agent-plugins/b2c-ops/skills/b2c-production-triage/SKILL.md +4 -4
- package/node_modules/@salesforce/b2c-agent-plugins/figma-to-sfnext-pagedesigner/README.md +4 -4
- package/node_modules/@salesforce/b2c-agent-plugins/figma-to-sfnext-pagedesigner/skills/figma-to-sfnext-pagedesigner/SKILL.md +76 -73
- package/node_modules/@salesforce/b2c-agent-plugins/figma-to-sfnext-pagedesigner/skills/figma-to-sfnext-pagedesigner/evals/trigger-evals.json +12 -10
- package/node_modules/@salesforce/b2c-agent-plugins/figma-to-sfnext-pagedesigner/skills/figma-to-sfnext-pagedesigner/references/PAGE-DESIGNER-SFN.md +49 -74
- package/node_modules/@salesforce/b2c-agent-plugins/package.json +1 -1
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/.codex-plugin/plugin.json +9 -6
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/README.md +43 -22
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/plugin.json +9 -6
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-accessibility/SKILL.md +103 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-accessibility/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-accessibility/references/checklist.md +64 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-analytics-consent/SKILL.md +91 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-analytics-consent/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-analytics-consent/references/CUSTOM-ADAPTER.md +49 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-authentication/SKILL.md +86 -79
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-authentication/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-authentication/references/COOKIES.md +39 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-authentication/references/LOGIN-FLOWS.md +59 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-commerce-features/SKILL.md +67 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-commerce-features/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-commerce-features/references/FEATURE-PREREQUISITES.md +37 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/SKILL.md +98 -136
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/references/COMPONENT-AUTHORING.md +118 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/references/SHAPE-TOKENS.md +51 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/references/STORYBOOK.md +54 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/references/TOKEN-SYSTEM.md +53 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-components/references/TROUBLESHOOTING.md +19 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-configuration/SKILL.md +116 -113
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-configuration/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-configuration/references/ENV-VARIABLES.md +55 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-configuration/references/MULTI-SITE-URLS.md +137 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/SKILL.md +100 -150
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/references/ACTIONS.md +53 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/references/API-CLIENTS.md +34 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/references/LOADERS.md +73 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/references/SCAPI-FETCHER.md +23 -77
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-deployment/SKILL.md +83 -97
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-deployment/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-deployment/references/MRT-DEPLOYMENT.md +37 -68
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-extensions/SKILL.md +73 -97
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-extensions/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-extensions/references/ACTION-HOOKS.md +57 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-extensions/references/BASE-AUDIT.md +47 -44
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-extensions/references/CLI-AND-INSTALL.md +58 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-extensions/references/EXTENSION-EXAMPLES.md +49 -86
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-hybrid-storefronts/SKILL.md +59 -114
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-hybrid-storefronts/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-hybrid-storefronts/references/HYBRID-PROXY-CONFIG.md +53 -90
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-i18n/SKILL.md +96 -131
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-i18n/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-i18n/references/locale-config.md +35 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-overview/SKILL.md +101 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-overview/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/SKILL.md +152 -131
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/references/BUSINESS-MANAGER.md +46 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/references/COMPONENT-REGISTRY.md +86 -53
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/references/DECORATOR-PATTERNS.md +119 -131
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/references/REVIEW-CHECKLIST.md +83 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-page-designer/references/TROUBLESHOOTING.md +35 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-performance/SKILL.md +75 -105
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-performance/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-performance/references/PERFORMANCE-REVIEW-CHECKLIST.md +85 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-performance/references/SUSPENSE-AND-STREAMING.md +58 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-project-setup/SKILL.md +103 -116
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-project-setup/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-project-setup/references/PROJECT-STRUCTURE.md +61 -99
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-project-setup/references/SCRIPTS.md +47 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-project-setup/references/SFNEXT-CLI.md +63 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-quality-gates/SKILL.md +70 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-quality-gates/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-quality-gates/references/lint-and-budgets.md +39 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-revalidation/SKILL.md +121 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-revalidation/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-revalidation/references/POLICIES-AND-TAGS.md +55 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-routing/SKILL.md +86 -143
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-routing/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-routing/references/ROUTE-CONVENTIONS.md +51 -51
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-routing/references/URLS-AND-SEO-ROUTES.md +22 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-scapi/SKILL.md +126 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-scapi/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-scapi/references/WORKED-EXAMPLE.md +39 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-security/SKILL.md +129 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-security/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-security/references/COOKIE-DOMAIN.md +39 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-security/references/TURNSTILE.md +43 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-seo/SKILL.md +95 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-seo/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-seo/references/MULTI-DOMAIN-BASE-PATH.md +17 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-seo/references/SEO-ROUTES.md +31 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-state-management/SKILL.md +80 -89
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-state-management/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-state-management/references/PATTERNS.md +45 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-testing/SKILL.md +105 -138
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-testing/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-testing/references/E2E.md +38 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-testing/references/STORYBOOK-PATTERNS.md +73 -94
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-testing/references/UNIT-AND-ROUTE-TESTS.md +60 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-theming/SKILL.md +104 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-theming/evals/trigger-evals.json +5 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-theming/references/REBRAND-CHECKLIST.md +29 -0
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next-figma/skills/sfnext-create-figma-kit/SKILL.md +3 -3
- package/oclif.manifest.json +21 -4
- package/package.json +3 -3
- package/content/guidance/storefront-next/sfnext-create-component/SKILL.md +0 -195
- package/content/guidance/storefront-next/sfnext-create-component/references/COMPONENT-AUTHORING.md +0 -206
- package/content/guidance/storefront-next/sfnext-create-component/references/PROJECT-STRUCTURE.md +0 -43
- package/content/guidance/storefront-next/sfnext-create-component/references/TOKEN-SYSTEM.md +0 -130
- package/content/guidance/storefront-next/sfnext-create-component/references/TROUBLESHOOTING.md +0 -34
- package/content/guidance/storefront-next/sfnext-create-vertical/SKILL.md +0 -135
- package/content/guidance/storefront-next/sfnext-create-vertical/references/COMPONENT-AUTHORING.md +0 -206
- package/content/guidance/storefront-next/sfnext-create-vertical/references/PROJECT-STRUCTURE.md +0 -43
- package/content/guidance/storefront-next/sfnext-create-vertical/references/TOKEN-SYSTEM.md +0 -130
- package/content/guidance/storefront-next/sfnext-create-vertical/references/TROUBLESHOOTING.md +0 -34
- package/content/guidance/storefront-next/sfnext-custom-apis/SKILL.md +0 -221
- package/content/guidance/storefront-next/sfnext-custom-apis/references/WORKFLOW-EXAMPLES.md +0 -294
- package/content/guidance/storefront-next/sfnext-data-fetching/references/LOADER-PATTERNS.md +0 -102
- package/content/guidance/storefront-next/sfnext-project-setup/references/ENV-VARIABLES.md +0 -82
- package/content/guidance/storefront-next/sfnext-scapi-management/SKILL.md +0 -203
- package/content/guidance/storefront-next/sfnext-scapi-management/references/CLI-COMMANDS.md +0 -122
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-component/SKILL.md +0 -195
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-component/evals/trigger-evals.json +0 -12
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-component/references/COMPONENT-AUTHORING.md +0 -206
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-component/references/PROJECT-STRUCTURE.md +0 -43
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-component/references/TOKEN-SYSTEM.md +0 -130
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-component/references/TROUBLESHOOTING.md +0 -34
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-vertical/SKILL.md +0 -135
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-vertical/evals/trigger-evals.json +0 -12
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-vertical/references/COMPONENT-AUTHORING.md +0 -206
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-vertical/references/PROJECT-STRUCTURE.md +0 -43
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-vertical/references/TOKEN-SYSTEM.md +0 -130
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-create-vertical/references/TROUBLESHOOTING.md +0 -34
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-custom-apis/SKILL.md +0 -221
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-custom-apis/evals/trigger-evals.json +0 -12
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-custom-apis/references/WORKFLOW-EXAMPLES.md +0 -294
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-data-fetching/references/LOADER-PATTERNS.md +0 -102
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-project-setup/references/ENV-VARIABLES.md +0 -82
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-scapi-management/SKILL.md +0 -203
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-scapi-management/evals/trigger-evals.json +0 -12
- package/node_modules/@salesforce/b2c-agent-plugins/storefront-next/skills/sfnext-scapi-management/references/CLI-COMMANDS.md +0 -122
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/custom-apis.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/custom-properties.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/jobs.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/products.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/promotions.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/saving.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/snippets.md +0 -0
- /package/content/guidance/mcp/{scapi → b2c-mcp-scapi}/references/tokens.md +0 -0
- /package/content/guidance/mcp/{server → b2c-mcp-server}/references/files.md +0 -0
|
@@ -1,190 +1,140 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sfnext-data-fetching
|
|
3
|
-
description:
|
|
3
|
+
description: >-
|
|
4
|
+
Load and mutate data in a Storefront Next project: route loaders that call SCAPI through createApiClients from @/lib/api-clients.server, critical versus streamed (deferred) data, createBasketAction and BasketAction for cart actions, createActionError and ErrorCode, data() responses, useFetcher and the useScapiFetcher hook with its server allowlist, request-scoped dedupe and MRT timeouts, non-personalized SCAPI responses, shopper context, and custom API clients. Use for "fetch products in a loader", "stream data", "add a cart action", "call SCAPI from a component", "resource route", "NormalizedApiError", or "loader blocks the page". Do not use for route files or links (use `storefront-next:sfnext-routing`), when loaders re-run after actions (use `storefront-next:sfnext-revalidation`), Suspense/LCP tuning (use `storefront-next:sfnext-performance`), or defining SCAPI custom APIs (use `storefront-next:sfnext-scapi`).
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Data Fetching
|
|
7
|
+
# Storefront Next Data Fetching
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
All data comes from the server. Loaders and actions run on the server with the shopper's session; the browser never calls SCAPI directly. Your project ships `docs/README-DATA.md`, `docs/README-SUSPENSE.md`, `docs/README-SCAPI-NON-PERSONALIZED-RESPONSES.md` and `docs/README-SHOPPER-CONTEXT.md`, plus the "Performance & Data Rules" in `AGENTS.md`; this skill is the quick path, those are authoritative.
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## The rules (same as AGENTS.md)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
1. Fetch on the server in `loader` (reads) and `action` (writes). There is no `clientLoader` or `clientAction`.
|
|
14
|
+
2. Classify every piece of data:
|
|
15
|
+
- **Critical** (needed for SEO, LCP, layout stability or the HTTP status): `await` it in the loader.
|
|
16
|
+
- **Non-critical** (below the fold, recommendations, reviews, secondary panels): start the request, do not await it, and return the unresolved promise so it streams.
|
|
17
|
+
- **Interaction-driven** (only after a click, hover or input): fetch on demand with a fetcher.
|
|
18
|
+
3. Never block a loader on non-critical data.
|
|
19
|
+
4. Pass the promise through `loaderData` and read it under its own `<Suspense>` with `use()` or `<Await>`. Keep the promise identity stable (compose it in the loader, never `Promise.all` or `.then` in render).
|
|
20
|
+
5. Export `shouldRevalidate` on routes whose loaders depend on URL filters (see `storefront-next:sfnext-revalidation`).
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|-----------|-------------|----------|
|
|
16
|
-
| `loader` | Route navigation | Initial page data |
|
|
17
|
-
| `action` | Form submission | Mutations (add to cart, update profile) |
|
|
18
|
-
| `useScapiFetcher` | User interaction | On-demand fetching (search suggestions, infinite scroll) |
|
|
22
|
+
## Loader with critical and streamed data
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
```tsx
|
|
25
|
+
import { Suspense } from 'react';
|
|
26
|
+
import { Await } from 'react-router';
|
|
27
|
+
import type { Route } from './+types/_app.example';
|
|
28
|
+
import { createApiClients } from '@/lib/api-clients.server';
|
|
29
|
+
import { fetchProductById } from '@/lib/api/products.server';
|
|
30
|
+
import { NormalizedApiError } from '@/lib/api/normalized-api-error';
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
export async function loader({ context, params }: Route.LoaderArgs) {
|
|
33
|
+
const clients = createApiClients(context);
|
|
23
34
|
|
|
24
|
-
-
|
|
25
|
-
|
|
35
|
+
// Non-critical: start now, do not await.
|
|
36
|
+
const related = clients.shopperSearch
|
|
37
|
+
.productSearch({ params: { query: { q: 'shirt', limit: 8 } } })
|
|
38
|
+
.then(({ data }) => data.hits ?? []);
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
params: { path: { id: params.productId } }
|
|
37
|
-
}).then(({ data }) => data),
|
|
38
|
-
};
|
|
40
|
+
// Critical: needed for status, SEO and LCP.
|
|
41
|
+
try {
|
|
42
|
+
const product = await fetchProductById(context, params.id ?? '', { expand: ['images', 'prices'] });
|
|
43
|
+
if (!product) throw new Response('Not found', { status: 404 });
|
|
44
|
+
return { product, related };
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (e instanceof NormalizedApiError && e.status) throw new Response(e.message, { status: e.status });
|
|
47
|
+
throw e;
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
category, // Resolved immediately
|
|
52
|
-
products: clients.shopperSearch.productSearch({
|
|
53
|
-
params: { query: { q: '', refine: { cgid: params.categoryId } } }
|
|
54
|
-
}).then(({ data }) => data), // Streamed
|
|
55
|
-
};
|
|
51
|
+
export default function Example({ loaderData }: Route.ComponentProps) {
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<h1>{loaderData.product.name}</h1>
|
|
55
|
+
<Suspense fallback={<div className="h-40" aria-busy="true" />}>
|
|
56
|
+
<Await resolve={loaderData.related}>{(hits) => <ul>{hits.map((h) => <li key={h.productId}>{h.productName}</li>)}</ul>}</Await>
|
|
57
|
+
</Suspense>
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
56
60
|
}
|
|
57
61
|
```
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
| Pattern | When | Example |
|
|
62
|
-
|---------|------|---------|
|
|
63
|
-
| Sync (full streaming) | All data can render progressively | Product page with reviews |
|
|
64
|
-
| Async (await critical) | SEO-critical data needed for page shell | Category page (needs category name) |
|
|
65
|
-
| Mixed | Some data critical, some deferrable | Category name (await) + product grid (stream) |
|
|
66
|
-
|
|
67
|
-
See [Loader Patterns Reference](references/LOADER-PATTERNS.md) for more patterns and data flow diagrams.
|
|
63
|
+
Real precedent: `src/routes/_app.p.$.tsx` awaits the product (404 becomes `throw new Response(..., { status: 404 })`) and streams the Page Designer page, schema and extras; `src/components/product-grid/deferred.tsx` is the streamed-grid pattern.
|
|
68
64
|
|
|
69
|
-
##
|
|
65
|
+
## SCAPI clients
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
`createApiClients(context)` from `@/lib/api-clients.server` returns `AppClients`: every generated Shopper API client (`shopperProducts`, `shopperSearch`, `shopperBasketsV2`, `shopperCustomers`, `shopperOrders`, `shopperLogin`, and more) plus generated clients for your custom APIs (`@/scapi/custom-clients`). Call shape:
|
|
72
68
|
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const productId = formData.get('productId') as string;
|
|
79
|
-
|
|
80
|
-
const clients = createApiClients(context);
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
await clients.shopperBasketsV2.addItemToBasket({
|
|
84
|
-
params: {
|
|
85
|
-
path: { basketId },
|
|
86
|
-
body: { productId, quantity: 1 },
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
return data({ success: true });
|
|
90
|
-
} catch (error) {
|
|
91
|
-
return data({ success: false, error: error.message }, { status: 400 });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
69
|
+
```ts
|
|
70
|
+
const { data } = await clients.shopperBasketsV2.addItemToBasket({
|
|
71
|
+
params: { path: { basketId }, query: {} },
|
|
72
|
+
body: [{ productId, quantity: 1 }], // body is a sibling of params; this endpoint takes an array
|
|
73
|
+
});
|
|
94
74
|
```
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
For on-demand data fetching triggered by user interactions (after page load):
|
|
76
|
+
Results are `{ data }`; failures throw. Prefer the thin wrappers in `src/lib/api/*.server.ts` (`fetchProductById`, `fetchProductsByIds` which chunks to the 24-id limit, category, search, order, customer and basket helpers): they log and rethrow as `NormalizedApiError` (`@/lib/api/normalized-api-error`, with `.status` and `.cause`). Add new wrappers there rather than calling clients inline in many routes.
|
|
99
77
|
|
|
100
|
-
|
|
101
|
-
import { useScapiFetcher } from '@/hooks/use-scapi-fetcher';
|
|
78
|
+
The clients already wrap `fetch` with request-scoped GET/HEAD dedupe (identical calls in one request share one response; any mutation clears the cache), a hard timeout from the `MRT_REQUEST_TIMEOUT` environment variable when set, and a health observer that logs 429 and load-status headers. Do not re-implement them. More: [API-CLIENTS.md](references/API-CLIENTS.md).
|
|
102
79
|
|
|
103
|
-
|
|
104
|
-
const parameters = useMemo(
|
|
105
|
-
() => ({ params: { query: { q, limit, currency } } }),
|
|
106
|
-
[q, limit, currency]
|
|
107
|
-
);
|
|
80
|
+
Trim payloads: pass only the `expand`/`select` values you render. Availability is cached about 60 seconds and prices/promotions about 15 minutes, so short-TTL data is a good candidate for streaming. Shopper-agnostic responses can be cached using the policy in `src/lib/scapi/non-personalized-response-policy.server.ts` (see the shipped README-SCAPI-NON-PERSONALIZED-RESPONSES); request `personalized: 'none'` only where the response truly is the same for all shoppers, as the navigation loader in `_app.tsx` does.
|
|
108
81
|
|
|
109
|
-
|
|
110
|
-
'shopperSearch',
|
|
111
|
-
'getSearchSuggestions',
|
|
112
|
-
parameters
|
|
113
|
-
);
|
|
82
|
+
## Mutations: actions
|
|
114
83
|
|
|
115
|
-
|
|
116
|
-
await fetcher.load();
|
|
117
|
-
}, [fetcher]);
|
|
84
|
+
Prefer an `action.*` route and a `<Form>` (navigating) or `useFetcher` (non-navigating).
|
|
118
85
|
|
|
119
|
-
|
|
120
|
-
data: fetcher.data,
|
|
121
|
-
isLoading: fetcher.state === 'loading',
|
|
122
|
-
refetch,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
```
|
|
86
|
+
Basket mutations use the factory, which loads the basket, parses `FormData`, catches errors and wraps the result:
|
|
126
87
|
|
|
127
|
-
|
|
88
|
+
```ts
|
|
89
|
+
// src/routes/action.example-remove-item.ts
|
|
90
|
+
import { data } from 'react-router';
|
|
91
|
+
import { BasketAction, createBasketAction } from '@/lib/cart/basket-action.server';
|
|
92
|
+
import { createActionError } from '@/lib/action-error-helpers.server';
|
|
93
|
+
import { ErrorCode } from '@/lib/error-codes';
|
|
128
94
|
|
|
129
|
-
|
|
95
|
+
export const action = createBasketAction(
|
|
96
|
+
{ method: 'POST', action: BasketAction.CartItemRemove, parse: (fd) => ({ itemId: String(fd.get('itemId') ?? '') }) },
|
|
97
|
+
async ({ input, basketId, clients }) => {
|
|
98
|
+
if (!input.itemId) {
|
|
99
|
+
return data({ success: false, error: createActionError({ code: ErrorCode.REQUIRED_FIELD, message: 'itemId is required' }) }, { status: 400 });
|
|
100
|
+
}
|
|
101
|
+
const { data: basket } = await clients.shopperBasketsV2.removeItemFromBasket({ params: { path: { basketId, itemId: input.itemId } } });
|
|
102
|
+
return basket; // factory returns { success: true, basket } and syncs the basket resource
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
```
|
|
130
106
|
|
|
131
|
-
|
|
107
|
+
Return the updated `Basket` for success. Return `data(payload, { status })` for validation errors (use `data()` from `react-router`, not `Response.json`). Thrown SCAPI 4xx errors pass their status through; others become 500. Available `BasketAction` values are listed in `src/lib/cart/basket-action.server.ts`. See the existing `action.cart-item-*.tsx` files. Details and non-basket actions: [ACTIONS.md](references/ACTIONS.md).
|
|
132
108
|
|
|
133
|
-
|
|
134
|
-
import { createApiClients } from '@/lib/api-clients';
|
|
109
|
+
## Fetching from components
|
|
135
110
|
|
|
136
|
-
|
|
137
|
-
|
|
111
|
+
- Use `useFetcher` against your own `action.*`/`resource.*` routes (`resourceRoutes` in `@/route-paths`).
|
|
112
|
+
- Use `useScapiFetcher(client, method, { params, body })` from `@/hooks/use-scapi-fetcher` for a small allowlisted set of Shopper calls without writing a route. It returns `.load()`, `.submit(payload)`, `.data`, `.errors`, `.success`. The server enforces an allowlist in `src/lib/scapi/resource-policy.ts` (for example `shopperProducts.getProduct`, `shopperBasketsV2.getBasket`, `shopperSearch.getSearchSuggestions`, and selected `shopperCustomers` address and profile mutations). Anything else needs a dedicated route. See [SCAPI-FETCHER.md](references/SCAPI-FETCHER.md).
|
|
113
|
+
- Do not fetch in `useEffect` on mount what the loader could fetch (see the performance review checklist in `storefront-next:sfnext-performance`).
|
|
114
|
+
- Fetchers and raw `fetch` calls that do not go through a submission do not trigger revalidation; actions do, for every active loader.
|
|
138
115
|
|
|
139
|
-
|
|
140
|
-
clients.shopperCustomers.getCustomer({...});
|
|
141
|
-
clients.shopperBasketsV2.getBasket({...});
|
|
142
|
-
clients.shopperSearch.productSearch({...});
|
|
143
|
-
clients.shopperOrders.getOrder({...});
|
|
144
|
-
}
|
|
145
|
-
```
|
|
116
|
+
## Checklist
|
|
146
117
|
|
|
147
|
-
|
|
118
|
+
1. Can the loader fetch this at request time? If yes, do it there.
|
|
119
|
+
2. Is it critical? `await`; otherwise return the promise with its own Suspense and a sized skeleton.
|
|
120
|
+
3. Did you request only the fields you render?
|
|
121
|
+
4. Mutation errors: `createActionError` with an `ErrorCode`, return with `data(..., { status })`.
|
|
122
|
+
5. New shared fetch logic goes in `src/lib/api/*.server.ts` with a test (`storefront-next:sfnext-testing`).
|
|
123
|
+
6. Run `pnpm typecheck` and `pnpm test`.
|
|
148
124
|
|
|
149
|
-
|
|
150
|
-
// GOOD — Parallel requests (all start simultaneously)
|
|
151
|
-
export function loader({ context }: LoaderFunctionArgs) {
|
|
152
|
-
const clients = createApiClients(context);
|
|
153
|
-
return {
|
|
154
|
-
product: clients.shopperProducts.getProduct({...}).then(({ data }) => data),
|
|
155
|
-
reviews: clients.shopperProducts.getReviews({...}).then(({ data }) => data),
|
|
156
|
-
recommendations: clients.shopperProducts.getRecommendations({...}).then(({ data }) => data),
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// AVOID — Sequential awaits of independent requests (unnecessarily slow)
|
|
161
|
-
export async function loader({ context }: LoaderFunctionArgs) {
|
|
162
|
-
const clients = createApiClients(context);
|
|
163
|
-
const product = await clients.shopperProducts.getProduct({...}); // Waits...
|
|
164
|
-
const reviews = await clients.shopperProducts.getReviews({...}); // Then waits again
|
|
165
|
-
return { product, reviews };
|
|
166
|
-
}
|
|
167
|
-
```
|
|
125
|
+
Deeper: [LOADERS.md](references/LOADERS.md).
|
|
168
126
|
|
|
169
|
-
##
|
|
127
|
+
## Finding more
|
|
170
128
|
|
|
171
|
-
|
|
172
|
-
|---------|---------|----------|
|
|
173
|
-
| Awaiting all data | Blocks page transition unnecessarily | Only await SEO-critical data; stream the rest |
|
|
174
|
-
| Client loaders | Not permitted in Storefront Next | Use server `loader` or `useScapiFetcher` |
|
|
175
|
-
| Sequential `await` | Slow data loading | Return promises in parallel |
|
|
176
|
-
| Missing `context` in `getConfig()` | Config unavailable | Pass `context` in server loaders: `getConfig(context)` |
|
|
129
|
+
Open `AGENTS.md` ("Key Documentation") and `docs/README-DATA.md` in your project. Search product docs with `b2c docs search "<term>"` or the `docs_search` MCP tool.
|
|
177
130
|
|
|
178
131
|
## Related Skills
|
|
179
132
|
|
|
180
|
-
- `storefront-next:sfnext-
|
|
181
|
-
- `storefront-next:sfnext-
|
|
182
|
-
- `storefront-next:sfnext-
|
|
183
|
-
- `storefront-next:sfnext-
|
|
184
|
-
- `storefront-next:sfnext-
|
|
185
|
-
- `storefront-next:sfnext-authentication` -
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
- [Loader Patterns Reference](references/LOADER-PATTERNS.md) - Data flow diagrams and advanced patterns
|
|
190
|
-
- [SCAPI Fetcher Reference](references/SCAPI-FETCHER.md) - Complete useScapiFetcher API and examples
|
|
133
|
+
- `storefront-next:sfnext-routing` - route files and links
|
|
134
|
+
- `storefront-next:sfnext-revalidation` - avoid wasted loader re-runs after actions
|
|
135
|
+
- `storefront-next:sfnext-performance` - Suspense placement, LCP, review checklist
|
|
136
|
+
- `storefront-next:sfnext-state-management` - basket provider and client state
|
|
137
|
+
- `storefront-next:sfnext-scapi` - SCAPI access and custom APIs
|
|
138
|
+
- `storefront-next:sfnext-authentication` - sessions behind loaders
|
|
139
|
+
- `b2c-cli:b2c-scapi-custom` - inspect custom API endpoints
|
|
140
|
+
- `b2c:b2c-custom-api-development` - build a custom API
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"query": "Add a loader on my Storefront Next page that awaits the product and streams recommendations so the page doesn't block", "should_trigger": true},
|
|
3
|
+
{"query": "How do I write a createBasketAction handler that returns a validation error?", "should_trigger": true},
|
|
4
|
+
{"query": "My product grid re-fetches every time I add an item to the cart; how do I stop loaders re-running after actions?", "should_trigger": false}
|
|
5
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Actions and errors
|
|
2
|
+
|
|
3
|
+
## Where to put a mutation
|
|
4
|
+
|
|
5
|
+
Create `src/routes/action.<name>.ts(x)` exporting only `action`. Add an entry to `resourceRoutes` in `src/route-paths.ts`. The `/action/**` paths are excluded from the site prefix. Use `<Form method="post" action={resourceRoutes.x}>` when the user should navigate or when a native form must work without JavaScript; use `useFetcher` for in-place changes (add to cart, quantity changes, toggles).
|
|
6
|
+
|
|
7
|
+
## Basket actions: `createBasketAction`
|
|
8
|
+
|
|
9
|
+
`import { BasketAction, createBasketAction } from '@/lib/cart/basket-action.server'`
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
createBasketAction(
|
|
13
|
+
{ method: 'POST' | 'PATCH', action: BasketAction.X, parse: (fd: FormData) => input },
|
|
14
|
+
async ({ input, basketId, basket, context, clients, logger }) => Basket | ReturnType<typeof data>
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- The factory enforces the HTTP method, ensures a basket exists, parses form data, runs the handler and converts the result.
|
|
19
|
+
- Returning a `Basket` yields `{ success: true, basket }` (status 200) and updates the basket resource through `updateBasketResource` so the provider picks it up.
|
|
20
|
+
- Returning `data({ success: false, error }, { status })` sends validation errors.
|
|
21
|
+
- Throwing yields `{ success: false, error }`; a SCAPI 4xx keeps its status, anything else is 500.
|
|
22
|
+
- Enum values: CartItemRemove, CartItemUpdate, CartItemAdd, CartSetAdd, CartBundleAdd, CartBundleUpdate, PromoCodeAdd, PromoCodeRemove, BonusProductAdd, SwatchOrder. Add new ones in the same file when a new kind of basket mutation needs its own name.
|
|
23
|
+
- Request bodies for some calls are arrays (`addItemToBasket` takes `[payload]`).
|
|
24
|
+
|
|
25
|
+
Read `action.cart-item-add.tsx`, `action.cart-item-update.tsx` and `action.cart-item-remove.tsx` for complete examples.
|
|
26
|
+
|
|
27
|
+
## Other actions
|
|
28
|
+
|
|
29
|
+
For non-basket work (customer, wishlist, consent, checkout), write a plain `action({ request, context })`: read `await request.formData()`, call `createApiClients(context)` or a `src/lib/api/*.server.ts` wrapper, and return `data(...)`. Check authentication with `getAuth(context)` where needed (`NOT_AUTHENTICATED`).
|
|
30
|
+
|
|
31
|
+
## Error shape
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { createActionError } from '@/lib/action-error-helpers.server';
|
|
35
|
+
import { ErrorCode } from '@/lib/error-codes';
|
|
36
|
+
|
|
37
|
+
createActionError({ code: ErrorCode.INVALID_INPUT, message: 'Invalid quantity' });
|
|
38
|
+
createActionError({ error: caughtError }); // normalizes SCAPI/unknown errors
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`ErrorCode` members: NOT_FOUND, NOT_AUTHENTICATED, NOT_AUTHORIZED, INVALID_INPUT, REQUIRED_FIELD, CONFLICT, EXPIRED, OPERATION_FAILED, OUT_OF_STOCK, RATE_LIMITED, METHOD_NOT_ALLOWED, UNKNOWN, SCAPI_UNSUPPORTED, CONFIGURATION_ERROR.
|
|
42
|
+
|
|
43
|
+
Response types live in `@/routes/types/action-responses` (`ActionResponse<T>`, `BasketActionResponse`). Return status codes with `data(payload, { status })` from `react-router`.
|
|
44
|
+
|
|
45
|
+
## Client side
|
|
46
|
+
|
|
47
|
+
- `useItemFetcher({ itemId, componentName })` (`@/hooks/use-item-fetcher`) gives per-line-item fetcher keys so a row shows its own pending state.
|
|
48
|
+
- Optimistic UI: read `fetcher.formData`, `useNavigation`, or `useOptimistic`.
|
|
49
|
+
- Side effects after a fetcher finishes: `useFetcherEffect` (`@/hooks/use-fetcher-effect`) or `useScapiFetcherEffect` (`@/hooks/use-scapi-fetcher-effect`) instead of hand-written `useEffect` chains.
|
|
50
|
+
|
|
51
|
+
## After an action
|
|
52
|
+
|
|
53
|
+
React Router revalidates every active loader by default. Decide which should re-run and declare it with `shouldRevalidate` or tags: `storefront-next:sfnext-revalidation`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# API clients
|
|
2
|
+
|
|
3
|
+
`createApiClients(context)` (`@/lib/api-clients.server`) builds, per request, the typed clients used in loaders and actions. Cost is low; call it in each function that needs it.
|
|
4
|
+
|
|
5
|
+
## What you get
|
|
6
|
+
|
|
7
|
+
`AppClients` (type in `@/scapi/custom-clients`) is the generated Shopper API clients merged with clients generated for your custom APIs. Authentication (shopper access token), `siteId`, locale and currency come from the request context. The `@/scapi` barrel exports schema and operation types, for example `ShopperProducts.schemas['Product']` and `ApiError`.
|
|
8
|
+
|
|
9
|
+
## Fetch layers
|
|
10
|
+
|
|
11
|
+
Each client uses `dedupe(timeout(healthObserver(originBoundFetch)))`:
|
|
12
|
+
|
|
13
|
+
| Layer | Behavior |
|
|
14
|
+
|---|---|
|
|
15
|
+
| dedupe | within one request, identical GET/HEAD calls share one promise; any non-GET call clears the whole registry |
|
|
16
|
+
| timeout | aborts calls after `MRT_REQUEST_TIMEOUT` ms when that variable is set (no-op otherwise) |
|
|
17
|
+
| health observer | logs 429 and SCAPI load-status signals |
|
|
18
|
+
|
|
19
|
+
Consequences: it is safe for several components' loaders to request the same product; do not add your own in-memory cache keyed by request; after a write in the same request, re-reads are real calls.
|
|
20
|
+
|
|
21
|
+
## Errors
|
|
22
|
+
|
|
23
|
+
Clients throw. In `src/lib/api/*.server.ts`, catch and `throw new NormalizedApiError(error)`; route code checks `e.status`. Map 404 to a `Response` with status 404; let unexpected errors reach the route `ErrorBoundary`.
|
|
24
|
+
|
|
25
|
+
## Custom APIs
|
|
26
|
+
|
|
27
|
+
Generated custom-API clients appear on `AppClients` (for example a notification client). To add one: define the API with `storefront-next:sfnext-scapi` guidance, generate the client as described there, and call it as `clients.<name>.<operation>({ params, body })`.
|
|
28
|
+
|
|
29
|
+
## Request shaping checklist
|
|
30
|
+
|
|
31
|
+
- `expand`/`select` only what the UI renders.
|
|
32
|
+
- `fetchProductsByIds` chunks at 24 ids; do not loop single `getProduct` calls.
|
|
33
|
+
- Start independent calls before awaiting (parallel), but keep non-critical calls un-awaited.
|
|
34
|
+
- Pass `personalized: 'none'` only for shopper-independent data, following `docs/README-SCAPI-NON-PERSONALIZED-RESPONSES.md`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Loader patterns
|
|
2
|
+
|
|
3
|
+
See also `docs/README-DATA.md`, `docs/README-SUSPENSE.md` and the "Performance & Data Rules" in `AGENTS.md` in your project.
|
|
4
|
+
|
|
5
|
+
## Critical versus streamed
|
|
6
|
+
|
|
7
|
+
| Data | Treatment |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Entity that decides status (product, category, order) | `await`; map SCAPI 404 to `throw new Response(msg, { status: 404 })` |
|
|
10
|
+
| Data visible in the first viewport, SEO tags, JSON-LD source | `await` |
|
|
11
|
+
| Page Designer page for the LCP region | `await` the page and render `<Region critical>` (see `storefront-next:sfnext-page-designer`) |
|
|
12
|
+
| Below the fold, recommendations, reviews, secondary panels | return the promise |
|
|
13
|
+
| Short-TTL data (availability about 60 s) that is not the LCP | prefer streaming |
|
|
14
|
+
| Needs a user gesture | fetcher, not loader |
|
|
15
|
+
|
|
16
|
+
Start every independent request before the first `await` so they run in parallel:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
const clients = createApiClients(context);
|
|
20
|
+
const extrasPromise = loadExtras(context, id); // started, not awaited
|
|
21
|
+
const product = await fetchProductById(context, id); // critical
|
|
22
|
+
return { product, extras: extrasPromise };
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Stable promises
|
|
26
|
+
|
|
27
|
+
Compose dependent work inside the loader (`extrasPromise.then(...)` there, never in render). A new promise object per render restarts Suspense and flashes the fallback. Do not wrap promises in `useMemo`. If a component must derive one, pin it with `useState(() => ...)` or re-pin via `useRef` keyed on the inputs. Full rules in `docs/README-SUSPENSE.md`.
|
|
28
|
+
|
|
29
|
+
A streamed promise that can reject needs an error path: use `<Await errorElement={...}>` or make the loader promise resolve to a fallback value (`.catch(() => [])`) when degraded output is acceptable, and log inside the loader.
|
|
30
|
+
|
|
31
|
+
## Consuming a promise
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<Suspense fallback={<RecommendationsSkeleton />}>
|
|
35
|
+
<Await resolve={loaderData.recommendations} errorElement={null}>
|
|
36
|
+
{(items) => <Recommendations items={items} />}
|
|
37
|
+
</Await>
|
|
38
|
+
</Suspense>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- One `<Suspense>` per independent async operation; siblings may resolve independently.
|
|
42
|
+
- The skeleton must reserve the final height (no `fallback={null}` above the fold).
|
|
43
|
+
- A component that reads a promise with `use()` must have a `<Suspense>` ancestor, otherwise it blocks the first byte.
|
|
44
|
+
- For product listings reuse `DeferredProductGrid` (`@/components/product-grid/deferred`).
|
|
45
|
+
|
|
46
|
+
## Shared layout data
|
|
47
|
+
|
|
48
|
+
Layout loaders (for example `_app.tsx` navigation) stream their data and child routes read it with `useRouteLoaderData('routes/_app')`. Put data that many routes need in the lowest common layout, not in each page.
|
|
49
|
+
|
|
50
|
+
## Request context
|
|
51
|
+
|
|
52
|
+
Loaders receive `context` (`Route.LoaderArgs`). Useful accessors:
|
|
53
|
+
|
|
54
|
+
| Need | Import |
|
|
55
|
+
|---|---|
|
|
56
|
+
| Logger | `getLogger` from `@/lib/logger.server` |
|
|
57
|
+
| Site, locale, currency | `siteContext` from `@salesforce/storefront-next-runtime/site-context` via `context.get(siteContext)` |
|
|
58
|
+
| Config | `getConfig` from `@salesforce/storefront-next-runtime/config` |
|
|
59
|
+
| Session | `getAuth` from `@/middlewares/auth.server` |
|
|
60
|
+
| Basket | `getBasket`, `getBasketSnapshot`, `ensureBasketId` from `@/middlewares/basket.server` |
|
|
61
|
+
|
|
62
|
+
Middleware order is defined in `src/root.tsx` (`export const middleware`); add new request-wide state there with `createContext`/middleware instead of re-deriving it in each loader.
|
|
63
|
+
|
|
64
|
+
## Shopper context and personalization
|
|
65
|
+
|
|
66
|
+
Shopper context (customer segments, source codes, custom qualifiers) is applied by `shopper-context` middleware and read in components with `useShopperContext`. Read `docs/README-SHOPPER-CONTEXT.md` before changing it. Personalized endpoints must not be cached as shared responses.
|
|
67
|
+
|
|
68
|
+
## Pitfalls
|
|
69
|
+
|
|
70
|
+
- `Promise.all([...])` over critical and non-critical together re-blocks the loader; await only the critical set.
|
|
71
|
+
- Mutating `loaderData` on the client is unsupported; derive in the loader.
|
|
72
|
+
- Do not add `clientLoader`.
|
|
73
|
+
- Shaping or normalizing data in render (lookup maps, chained passes) belongs in the loader.
|
|
@@ -1,93 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `useScapiFetcher`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`import { useScapiFetcher } from '@/hooks/use-scapi-fetcher'`
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Component calls useScapiFetcher()
|
|
7
|
-
↓
|
|
8
|
-
Hook builds URL: /resource/api/client/{encoded-params}
|
|
9
|
-
↓
|
|
10
|
-
fetcher.load() or fetcher.submit()
|
|
11
|
-
↓
|
|
12
|
-
resource.api.client.$resource.ts loader/action runs ON SERVER
|
|
13
|
-
↓
|
|
14
|
-
createApiClients(context) makes SCAPI call (server-side)
|
|
15
|
-
↓
|
|
16
|
-
JSON response returned to component
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Even though `useScapiFetcher` is called from the browser, the actual SCAPI requests happen **on the server** through the resource route, keeping credentials secure.
|
|
20
|
-
|
|
21
|
-
## API
|
|
5
|
+
A typed wrapper over React Router's `useFetcher` that calls a Shopper client method through a single resource route (`src/routes/resource.api.client.$resource.ts`). The request is encoded (base64url of `[client, method, options]`) into `/resource/api/client/:resource`, executed on the server, and the SCAPI `data` is returned unwrapped.
|
|
22
6
|
|
|
23
|
-
```
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
parameters // SCAPI parameters object
|
|
28
|
-
);
|
|
7
|
+
```tsx
|
|
8
|
+
const product = useScapiFetcher('shopperProducts', 'getProduct', {
|
|
9
|
+
params: { path: { id: variantId }, query: { expand: ['availability', 'prices'] } },
|
|
10
|
+
});
|
|
29
11
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
fetcher.load() // Trigger a GET request
|
|
34
|
-
fetcher.submit() // Trigger a POST request
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (variantId && product.state === 'idle' && !product.data && !product.errors) void product.load();
|
|
14
|
+
}, [variantId, product]); // keep deps stable; see checklist
|
|
35
15
|
```
|
|
36
16
|
|
|
37
|
-
|
|
17
|
+
Returned object: `.load()` (GET), `.submit(payload, opts)` (mutation), `.data`, `.errors`, `.success`, plus the fetcher fields such as `.state`.
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
|----------|-----|
|
|
41
|
-
| Load product data on page visit | `loader` |
|
|
42
|
-
| Load checkout data | `loader` |
|
|
43
|
-
| Search suggestions as user types | `useScapiFetcher` |
|
|
44
|
-
| Update customer profile in modal | `useScapiFetcher` |
|
|
45
|
-
| Load recommendations after page loads | `useScapiFetcher` |
|
|
46
|
-
| Fetch bonus products when modal opens | `useScapiFetcher` |
|
|
47
|
-
| Infinite scroll / Load more | `useScapiFetcher` |
|
|
19
|
+
Real users: `src/providers/basket.tsx` (`shopperBasketsV2.getBasket`) and `src/components/cart-item-modal/*` (`shopperProducts.getProduct`).
|
|
48
20
|
|
|
49
|
-
##
|
|
21
|
+
## Server allowlist (`src/lib/scapi/resource-policy.ts`)
|
|
50
22
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
[navigate] → [server fetch] → [stream to client]
|
|
54
|
-
Data available: Streamed during render via Suspense
|
|
23
|
+
- Loaders: `shopperBasketsV2.getBasket`, `shopperProducts.getProduct`, `shopperProducts.getProducts`, `shopperSearch.getSearchSuggestions`.
|
|
24
|
+
- Actions: `shopperCustomers.createCustomerAddress`, `updateCustomerAddress`, `removeCustomerAddress`, `updateCustomer`, `updateCustomerPassword`.
|
|
55
25
|
|
|
56
|
-
|
|
57
|
-
[render] → [user action] → [fetch] → [re-render]
|
|
58
|
-
Data available: AFTER user action, component re-renders
|
|
59
|
-
```
|
|
26
|
+
Everything else is rejected. `organizationId`, `siteId` and `locale` are set by the server, and request bodies and headers are sanitized. The older "helpers" overload is unsupported.
|
|
60
27
|
|
|
61
|
-
|
|
28
|
+
Need something else (recommendations, category products, your own custom API)? Create a `resource.*` route with a `loader`. Pattern: same-origin check, input validation, `Cache-Control: no-store` for personalized output (`resource.recommendations.ts`, `resource.category-products.ts`). To allow another client call, change `resource-policy.ts` deliberately and add a test; do not widen it casually.
|
|
62
29
|
|
|
63
|
-
|
|
64
|
-
import { useScapiFetcher } from '@/hooks/use-scapi-fetcher';
|
|
65
|
-
import { useMemo, useCallback } from 'react';
|
|
30
|
+
## Related hooks
|
|
66
31
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
() => ({
|
|
70
|
-
params: {
|
|
71
|
-
query: { q, limit, currency }
|
|
72
|
-
}
|
|
73
|
-
}),
|
|
74
|
-
[q, limit, currency]
|
|
75
|
-
);
|
|
32
|
+
- `useScapiFetcherEffect(fetcher, { onSuccess, onError })` runs callbacks once per completed request.
|
|
33
|
+
- `useScapiFetchClient` (`@/hooks/use-scapi-fetch`) is a lower-level, non-fetcher variant for one-off calls.
|
|
76
34
|
|
|
77
|
-
|
|
78
|
-
'shopperSearch',
|
|
79
|
-
'getSearchSuggestions',
|
|
80
|
-
parameters
|
|
81
|
-
);
|
|
35
|
+
## When not to use it
|
|
82
36
|
|
|
83
|
-
|
|
84
|
-
await fetcher.load();
|
|
85
|
-
}, [fetcher]);
|
|
37
|
+
If the data is knowable at request time, fetch it in the loader. Mounting a component that fires `useScapiFetcher().load()` in an effect for data the route could have streamed adds a waterfall; see the performance review checklist in `storefront-next:sfnext-performance`.
|
|
86
38
|
|
|
87
|
-
|
|
88
|
-
data: fetcher.data,
|
|
89
|
-
isLoading: fetcher.state === 'loading',
|
|
90
|
-
refetch
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
```
|
|
39
|
+
Calls made through `useScapiFetcher().load()` are fetcher loads, not submissions, so they do not revalidate other loaders. `.submit()` is a submission and does.
|