@salesforce/b2c-cli 2.3.1 → 2.5.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/dev.js +8 -8
- package/bin/run.js +5 -7
- package/content/guidance/b2c/b2c-business-manager-extensions/SKILL.md +361 -0
- package/content/guidance/b2c/b2c-business-manager-extensions/references/EXTENSIONS-XML.md +458 -0
- package/content/guidance/b2c/b2c-controllers/SKILL.md +301 -0
- package/content/guidance/b2c/b2c-controllers/references/CLASSIC-PATTERNS.md +335 -0
- package/content/guidance/b2c/b2c-controllers/references/SFRA-PATTERNS.md +400 -0
- package/content/guidance/b2c/b2c-custom-api-development/SKILL.md +281 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/CONTRACT.md +142 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/IMPLEMENTATION.md +153 -0
- package/content/guidance/b2c/b2c-custom-api-development/references/TESTING.md +118 -0
- package/content/guidance/b2c/b2c-custom-caches/SKILL.md +279 -0
- package/content/guidance/b2c/b2c-custom-job-steps/SKILL.md +520 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/CHUNK-ORIENTED.md +377 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/JOBS-XML.md +212 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/STEPTYPES-JSON.md +373 -0
- package/content/guidance/b2c/b2c-custom-job-steps/references/TASK-ORIENTED.md +344 -0
- package/content/guidance/b2c/b2c-custom-objects/SKILL.md +327 -0
- package/content/guidance/b2c/b2c-custom-objects/references/OCAPI-SEARCH.md +298 -0
- package/content/guidance/b2c/b2c-forms/SKILL.md +242 -0
- package/content/guidance/b2c/b2c-forms/references/FORM-XML.md +409 -0
- package/content/guidance/b2c/b2c-hooks/SKILL.md +500 -0
- package/content/guidance/b2c/b2c-hooks/references/OCAPI-SCAPI-HOOKS.md +403 -0
- package/content/guidance/b2c/b2c-hooks/references/ORDER-HOOK-LIFECYCLE.md +169 -0
- package/content/guidance/b2c/b2c-hooks/references/SYSTEM-HOOKS.md +433 -0
- package/content/guidance/b2c/b2c-isml/SKILL.md +320 -0
- package/content/guidance/b2c/b2c-isml/references/EXPRESSIONS.md +366 -0
- package/content/guidance/b2c/b2c-isml/references/TAGS.md +443 -0
- package/content/guidance/b2c/b2c-localization/SKILL.md +344 -0
- package/content/guidance/b2c/b2c-localization/references/PATTERNS.md +407 -0
- package/content/guidance/b2c/b2c-logging/SKILL.md +352 -0
- package/content/guidance/b2c/b2c-logging/references/LOG-FILES.md +282 -0
- package/content/guidance/b2c/b2c-metadata/SKILL.md +406 -0
- package/content/guidance/b2c/b2c-metadata/references/SYSTEM-OBJECTS.md +320 -0
- package/content/guidance/b2c/b2c-metadata/references/XML-EXAMPLES.md +366 -0
- package/content/guidance/b2c/b2c-onboarding/SKILL.md +154 -0
- package/content/guidance/b2c/b2c-ordering/SKILL.md +391 -0
- package/content/guidance/b2c/b2c-page-designer/SKILL.md +410 -0
- package/content/guidance/b2c/b2c-page-designer/references/ATTRIBUTE-TYPES.md +436 -0
- package/content/guidance/b2c/b2c-page-designer/references/META-DEFINITIONS.md +340 -0
- package/content/guidance/b2c/b2c-querying-data/SKILL.md +289 -0
- package/content/guidance/b2c/b2c-querying-data/references/PERFORMANCE-APIS.md +74 -0
- package/content/guidance/b2c/b2c-scapi-admin/SKILL.md +53 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/CLIENT-EXAMPLES.md +440 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/INTEGRATION-PATTERNS.md +518 -0
- package/content/guidance/b2c/b2c-scapi-admin/references/OAUTH-SCOPES.md +337 -0
- package/content/guidance/b2c/b2c-scapi-shopper/SKILL.md +56 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/CHECKOUT-FLOW.md +466 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/CLIENT-EXAMPLES.md +351 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/COMMON-PATTERNS.md +390 -0
- package/content/guidance/b2c/b2c-scapi-shopper/references/SCOPES.md +290 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/SKILL.md +420 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/PASSKEYS.md +126 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/SESSION-BRIDGE.md +267 -0
- package/content/guidance/b2c/b2c-slas-auth-patterns/references/TOKEN-LIFECYCLE.md +367 -0
- package/content/guidance/b2c/b2c-webservices/SKILL.md +318 -0
- package/content/guidance/b2c/b2c-webservices/references/FTP-SERVICES.md +524 -0
- package/content/guidance/b2c/b2c-webservices/references/HTTP-SERVICES.md +578 -0
- package/content/guidance/b2c/b2c-webservices/references/SERVICES-XML.md +351 -0
- package/content/guidance/b2c/b2c-webservices/references/SOAP-SERVICES.md +587 -0
- package/content/guidance/b2c-cli/b2c-am/SKILL.md +277 -0
- package/content/guidance/b2c-cli/b2c-bm-users-roles/SKILL.md +210 -0
- package/content/guidance/b2c-cli/b2c-cap/SKILL.md +131 -0
- package/content/guidance/b2c-cli/b2c-cip/SKILL.md +116 -0
- package/content/guidance/b2c-cli/b2c-cip/references/KNOWN_TABLES.md +105 -0
- package/content/guidance/b2c-cli/b2c-cip/references/SALES_ANALYSIS.md +50 -0
- package/content/guidance/b2c-cli/b2c-cip/references/STARTER_QUERIES.md +147 -0
- package/content/guidance/b2c-cli/b2c-code/SKILL.md +146 -0
- package/content/guidance/b2c-cli/b2c-config/SKILL.md +481 -0
- package/content/guidance/b2c-cli/b2c-content/SKILL.md +176 -0
- package/content/guidance/b2c-cli/b2c-debug/SKILL.md +138 -0
- package/content/guidance/b2c-cli/b2c-docs/SKILL.md +301 -0
- package/content/guidance/b2c-cli/b2c-ecdn/SKILL.md +137 -0
- package/content/guidance/b2c-cli/b2c-ecdn/references/ADVANCED.md +97 -0
- package/content/guidance/b2c-cli/b2c-ecdn/references/SECURITY.md +75 -0
- package/content/guidance/b2c-cli/b2c-import-set-migrations/SKILL.md +265 -0
- package/content/guidance/b2c-cli/b2c-job/SKILL.md +70 -0
- package/content/guidance/b2c-cli/b2c-job/references/EXPORT.md +121 -0
- package/content/guidance/b2c-cli/b2c-job/references/IMPORT.md +55 -0
- package/content/guidance/b2c-cli/b2c-job/references/RUN-AND-MONITOR.md +148 -0
- package/content/guidance/b2c-cli/b2c-logs/SKILL.md +249 -0
- package/content/guidance/b2c-cli/b2c-metrics/SKILL.md +315 -0
- package/content/guidance/b2c-cli/b2c-mrt/SKILL.md +207 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/BUNDLE-COMMANDS.md +203 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/ENVIRONMENT-COMMANDS.md +218 -0
- package/content/guidance/b2c-cli/b2c-mrt/references/PROJECT-COMMANDS.md +154 -0
- package/content/guidance/b2c-cli/b2c-sandbox/SKILL.md +112 -0
- package/content/guidance/b2c-cli/b2c-scapi-custom/SKILL.md +126 -0
- package/content/guidance/b2c-cli/b2c-scapi-schemas/SKILL.md +70 -0
- package/content/guidance/b2c-cli/b2c-scapi-schemas/references/CLI-EXAMPLES.md +108 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/SKILL.md +57 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/IMPORT-OPTIONS.md +118 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/METADATA-XML.md +381 -0
- package/content/guidance/b2c-cli/b2c-site-import-export/references/WORKFLOWS.md +182 -0
- package/content/guidance/b2c-cli/b2c-sites/SKILL.md +112 -0
- package/content/guidance/b2c-cli/b2c-slas/SKILL.md +182 -0
- package/content/guidance/b2c-cli/b2c-webdav/SKILL.md +186 -0
- package/content/guidance/b2c-ops/b2c-checkout-triage/SKILL.md +68 -0
- package/content/guidance/b2c-ops/b2c-edge-traffic-triage/SKILL.md +69 -0
- package/content/guidance/b2c-ops/b2c-job-health/SKILL.md +80 -0
- package/content/guidance/b2c-ops/b2c-job-health/references/job-logs.md +21 -0
- package/content/guidance/b2c-ops/b2c-order-failure-triage/SKILL.md +66 -0
- package/content/guidance/b2c-ops/b2c-order-failure-triage/references/order-evidence.md +87 -0
- package/content/guidance/b2c-ops/b2c-production-triage/SKILL.md +88 -0
- package/content/guidance/b2c-ops/b2c-production-triage/references/escalation.md +57 -0
- package/content/guidance/index.json +1969 -0
- 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 +127 -0
- 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 +153 -0
- 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 +163 -0
- 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 +140 -0
- 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 +39 -0
- package/content/guidance/storefront-next/sfnext-deployment/SKILL.md +127 -0
- package/content/guidance/storefront-next/sfnext-deployment/references/MRT-DEPLOYMENT.md +59 -0
- package/content/guidance/storefront-next/sfnext-extensions/SKILL.md +118 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/ACTION-HOOKS.md +57 -0
- package/content/guidance/storefront-next/sfnext-extensions/references/BASE-AUDIT.md +68 -0
- 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 +79 -0
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/SKILL.md +93 -0
- package/content/guidance/storefront-next/sfnext-hybrid-storefronts/references/HYBRID-PROXY-CONFIG.md +86 -0
- package/content/guidance/storefront-next/sfnext-i18n/SKILL.md +152 -0
- 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 +202 -0
- 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 +111 -0
- package/content/guidance/storefront-next/sfnext-page-designer/references/DECORATOR-PATTERNS.md +168 -0
- 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 +102 -0
- 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 +147 -0
- package/content/guidance/storefront-next/sfnext-project-setup/references/PROJECT-STRUCTURE.md +61 -0
- 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 +123 -0
- package/content/guidance/storefront-next/sfnext-routing/references/ROUTE-CONVENTIONS.md +65 -0
- 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 +114 -0
- package/content/guidance/storefront-next/sfnext-state-management/references/PATTERNS.md +45 -0
- package/content/guidance/storefront-next/sfnext-testing/SKILL.md +151 -0
- package/content/guidance/storefront-next/sfnext-testing/references/E2E.md +38 -0
- package/content/guidance/storefront-next/sfnext-testing/references/STORYBOOK-PATTERNS.md +97 -0
- 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/cap/install.d.ts +1 -0
- package/dist/commands/cap/list.d.ts +1 -0
- package/dist/commands/cap/pull.d.ts +1 -0
- package/dist/commands/cap/tasks.d.ts +1 -0
- package/dist/commands/cap/uninstall.d.ts +1 -0
- package/dist/commands/cip/describe.d.ts +1 -0
- package/dist/commands/cip/query.d.ts +1 -0
- package/dist/commands/cip/report/bot-traffic-share.d.ts +1 -0
- package/dist/commands/cip/report/checkout-funnel-dropoff.d.ts +1 -0
- package/dist/commands/cip/report/controller-error-rate-trend.d.ts +1 -0
- package/dist/commands/cip/report/controller-health-scorecard.d.ts +1 -0
- package/dist/commands/cip/report/customer-registration-trends.d.ts +1 -0
- package/dist/commands/cip/report/discount-depth-breakdown.d.ts +1 -0
- package/dist/commands/cip/report/inventory-stockout-by-location.d.ts +1 -0
- package/dist/commands/cip/report/new-vs-returning-buyer-revenue.d.ts +1 -0
- package/dist/commands/cip/report/ocapi-client-usage.d.ts +1 -0
- package/dist/commands/cip/report/ocapi-requests.d.ts +1 -0
- package/dist/commands/cip/report/payment-method-performance.d.ts +1 -0
- package/dist/commands/cip/report/product-co-purchase-analysis.d.ts +1 -0
- package/dist/commands/cip/report/promotion-discount-analysis.d.ts +1 -0
- package/dist/commands/cip/report/promotion-roi-leaderboard.d.ts +1 -0
- package/dist/commands/cip/report/recommender-effectiveness.d.ts +1 -0
- package/dist/commands/cip/report/remote-include-performance.d.ts +1 -0
- package/dist/commands/cip/report/revenue-by-channel.d.ts +1 -0
- package/dist/commands/cip/report/sales-analytics.d.ts +1 -0
- package/dist/commands/cip/report/sales-summary.d.ts +1 -0
- package/dist/commands/cip/report/scapi-cache-hit-ratio.d.ts +1 -0
- package/dist/commands/cip/report/scapi-error-rate-by-status.d.ts +1 -0
- package/dist/commands/cip/report/scapi-latency-distribution.d.ts +1 -0
- package/dist/commands/cip/report/scapi-traffic-latency.d.ts +1 -0
- package/dist/commands/cip/report/search-query-performance.d.ts +1 -0
- package/dist/commands/cip/report/top-referrers.d.ts +1 -0
- package/dist/commands/cip/report/top-selling-products.d.ts +1 -0
- package/dist/commands/cip/report/zero-result-searches.d.ts +1 -0
- package/dist/commands/cip/tables.d.ts +1 -0
- package/dist/commands/code/activate.d.ts +1 -0
- package/dist/commands/code/delete.d.ts +1 -0
- package/dist/commands/code/deploy.d.ts +1 -0
- package/dist/commands/code/download.d.ts +1 -0
- package/dist/commands/code/watch.d.ts +1 -0
- package/dist/commands/commands/search.d.ts +35 -0
- package/dist/commands/commands/search.js +85 -0
- package/dist/commands/commands/search.js.map +1 -0
- package/dist/commands/content/export.d.ts +1 -0
- package/dist/commands/content/list.d.ts +1 -0
- package/dist/commands/content/validate.d.ts +1 -0
- package/dist/commands/debug/cli.d.ts +1 -0
- package/dist/commands/debug/index.d.ts +1 -0
- package/dist/commands/docs/cache.d.ts +1 -0
- package/dist/commands/docs/download.d.ts +1 -0
- package/dist/commands/docs/read.d.ts +1 -0
- package/dist/commands/docs/schema.d.ts +1 -0
- package/dist/commands/docs/search.d.ts +1 -0
- package/dist/commands/docs/skill.d.ts +41 -0
- package/dist/commands/docs/skill.js +183 -0
- package/dist/commands/docs/skill.js.map +1 -0
- package/dist/commands/ecdn/cache/purge.d.ts +1 -0
- package/dist/commands/ecdn/certificates/add.d.ts +1 -0
- package/dist/commands/ecdn/certificates/delete.d.ts +1 -0
- package/dist/commands/ecdn/certificates/list.d.ts +1 -0
- package/dist/commands/ecdn/certificates/update.d.ts +1 -0
- package/dist/commands/ecdn/certificates/validate.d.ts +1 -0
- package/dist/commands/ecdn/cipher-suites/get.d.ts +1 -0
- package/dist/commands/ecdn/cipher-suites/update.d.ts +1 -0
- package/dist/commands/ecdn/firewall/create.d.ts +1 -0
- package/dist/commands/ecdn/firewall/delete.d.ts +1 -0
- package/dist/commands/ecdn/firewall/get.d.ts +1 -0
- package/dist/commands/ecdn/firewall/list.d.ts +1 -0
- package/dist/commands/ecdn/firewall/reorder.d.ts +1 -0
- package/dist/commands/ecdn/firewall/update.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/create.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/delete.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/get.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/list.d.ts +1 -0
- package/dist/commands/ecdn/logpush/jobs/update.d.ts +1 -0
- package/dist/commands/ecdn/logpush/ownership.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/create.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/delete.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/get.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/rules/delete.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/rules/update.d.ts +1 -0
- package/dist/commands/ecdn/mrt-rules/update.d.ts +1 -0
- package/dist/commands/ecdn/mtls/create.d.ts +1 -0
- package/dist/commands/ecdn/mtls/delete.d.ts +1 -0
- package/dist/commands/ecdn/mtls/get.d.ts +1 -0
- package/dist/commands/ecdn/mtls/issue.d.ts +1 -0
- package/dist/commands/ecdn/mtls/list.d.ts +1 -0
- package/dist/commands/ecdn/mtls/setup.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/delete.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/get.d.ts +1 -0
- package/dist/commands/ecdn/origin-headers/set.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/create.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/delete.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/notifications/list.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/create.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/delete.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/get.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/list.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/policies/update.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/scripts/get.d.ts +1 -0
- package/dist/commands/ecdn/page-shield/scripts/list.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/create.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/delete.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/get.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/list.d.ts +1 -0
- package/dist/commands/ecdn/rate-limit/update.d.ts +1 -0
- package/dist/commands/ecdn/security/get.d.ts +1 -0
- package/dist/commands/ecdn/security/update.d.ts +1 -0
- package/dist/commands/ecdn/speed/get.d.ts +1 -0
- package/dist/commands/ecdn/speed/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/groups/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/groups/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/managed-rules/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/managed-rules/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/migrate.d.ts +1 -0
- package/dist/commands/ecdn/waf/owasp/get.d.ts +1 -0
- package/dist/commands/ecdn/waf/owasp/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/get.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/rules/update.d.ts +1 -0
- package/dist/commands/ecdn/waf/rulesets/list.d.ts +1 -0
- package/dist/commands/ecdn/waf/rulesets/update.d.ts +1 -0
- package/dist/commands/ecdn/zones/create.d.ts +1 -0
- package/dist/commands/ecdn/zones/list.d.ts +1 -0
- package/dist/commands/job/execution/delete.d.ts +1 -0
- package/dist/commands/job/export.d.ts +1 -0
- package/dist/commands/job/import-set.d.ts +1 -0
- package/dist/commands/job/import.d.ts +1 -0
- package/dist/commands/job/log.d.ts +1 -0
- package/dist/commands/job/run.d.ts +1 -0
- package/dist/commands/job/run.js +1 -1
- package/dist/commands/job/run.js.map +1 -1
- package/dist/commands/job/search.d.ts +1 -0
- package/dist/commands/job/wait.d.ts +1 -0
- package/dist/commands/logs/get.d.ts +1 -0
- package/dist/commands/logs/list.d.ts +1 -0
- package/dist/commands/logs/tail.d.ts +1 -0
- package/dist/commands/metrics/controller.d.ts +1 -0
- package/dist/commands/metrics/ecdn.d.ts +1 -0
- package/dist/commands/metrics/mrt.d.ts +1 -0
- package/dist/commands/metrics/ocapi.d.ts +1 -0
- package/dist/commands/metrics/overall.d.ts +1 -0
- package/dist/commands/metrics/sales.d.ts +1 -0
- package/dist/commands/metrics/scapi-hooks.d.ts +1 -0
- package/dist/commands/metrics/scapi.d.ts +1 -0
- package/dist/commands/metrics/third-party.d.ts +1 -0
- package/dist/commands/mrt/bundle/delete.d.ts +1 -0
- package/dist/commands/mrt/bundle/deploy.d.ts +1 -0
- package/dist/commands/mrt/bundle/download.d.ts +1 -0
- package/dist/commands/mrt/bundle/history.d.ts +1 -0
- package/dist/commands/mrt/bundle/list.d.ts +1 -0
- package/dist/commands/mrt/bundle/save.d.ts +1 -0
- package/dist/commands/mrt/bundle/upload-v2.d.ts +1 -0
- package/dist/commands/mrt/env/access-control/list.d.ts +1 -0
- package/dist/commands/mrt/env/b2c.d.ts +1 -0
- package/dist/commands/mrt/env/clone.d.ts +1 -0
- package/dist/commands/mrt/env/create.d.ts +1 -0
- package/dist/commands/mrt/env/delete.d.ts +1 -0
- package/dist/commands/mrt/env/get.d.ts +1 -0
- package/dist/commands/mrt/env/invalidate.d.ts +1 -0
- package/dist/commands/mrt/env/list.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/clone.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/create.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/delete.d.ts +1 -0
- package/dist/commands/mrt/env/redirect/list.d.ts +1 -0
- package/dist/commands/mrt/env/update.d.ts +1 -0
- package/dist/commands/mrt/env/var/delete.d.ts +1 -0
- package/dist/commands/mrt/env/var/list.d.ts +1 -0
- package/dist/commands/mrt/env/var/push.d.ts +1 -0
- package/dist/commands/mrt/env/var/set.d.ts +1 -0
- package/dist/commands/mrt/org/b2c.d.ts +1 -0
- package/dist/commands/mrt/org/cert/create.d.ts +1 -0
- package/dist/commands/mrt/org/cert/delete.d.ts +1 -0
- package/dist/commands/mrt/org/cert/get.d.ts +1 -0
- package/dist/commands/mrt/org/cert/list.d.ts +1 -0
- package/dist/commands/mrt/org/cert/restart-validation.d.ts +1 -0
- package/dist/commands/mrt/org/list.d.ts +1 -0
- package/dist/commands/mrt/org/member/add.d.ts +1 -0
- package/dist/commands/mrt/org/member/get.d.ts +1 -0
- package/dist/commands/mrt/org/member/list.d.ts +1 -0
- package/dist/commands/mrt/org/member/remove.d.ts +1 -0
- package/dist/commands/mrt/org/member/update.d.ts +1 -0
- package/dist/commands/mrt/project/create.d.ts +1 -0
- package/dist/commands/mrt/project/delete.d.ts +1 -0
- package/dist/commands/mrt/project/get.d.ts +1 -0
- package/dist/commands/mrt/project/list.d.ts +1 -0
- package/dist/commands/mrt/project/member/add.d.ts +1 -0
- package/dist/commands/mrt/project/member/get.d.ts +1 -0
- package/dist/commands/mrt/project/member/list.d.ts +1 -0
- package/dist/commands/mrt/project/member/remove.d.ts +1 -0
- package/dist/commands/mrt/project/member/update.d.ts +1 -0
- package/dist/commands/mrt/project/notification/delete.d.ts +1 -0
- package/dist/commands/mrt/project/notification/get.d.ts +1 -0
- package/dist/commands/mrt/project/update.d.ts +1 -0
- package/dist/commands/mrt/save-credentials.d.ts +1 -0
- package/dist/commands/mrt/tail-logs.d.ts +1 -0
- package/dist/commands/mrt/user/api-key.d.ts +1 -0
- package/dist/commands/mrt/user/email-prefs.d.ts +1 -0
- package/dist/commands/mrt/user/profile.d.ts +1 -0
- package/dist/commands/scaffold/init.js +2 -1
- package/dist/commands/scaffold/init.js.map +1 -1
- package/dist/commands/scapi/custom/status.d.ts +1 -0
- package/dist/commands/scapi/schemas/get.d.ts +9 -0
- package/dist/commands/scapi/schemas/get.js +51 -19
- package/dist/commands/scapi/schemas/get.js.map +1 -1
- package/dist/commands/scapi/schemas/list.d.ts +6 -0
- package/dist/commands/scapi/schemas/list.js +28 -18
- package/dist/commands/scapi/schemas/list.js.map +1 -1
- package/dist/commands/setup/get.d.ts +36 -0
- package/dist/commands/setup/get.js +67 -0
- package/dist/commands/setup/get.js.map +1 -0
- package/dist/commands/setup/ide/tsserver-plugin.d.ts +1 -0
- package/dist/commands/setup/ide/vscode-types.d.ts +1 -0
- package/dist/commands/setup/index.js +4 -3
- package/dist/commands/setup/index.js.map +1 -1
- package/dist/commands/setup/inspect.d.ts +1 -0
- package/dist/commands/setup/inspect.js +15 -4
- package/dist/commands/setup/inspect.js.map +1 -1
- package/dist/commands/setup/instance/create.d.ts +9 -0
- package/dist/commands/setup/instance/create.js +22 -6
- package/dist/commands/setup/instance/create.js.map +1 -1
- package/dist/commands/setup/instance/list.d.ts +1 -0
- package/dist/commands/setup/instance/list.js +3 -4
- package/dist/commands/setup/instance/list.js.map +1 -1
- package/dist/commands/setup/instance/remove.d.ts +1 -0
- package/dist/commands/setup/instance/remove.js +5 -5
- package/dist/commands/setup/instance/remove.js.map +1 -1
- package/dist/commands/setup/instance/set-active.d.ts +6 -0
- package/dist/commands/setup/instance/set-active.js +20 -5
- package/dist/commands/setup/instance/set-active.js.map +1 -1
- package/dist/commands/setup/openshell.d.ts +1 -0
- package/dist/commands/setup/set.d.ts +37 -0
- package/dist/commands/setup/set.js +87 -0
- package/dist/commands/setup/set.js.map +1 -0
- package/dist/commands/setup/skills.d.ts +1 -0
- package/dist/commands/setup/unset.d.ts +37 -0
- package/dist/commands/setup/unset.js +55 -0
- package/dist/commands/setup/unset.js.map +1 -0
- package/dist/commands/slas/client/create.d.ts +1 -0
- package/dist/commands/slas/client/delete.d.ts +1 -0
- package/dist/commands/slas/client/get.d.ts +1 -0
- package/dist/commands/slas/client/list.d.ts +1 -0
- package/dist/commands/slas/client/open.d.ts +1 -0
- package/dist/commands/slas/client/update.d.ts +1 -0
- package/dist/commands/slas/token.d.ts +1 -0
- package/dist/commands/slas/token.js +2 -1
- package/dist/commands/slas/token.js.map +1 -1
- package/dist/commands/webdav/get.d.ts +1 -0
- package/dist/commands/webdav/rm.d.ts +1 -0
- package/dist/help.d.ts +25 -0
- package/dist/help.js +96 -0
- package/dist/help.js.map +1 -0
- package/dist/lib/scaffold/generate-helper.js +3 -2
- package/dist/lib/scaffold/generate-helper.js.map +1 -1
- package/dist/lib/skills.d.ts +19 -0
- package/dist/lib/skills.js +75 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/utils/cip/command.d.ts +1 -0
- package/dist/utils/ecdn/zone-command.d.ts +1 -0
- package/dist/utils/setup/config-field-command.d.ts +25 -0
- package/dist/utils/setup/config-field-command.js +41 -0
- package/dist/utils/setup/config-field-command.js.map +1 -0
- package/dist/utils/slas/client.d.ts +1 -0
- package/oclif.manifest.json +20802 -16968
- package/package.json +11 -5
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sfnext-configuration
|
|
3
|
+
description: >-
|
|
4
|
+
Configure a Storefront Next app: config.server.ts (defineConfig, metadata/runtime/app), AppConfig types in
|
|
5
|
+
src/types/config.ts, PUBLIC__ environment variable overrides, getConfig(context) / useConfig(), extension config
|
|
6
|
+
(app.extension, app.serverExtension), protected paths, server-only secrets, and multi-site settings
|
|
7
|
+
(commerce.sites, url.prefix, seoRoutes, siteAliasMap, cookie domain). Use when editing config.server.ts, adding a
|
|
8
|
+
config value, an env var is ignored or "not applied", `pnpm config:inspect` / `config:push-env`, "Ignoring
|
|
9
|
+
environment variable" warnings, protected config path errors, setting up a second site or locale URLs, or
|
|
10
|
+
going through the pre-launch config checklist.
|
|
11
|
+
Do not use for creating a project or the npm script list (use `storefront-next:sfnext-project-setup`), pushing
|
|
12
|
+
bundles or MRT env management (use `storefront-next:sfnext-deployment` / `b2c-cli:b2c-mrt`), or hybrid
|
|
13
|
+
proxy and cookie-sharing with SFRA (use `storefront-next:sfnext-hybrid-storefronts`).
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Storefront Next Configuration
|
|
17
|
+
|
|
18
|
+
All app settings live in one typed file, `config.server.ts`. Environment variables override its values per environment; `defineConfig()` deep-merges them at startup. Full references ship in the project: `docs/README-CONFIG.md` (required/optional variable tables) and `docs/README-CONFIG-OPTIONS.md` (every option).
|
|
19
|
+
|
|
20
|
+
## Model
|
|
21
|
+
|
|
22
|
+
`config.server.ts` default-exports `defineConfig<Config>({ metadata, runtime, app }, { protectedPaths })`, imported from `@salesforce/storefront-next-runtime/config`.
|
|
23
|
+
|
|
24
|
+
| Section | Visible to | Contents |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `app` | Server and browser (`window.__APP_CONFIG__`) | Commerce API, sites, features, URL/SEO routes, images, security, engagement |
|
|
27
|
+
| `runtime` | Server only | MRT settings such as `ssrParameters` (`ssrFunctionNodeVersion`, `envBasePath`), `ssrOnly`, `ssrShared` |
|
|
28
|
+
| `metadata` | Server only | Project name/slug |
|
|
29
|
+
|
|
30
|
+
Because `app` reaches the browser, never put secrets in it.
|
|
31
|
+
|
|
32
|
+
## Add a config value
|
|
33
|
+
|
|
34
|
+
1. Add the field to `AppConfig` in `src/types/config.ts` (`Config = BaseConfig<AppConfig>`; the `AppConfigShape` / `ClientFacingAppConfigShape` augmentations in that file give `getConfig` and `useConfig` their types).
|
|
35
|
+
2. Set a default under `app` in `config.server.ts`. **Define the key even if the default is empty (`''`, `[]`)**; env overrides are only accepted for paths that exist here.
|
|
36
|
+
3. Override per environment: `PUBLIC__app__myFeature__enabled=true`.
|
|
37
|
+
4. Run `pnpm typecheck`.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// src/types/config.ts (inside AppConfig)
|
|
41
|
+
myFeature: { enabled: boolean; maxItems: number };
|
|
42
|
+
|
|
43
|
+
// config.server.ts (inside app)
|
|
44
|
+
myFeature: { enabled: false, maxItems: 10 },
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Read config
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { getConfig, useConfig } from '@salesforce/storefront-next-runtime/config';
|
|
51
|
+
|
|
52
|
+
// Loaders, actions, middleware (server): context is REQUIRED
|
|
53
|
+
export function loader({ context }: LoaderFunctionArgs) {
|
|
54
|
+
const config = getConfig(context); // full AppConfig incl. serverExtension
|
|
55
|
+
return { max: config.myFeature.maxItems };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Components
|
|
59
|
+
const config = useConfig(); // client-facing shape (no serverExtension)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- `getConfig()` with no argument is for browser-only modules (reads `window.__APP_CONFIG__`); it returns the client-facing shape. Calling it on the server without a context **throws** "Configuration not available".
|
|
63
|
+
- Routes have no `clientLoader` (forbidden by the project rules), so server code always passes `context`.
|
|
64
|
+
- Middleware can read `context.get(appConfigContext)` (exported from the same module).
|
|
65
|
+
- In tests use `mockConfig`, `mockBuildConfig`, `ConfigWrapper`, `createConfigWrapper` from `@/test-utils/config`.
|
|
66
|
+
|
|
67
|
+
## Environment variable rules
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
PUBLIC__app__<path>__<to>__<key>=value -> config.app.<path>.<to>.<key>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Rule | Detail |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `PUBLIC__` prefix | Merged into config and **exposed to the browser**. Client IDs, flags, site lists only |
|
|
76
|
+
| No prefix | Server-only; not part of config. Read with `process.env` in server code |
|
|
77
|
+
| Path must exist | An unknown path is skipped with `[Config Warning] Ignoring environment variable ...` in the server log, so a typo is silent to the user |
|
|
78
|
+
| Parsing | Numbers, `true`/`false` (as strings), JSON arrays/objects; empty value becomes `''` |
|
|
79
|
+
| Precedence | Deeper (more specific) paths win over a JSON blob at a parent path |
|
|
80
|
+
| Case | Path matching is case-insensitive |
|
|
81
|
+
| Depth | Max 10 segments; use a JSON value for deeper structures |
|
|
82
|
+
| Restart | `.env` is read at startup; restart `pnpm dev` |
|
|
83
|
+
|
|
84
|
+
### Required variables
|
|
85
|
+
|
|
86
|
+
Only three are required to boot (`.env` locally; MRT environment variables when deployed):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
PUBLIC__app__commerce__api__clientId=...
|
|
90
|
+
PUBLIC__app__commerce__api__organizationId=...
|
|
91
|
+
PUBLIC__app__commerce__api__shortCode=...
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Do not set `PUBLIC__app__commerce__api__siteId`; that path does not exist and is ignored with a warning. To point at your own site, set `PUBLIC__app__defaultSiteId` and `PUBLIC__app__commerce__sites` (see [MULTI-SITE-URLS.md](references/MULTI-SITE-URLS.md)). Everything else is optional; see [ENV-VARIABLES.md](references/ENV-VARIABLES.md).
|
|
95
|
+
|
|
96
|
+
### Debug "my env var is not applied"
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pnpm config:inspect # shows each value and whether it comes from config.server.ts, .env, or MRT
|
|
100
|
+
pnpm sfnext config inspect --project my-project --environment staging # include MRT env values
|
|
101
|
+
pnpm config:push-env # push .env variables to an MRT environment (b2c mrt env var push)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The `config:inspect` script echoes a tip after the command, so call `sfnext config inspect` directly when passing flags.
|
|
105
|
+
|
|
106
|
+
Checklist: `PUBLIC__` with a double underscore, path exists in `config.server.ts`, booleans are the string `true`, dev server restarted, variable set on the right MRT environment, and the path is not protected.
|
|
107
|
+
|
|
108
|
+
## Protected paths
|
|
109
|
+
|
|
110
|
+
`defineConfig(..., { protectedPaths })` locks paths against env override. Setting a protected path, or a parent of one (e.g. `PUBLIC__app__url`), **throws at startup**. The template protects:
|
|
111
|
+
|
|
112
|
+
- `app__url__prefix`, `app__url__excludeRoutes`, `app__url__seoRoutes` (compiled into routes at build time; change `config.server.ts` and rebuild/redeploy)
|
|
113
|
+
- `app__engagement__adapters__einstein`, `...__data360`, `...__activeData__enabled`, `...__activeData__eventToggles`
|
|
114
|
+
|
|
115
|
+
The rest of `app.engagement` is overridable. Check the `protectedConfigPaths` export in your `config.server.ts` for the current list.
|
|
116
|
+
|
|
117
|
+
## Secrets
|
|
118
|
+
|
|
119
|
+
Server-only values are plain env vars read via `process.env` in server routes/middleware, never through config:
|
|
120
|
+
|
|
121
|
+
- `COMMERCE_API_SLAS_SECRET`: only for a private SLAS client (`commerce.api.privateKeyEnabled`). A public client needs no secret.
|
|
122
|
+
- `GUEST_ORDER_LOOKUP_COOKIE_SECRET`: required when guest order lookup is enabled.
|
|
123
|
+
- `MARKETING_CLOUD_*`: only for email-mode passwordless/reset with your own Marketing Cloud tenant.
|
|
124
|
+
|
|
125
|
+
Extension `config.ts` / `server-config.ts` must not read `process.env` (an AST check fails discovery).
|
|
126
|
+
|
|
127
|
+
## Extension config
|
|
128
|
+
|
|
129
|
+
Extensions add config without touching core files:
|
|
130
|
+
|
|
131
|
+
- `src/extensions/<name>/config.ts` (default-export an object) is merged into `app.extension.<camelCaseFolder>`; override with `PUBLIC__app__extension__<key>__<setting>`.
|
|
132
|
+
- `src/extensions/<name>/server-config.ts` is merged into `app.serverExtension.<camelCaseFolder>`, available only through `getConfig(context)`; no env override, and the build fails if a client chunk imports it.
|
|
133
|
+
- `pnpm config:aggregate-extensions` regenerates the merged files (run automatically by `dev`, `build`, `typecheck`).
|
|
134
|
+
|
|
135
|
+
## Multi-site and URLs
|
|
136
|
+
|
|
137
|
+
Short version: the current site comes from the request (site-context middleware), not from `commerce.sites[0]`. Use `useSite()` from `@salesforce/storefront-next-runtime/site-context` in components and `context.get(siteContext)` (same module) on the server. URL shape, `seoRoutes`, aliases, MRT Data Store sites, cookie domain, multiple domains, and base path are in [MULTI-SITE-URLS.md](references/MULTI-SITE-URLS.md).
|
|
138
|
+
|
|
139
|
+
## Pre-launch config gate
|
|
140
|
+
|
|
141
|
+
Before going live, review these defaults in `config.server.ts`:
|
|
142
|
+
|
|
143
|
+
1. `app.engagement.adapters` (Einstein, Active Data, Data 360): defaults carry **demo IDs/hosts**. Enable only with your own values, or disable them; also confirm consent categories.
|
|
144
|
+
2. `images.host` defaults to the DIS **staging** host (`edge.disstg...`); use the production DIS host, and add `realmHostMappings` for custom domains.
|
|
145
|
+
3. `.env` / MRT variables point at your own client ID, organization ID, short code, and site(s), not the demo backend.
|
|
146
|
+
4. `commerce.sites`, `defaultSiteId`, and `siteAliasMap` match your Business Manager sites; `url.seoRoutes` has an entry for every active site if enabled.
|
|
147
|
+
5. Cookie domain (`cookies.domain`) matches Business Manager Hybrid Auth level if used.
|
|
148
|
+
6. Security headers / CSP (`app.security.headers`) and Turnstile keys are reviewed (`storefront-next:sfnext-security`).
|
|
149
|
+
|
|
150
|
+
## Related Skills
|
|
151
|
+
|
|
152
|
+
- `storefront-next:sfnext-project-setup` - Create the project, `.env` basics, scripts
|
|
153
|
+
- `storefront-next:sfnext-deployment` - Push bundles and sync MRT env vars
|
|
154
|
+
- `storefront-next:sfnext-routing` - Site-aware links and SEO route behavior
|
|
155
|
+
- `storefront-next:sfnext-i18n` - Locales, translations, locale switching
|
|
156
|
+
- `storefront-next:sfnext-extensions` - Extension authoring
|
|
157
|
+
- `storefront-next:sfnext-hybrid-storefronts` - Hybrid proxy and shared cookies with SFRA
|
|
158
|
+
- `storefront-next:sfnext-security` - Security headers, CSP, Turnstile
|
|
159
|
+
- `b2c-cli:b2c-mrt` - MRT environment variable management
|
|
160
|
+
|
|
161
|
+
## Finding more
|
|
162
|
+
|
|
163
|
+
Project `docs/README-CONFIG.md`, `docs/README-CONFIG-OPTIONS.md`, `docs/README-MULTI-SITE.md`; `b2c docs search "<topic>" --category sfnext`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Environment Variables
|
|
2
|
+
|
|
3
|
+
Authoritative, current tables ship in the project: `docs/README-CONFIG.md` ("Required vs Optional Variables") and `docs/README-CONFIG-OPTIONS.md`. This file summarizes the categories; check those docs for defaults.
|
|
4
|
+
|
|
5
|
+
## Where variables come from
|
|
6
|
+
|
|
7
|
+
| Source | Used by |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `.env` in the project root | `pnpm dev`, `pnpm start`, and `sfnext` commands (`push`, `config inspect`, ...). `.env.default` is a template only and is never loaded |
|
|
10
|
+
| Managed Runtime environment variables | The deployed storefront. Set in Runtime Admin or with `b2c mrt env var set` / `b2c mrt env var push` (`pnpm config:push-env`) |
|
|
11
|
+
|
|
12
|
+
`.env` is not uploaded by `pnpm push`. See `storefront-next:sfnext-deployment`.
|
|
13
|
+
|
|
14
|
+
## Required to run
|
|
15
|
+
|
|
16
|
+
`PUBLIC__app__commerce__api__clientId`, `PUBLIC__app__commerce__api__organizationId`, `PUBLIC__app__commerce__api__shortCode`.
|
|
17
|
+
|
|
18
|
+
## Required to push to Managed Runtime
|
|
19
|
+
|
|
20
|
+
| Variable | Notes |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `MRT_PROJECT` | MRT project slug (also `--project`, `SFCC_MRT_PROJECT`, or dw.json `mrtProject`). Set it explicitly |
|
|
23
|
+
| `MRT_TARGET` | Target environment (also `--environment`). Optional unless using `--wait` |
|
|
24
|
+
| `MRT_API_KEY` | API key (or `MRT_CREDENTIALS_FILE` / `~/.mobify`). Also `MRT_CLOUD_ORIGIN` for a non-default MRT origin |
|
|
25
|
+
|
|
26
|
+
These are read by the `sfnext` CLI; they are not storefront runtime config.
|
|
27
|
+
|
|
28
|
+
## Common optional `PUBLIC__` overrides
|
|
29
|
+
|
|
30
|
+
| Variable | Effect |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `PUBLIC__app__defaultSiteId` | Default site ID |
|
|
33
|
+
| `PUBLIC__app__commerce__sites` | JSON array of site definitions |
|
|
34
|
+
| `PUBLIC__app__commerce__sitesFromDal` | `false` keeps static `commerce.sites` authoritative instead of MRT Data Store sites |
|
|
35
|
+
| `PUBLIC__app__cookies__domain` | Cookie domain for all storefront cookies |
|
|
36
|
+
| `PUBLIC__app__commerce__api__privateKeyEnabled` | Use a private SLAS client (needs `COMMERCE_API_SLAS_SECRET`) |
|
|
37
|
+
| `PUBLIC__app__commerce__api__proxy`, `...__callback` | SCAPI proxy and OAuth callback paths |
|
|
38
|
+
| `PUBLIC__app__hybrid__enabled` | Hybrid mode |
|
|
39
|
+
| `PUBLIC__app__features__*` | Feature toggles (passwordless login, social login, shopper context, MRT-based Page Designer resolution, ...) |
|
|
40
|
+
| `PUBLIC__app__security__turnstile__*` | Turnstile bot protection |
|
|
41
|
+
| `PUBLIC__app__commerce__shopperAgent` | Shopper Agent widget config (the older `PUBLIC__app__cimulateAgent` is deprecated) |
|
|
42
|
+
| `PUBLIC__app__extension__<key>__<setting>` | Extension config overrides |
|
|
43
|
+
|
|
44
|
+
## Server-only (no prefix)
|
|
45
|
+
|
|
46
|
+
`COMMERCE_API_SLAS_SECRET` (private client only), `GUEST_ORDER_LOOKUP_COOKIE_SECRET`, `MARKETING_CLOUD_CLIENT_ID` / `_CLIENT_SECRET` / `_AUTH_BASE_URL` / `_REST_BASE_URL`, `SFCC_LOG_LEVEL` (`error|warn|info|debug`), and local-dev hybrid proxy variables (`HYBRID_PROXY_ENABLED`, `HYBRID_ROUTING_RULES`, `HYBRID_PROXY_LOCALE`, `SFCC_ORIGIN`; see `storefront-next:sfnext-hybrid-storefronts`).
|
|
47
|
+
|
|
48
|
+
## Gotchas
|
|
49
|
+
|
|
50
|
+
- Unknown `PUBLIC__` paths are ignored with a server-log warning; define the key in `config.server.ts` first (an empty default is enough).
|
|
51
|
+
- Protected paths (`app__url__*`, selected engagement adapter paths) throw if set via env; see SKILL.md.
|
|
52
|
+
- Booleans must be the string `true`/`false`. Objects and arrays are JSON strings.
|
|
53
|
+
- Multi-line JSON is supported in `.env`; on MRT, keep values compact.
|
|
54
|
+
- MRT limits total variable size and name length; see Salesforce's [Environment Variables constraints](https://developer.salesforce.com/docs/commerce/sfnext/guide/sfnext-mrt-environment-vars.html) for the current numbers rather than relying on a figure here.
|
|
55
|
+
- Vite also loads `.env.<mode>` files by build mode.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Multi-Site, URLs, Domains, Base Path, and Cookie Domain
|
|
2
|
+
|
|
3
|
+
Everything that decides which site/locale a request is for and what URLs look like. Depth ships in the project: `docs/README-MULTI-SITE.md` (URL config, SEO routes, switchers), `docs/README-MULTI-DOMAIN.md`, `docs/README-BASE-PATH.md`, `docs/README-COOKIE-DOMAIN.md`, and `docs/migrations/seo-url-rules/README.md`. Read the relevant one before changing these settings.
|
|
4
|
+
|
|
5
|
+
## Quick decision table
|
|
6
|
+
|
|
7
|
+
| Goal | Setting | Rebuild needed? |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Add a site or locale | `commerce.sites` (or MRT Data Store sites), i18n locale files | No (env) |
|
|
10
|
+
| Change what the site/locale segments look like | `app.siteAliasMap`, `app.localeAliasMap` | No |
|
|
11
|
+
| Change URL shape (`/:siteId/:localeId`) | `app.url.prefix` / `search` / `excludeRoutes` | **Yes**, protected |
|
|
12
|
+
| Custom product/category URL prefixes | `app.url.seoRoutes` | **Yes**, protected |
|
|
13
|
+
| Many domains, one environment | CDN + `X-Forwarded-Host` (automatic), `images.realmHostMappings` | No |
|
|
14
|
+
| One domain, many environments | `runtime.ssrParameters.envBasePath` | Yes, pushed with `pnpm push` |
|
|
15
|
+
| Share cookies across subdomains | `app.cookies.domain` / per-site override + Business Manager | No |
|
|
16
|
+
|
|
17
|
+
## Sites and locales
|
|
18
|
+
|
|
19
|
+
Sites are defined in `config.server.ts` under `app.commerce.sites` and selected with `app.defaultSiteId`. Override from the environment:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
PUBLIC__app__defaultSiteId=MySite
|
|
23
|
+
PUBLIC__app__commerce__sites='[{"id":"MySite","defaultLocale":"en-US","defaultCurrency":"USD","supportedLocales":[{"id":"en-US","preferredCurrency":"USD"},{"id":"de-DE","preferredCurrency":"EUR"}],"supportedCurrencies":["USD","EUR"]}]'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- Each `supportedLocales` entry needs matching translation files under `src/locales/<locale>/` and to be listed in `app.i18n.supportedLngs`, otherwise the locale selector hides it. The currency switcher only shows when the site has more than one supported currency (`storefront-next:sfnext-i18n`).
|
|
27
|
+
- `commerce.sitesFromDal` is **on by default**: live site data from the MRT Data Store replaces the static `commerce.sites` for site/locale/currency resolution. If the Data Store is unavailable, or the default site is missing from it, the static list is used. Opt out with `PUBLIC__app__commerce__sitesFromDal=false`. URL aliases always come from `siteAliasMap` / `localeAliasMap`, never from the Data Store.
|
|
28
|
+
- There is no `siteId` under `commerce.api`; the site is a per-request parameter that `createApiClients(context)` adds to every SCAPI call.
|
|
29
|
+
|
|
30
|
+
### Reading the current site
|
|
31
|
+
|
|
32
|
+
The site is resolved per request by the site-context middleware from the URL, cookie, or header.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// Component
|
|
36
|
+
import { useSite } from '@salesforce/storefront-next-runtime/site-context';
|
|
37
|
+
const { site, language, currency } = useSite(); // throws outside SiteProvider (mounted in root.tsx)
|
|
38
|
+
|
|
39
|
+
// Loader / action / middleware
|
|
40
|
+
import { siteContext } from '@salesforce/storefront-next-runtime/site-context';
|
|
41
|
+
const siteId = context.get(siteContext)?.site?.id;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Never use `config.commerce.sites[0]` as "the current site".
|
|
45
|
+
|
|
46
|
+
## URL shape (`app.url`)
|
|
47
|
+
|
|
48
|
+
Default in the template:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
url: {
|
|
52
|
+
prefix: '/:siteId/:localeId',
|
|
53
|
+
excludeRoutes: ['/resource/**', '/action/**'],
|
|
54
|
+
},
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Pages then live at `/global/en-GB/product/...`; bare `/` is redirected to the default site/locale by the home route loader. `prefix` and `search` accept `:siteId` and `:localeId`; alternatives (locale only, site in path with `?lng=`, everything in query params) are in README-MULTI-SITE "URL Config Use Cases".
|
|
58
|
+
|
|
59
|
+
Rules:
|
|
60
|
+
|
|
61
|
+
- `url.prefix`, `url.excludeRoutes`, `url.seoRoutes` are **protected**: compiled into routes at build time. Edit `config.server.ts`, rebuild, redeploy. Setting `PUBLIC__app__url__...` throws.
|
|
62
|
+
- If you change the prefix order or drop site/locale from the path, update `app.siteDetectionConfig` / `app.localeDetectionConfig` (`order`, `lookupFromPathIndex`, `lookupQuerystring`, `lookupHeader`) so detection reads back what `buildUrl` writes. Locale query param key must stay `lng`; the default site query key is `site`.
|
|
63
|
+
- Detection defaults: site order `path, querystring, cookie, header` (header `X-Site-Id`, cookie `site_id`); locale order `path, querystring, cookie, header` (cookie `lng`).
|
|
64
|
+
|
|
65
|
+
### Aliases
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
siteAliasMap: { RefArchGlobal: 'global', RefArch: 'us' }, // app.siteAliasMap
|
|
69
|
+
localeAliasMap: { 'en-US': 'us' }, // app.localeAliasMap
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Optional. Without them raw IDs appear in URLs. Changing an alias changes public URLs; plan redirects.
|
|
73
|
+
|
|
74
|
+
### Building URLs correctly
|
|
75
|
+
|
|
76
|
+
| Where | Use |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| JSX links | `Link` / `NavLink` from `@/components/link` |
|
|
79
|
+
| Imperative navigation | `useNavigate` from `@/hooks/use-navigate` |
|
|
80
|
+
| Loader/action redirects | `redirect(buildUrlFromContext('/login', context))` from `@/lib/url.server` |
|
|
81
|
+
| `<Form action>` | Prefix manually with `buildUrl` from `@salesforce/storefront-next-runtime/site-context` + `useCurrentSiteAndLocaleRef` (`@/hooks/use-current-site-and-locale-ref`); React Router `<Form>` does not prefix |
|
|
82
|
+
| Typed route patterns | `routeHref` from `@/route-paths` |
|
|
83
|
+
|
|
84
|
+
Importing `Link`/`useNavigate` from `react-router` produces unprefixed URLs that 404.
|
|
85
|
+
|
|
86
|
+
### SEO routes (`url.seoRoutes`)
|
|
87
|
+
|
|
88
|
+
Replicate Business Manager product/category URL prefixes into the route table, keyed by Commerce site ID:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
url: {
|
|
92
|
+
prefix: '/:siteId/:localeId',
|
|
93
|
+
excludeRoutes: ['/resource/**', '/action/**'],
|
|
94
|
+
seoRoutes: {
|
|
95
|
+
RefArchGlobal: { product: { prefix: 'p' }, category: { prefix: 'c', mode: 'id-suffix' } },
|
|
96
|
+
RefArch: { product: { prefix: 'product' }, category: { prefix: 'category', mode: 'id-suffix' } },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- Every active site needs an entry, or the build fails for the omitted ones.
|
|
102
|
+
- Category `mode`: `id-suffix` (deterministic, ID in last segment) or `slug-path` (needs Shopper Products 1.13 and Shopper Search 1.15 on the instance).
|
|
103
|
+
- Prefixes are single static segments; invalid, reserved, duplicate, or colliding prefixes fail the build. The canonical product/category route modules must be leaf routes.
|
|
104
|
+
- Build links with `createProductUrl` / `createCategoryUrl*` and `useSeoUrlContext()` rather than hand-built paths. Mapping from Business Manager is manual and one-directional.
|
|
105
|
+
- Legacy or unmatched URLs can resolve via the Shopper SEO URL mapping fallback (`app.seoFallback.sites`). Read `docs/migrations/seo-url-rules/README.md` before enabling (redirect loops, indexed URLs).
|
|
106
|
+
|
|
107
|
+
## Multiple domains, one environment
|
|
108
|
+
|
|
109
|
+
One MRT environment can serve many domains. The public origin is taken per request from `X-Forwarded-Host` / `X-Forwarded-Proto`, so links, SLAS `redirect_uri`, magic-link emails, canonical/hreflang tags, and JSON-LD use the domain the shopper used. Nothing to configure in the storefront except:
|
|
110
|
+
|
|
111
|
+
- Attach each domain as an external hostname on the MRT environment and add each domain's callback URLs to the SLAS client's redirect list.
|
|
112
|
+
- `EXTERNAL_DOMAIN_NAME` is only a fallback when no forwarded host exists (startup, local dev); leave the default.
|
|
113
|
+
- `images.realmHostMappings` (`[{ hostSuffix, realm }]`) so DIS resolves the realm on custom domains.
|
|
114
|
+
- To map each domain to a different site, have the CDN set `X-Site-Id` per domain and configure `url.prefix: '/:localeId'`, `siteDetectionConfig: { order: ['header','cookie'], lookupHeader: 'X-Site-Id' }`, `localeDetectionConfig: { lookupFromPathIndex: 0 }`. If the edge cannot inject headers, keep the site in the URL path.
|
|
115
|
+
- Keep cookies host-only across unrelated brand domains.
|
|
116
|
+
|
|
117
|
+
## Base path (one domain, many environments)
|
|
118
|
+
|
|
119
|
+
`runtime.ssrParameters.envBasePath: '/storefront-a'` in `config.server.ts` serves one environment under a path segment (single segment, `/` + up to 63 URL-safe chars). The value is sent to MRT during `pnpm push`; the CDN routes by that first segment and MRT does not strip it. Routes, bundle asset URLs, and React Router `basename` adapt automatically, and site/locale path detection skips the base path.
|
|
120
|
+
|
|
121
|
+
Caveats: raw `fetch()` / `sendBeacon()` from client code must prepend `getBasePath()` from `@/lib/utils`; `redirect()` in middleware is not auto-prefixed; `request.url` still includes the base path; fixed Express routes (e.g. health check) are served without it. Locally `pnpm dev` / `pnpm start` read it from `config.server.ts` and redirect unprefixed requests.
|
|
122
|
+
|
|
123
|
+
## Cookie domain
|
|
124
|
+
|
|
125
|
+
Cookies are host-only by default. To share a session across subdomains (or with SFRA in a hybrid setup):
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
PUBLIC__app__cookies__domain=.example.com # global default
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Per-site override: `commerce.sites[].cookies.domain` (wins over the global value). The domain applies to every cookie the storefront writes (auth/session and site-context). It must be a parent of the serving host or browsers silently drop the cookies. Do **not** set `app.siteContext.cookieOptions.domain` (ignored).
|
|
132
|
+
|
|
133
|
+
Business Manager must agree: **Merchant Tools > Site Preferences > Hybrid Auth Settings** cookie-domain level `0` (host only) when unset, `2` (first-level parent domain) when set. Level `1` is rejected. A mismatch breaks cross-subdomain sessions or creates duplicate cookies. Verify with `curl -sI https://www.example.com/ | grep -i set-cookie` (same `Domain=` on every cookie, no duplicates). See `storefront-next:sfnext-hybrid-storefronts` for the hybrid side.
|
|
134
|
+
|
|
135
|
+
## Switchers
|
|
136
|
+
|
|
137
|
+
Site, locale, and currency switchers post to `/action/set-site-context` (which sets `site_id`, `lng`, and the currency cookie and redirects). Currency resolution order: cookie, then the locale's `preferredCurrency`, then the site's `defaultCurrency`.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sfnext-data-fetching
|
|
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`).
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Storefront Next Data Fetching
|
|
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.
|
|
10
|
+
|
|
11
|
+
## The rules (same as AGENTS.md)
|
|
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`).
|
|
21
|
+
|
|
22
|
+
## Loader with critical and streamed data
|
|
23
|
+
|
|
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';
|
|
31
|
+
|
|
32
|
+
export async function loader({ context, params }: Route.LoaderArgs) {
|
|
33
|
+
const clients = createApiClients(context);
|
|
34
|
+
|
|
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 ?? []);
|
|
39
|
+
|
|
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
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
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
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
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.
|
|
64
|
+
|
|
65
|
+
## SCAPI clients
|
|
66
|
+
|
|
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:
|
|
68
|
+
|
|
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
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
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.
|
|
77
|
+
|
|
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).
|
|
79
|
+
|
|
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.
|
|
81
|
+
|
|
82
|
+
## Mutations: actions
|
|
83
|
+
|
|
84
|
+
Prefer an `action.*` route and a `<Form>` (navigating) or `useFetcher` (non-navigating).
|
|
85
|
+
|
|
86
|
+
Basket mutations use the factory, which loads the basket, parses `FormData`, catches errors and wraps the result:
|
|
87
|
+
|
|
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';
|
|
94
|
+
|
|
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
|
+
```
|
|
106
|
+
|
|
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).
|
|
108
|
+
|
|
109
|
+
## Fetching from components
|
|
110
|
+
|
|
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.
|
|
115
|
+
|
|
116
|
+
## Checklist
|
|
117
|
+
|
|
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`.
|
|
124
|
+
|
|
125
|
+
Deeper: [LOADERS.md](references/LOADERS.md).
|
|
126
|
+
|
|
127
|
+
## Finding more
|
|
128
|
+
|
|
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.
|
|
130
|
+
|
|
131
|
+
## Related Skills
|
|
132
|
+
|
|
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,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`.
|