@salesforce/b2c-cli 2.3.0 → 2.4.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.
Files changed (421) hide show
  1. package/bin/dev.js +8 -8
  2. package/bin/run.js +5 -7
  3. package/content/guidance/b2c/b2c-business-manager-extensions/SKILL.md +361 -0
  4. package/content/guidance/b2c/b2c-business-manager-extensions/references/EXTENSIONS-XML.md +458 -0
  5. package/content/guidance/b2c/b2c-controllers/SKILL.md +301 -0
  6. package/content/guidance/b2c/b2c-controllers/references/CLASSIC-PATTERNS.md +335 -0
  7. package/content/guidance/b2c/b2c-controllers/references/SFRA-PATTERNS.md +400 -0
  8. package/content/guidance/b2c/b2c-custom-api-development/SKILL.md +281 -0
  9. package/content/guidance/b2c/b2c-custom-api-development/references/CONTRACT.md +142 -0
  10. package/content/guidance/b2c/b2c-custom-api-development/references/IMPLEMENTATION.md +153 -0
  11. package/content/guidance/b2c/b2c-custom-api-development/references/TESTING.md +118 -0
  12. package/content/guidance/b2c/b2c-custom-caches/SKILL.md +279 -0
  13. package/content/guidance/b2c/b2c-custom-job-steps/SKILL.md +520 -0
  14. package/content/guidance/b2c/b2c-custom-job-steps/references/CHUNK-ORIENTED.md +377 -0
  15. package/content/guidance/b2c/b2c-custom-job-steps/references/JOBS-XML.md +212 -0
  16. package/content/guidance/b2c/b2c-custom-job-steps/references/STEPTYPES-JSON.md +373 -0
  17. package/content/guidance/b2c/b2c-custom-job-steps/references/TASK-ORIENTED.md +344 -0
  18. package/content/guidance/b2c/b2c-custom-objects/SKILL.md +327 -0
  19. package/content/guidance/b2c/b2c-custom-objects/references/OCAPI-SEARCH.md +298 -0
  20. package/content/guidance/b2c/b2c-forms/SKILL.md +242 -0
  21. package/content/guidance/b2c/b2c-forms/references/FORM-XML.md +409 -0
  22. package/content/guidance/b2c/b2c-hooks/SKILL.md +500 -0
  23. package/content/guidance/b2c/b2c-hooks/references/OCAPI-SCAPI-HOOKS.md +403 -0
  24. package/content/guidance/b2c/b2c-hooks/references/ORDER-HOOK-LIFECYCLE.md +169 -0
  25. package/content/guidance/b2c/b2c-hooks/references/SYSTEM-HOOKS.md +433 -0
  26. package/content/guidance/b2c/b2c-isml/SKILL.md +320 -0
  27. package/content/guidance/b2c/b2c-isml/references/EXPRESSIONS.md +366 -0
  28. package/content/guidance/b2c/b2c-isml/references/TAGS.md +443 -0
  29. package/content/guidance/b2c/b2c-localization/SKILL.md +344 -0
  30. package/content/guidance/b2c/b2c-localization/references/PATTERNS.md +407 -0
  31. package/content/guidance/b2c/b2c-logging/SKILL.md +352 -0
  32. package/content/guidance/b2c/b2c-logging/references/LOG-FILES.md +282 -0
  33. package/content/guidance/b2c/b2c-metadata/SKILL.md +406 -0
  34. package/content/guidance/b2c/b2c-metadata/references/SYSTEM-OBJECTS.md +320 -0
  35. package/content/guidance/b2c/b2c-metadata/references/XML-EXAMPLES.md +366 -0
  36. package/content/guidance/b2c/b2c-onboarding/SKILL.md +154 -0
  37. package/content/guidance/b2c/b2c-ordering/SKILL.md +391 -0
  38. package/content/guidance/b2c/b2c-page-designer/SKILL.md +410 -0
  39. package/content/guidance/b2c/b2c-page-designer/references/ATTRIBUTE-TYPES.md +436 -0
  40. package/content/guidance/b2c/b2c-page-designer/references/META-DEFINITIONS.md +340 -0
  41. package/content/guidance/b2c/b2c-querying-data/SKILL.md +289 -0
  42. package/content/guidance/b2c/b2c-querying-data/references/PERFORMANCE-APIS.md +74 -0
  43. package/content/guidance/b2c/b2c-scapi-admin/SKILL.md +53 -0
  44. package/content/guidance/b2c/b2c-scapi-admin/references/CLIENT-EXAMPLES.md +440 -0
  45. package/content/guidance/b2c/b2c-scapi-admin/references/INTEGRATION-PATTERNS.md +518 -0
  46. package/content/guidance/b2c/b2c-scapi-admin/references/OAUTH-SCOPES.md +337 -0
  47. package/content/guidance/b2c/b2c-scapi-shopper/SKILL.md +56 -0
  48. package/content/guidance/b2c/b2c-scapi-shopper/references/CHECKOUT-FLOW.md +466 -0
  49. package/content/guidance/b2c/b2c-scapi-shopper/references/CLIENT-EXAMPLES.md +351 -0
  50. package/content/guidance/b2c/b2c-scapi-shopper/references/COMMON-PATTERNS.md +390 -0
  51. package/content/guidance/b2c/b2c-scapi-shopper/references/SCOPES.md +290 -0
  52. package/content/guidance/b2c/b2c-slas-auth-patterns/SKILL.md +420 -0
  53. package/content/guidance/b2c/b2c-slas-auth-patterns/references/PASSKEYS.md +126 -0
  54. package/content/guidance/b2c/b2c-slas-auth-patterns/references/SESSION-BRIDGE.md +267 -0
  55. package/content/guidance/b2c/b2c-slas-auth-patterns/references/TOKEN-LIFECYCLE.md +367 -0
  56. package/content/guidance/b2c/b2c-webservices/SKILL.md +318 -0
  57. package/content/guidance/b2c/b2c-webservices/references/FTP-SERVICES.md +524 -0
  58. package/content/guidance/b2c/b2c-webservices/references/HTTP-SERVICES.md +578 -0
  59. package/content/guidance/b2c/b2c-webservices/references/SERVICES-XML.md +351 -0
  60. package/content/guidance/b2c/b2c-webservices/references/SOAP-SERVICES.md +587 -0
  61. package/content/guidance/b2c-cli/b2c-am/SKILL.md +277 -0
  62. package/content/guidance/b2c-cli/b2c-bm-users-roles/SKILL.md +210 -0
  63. package/content/guidance/b2c-cli/b2c-cap/SKILL.md +131 -0
  64. package/content/guidance/b2c-cli/b2c-cip/SKILL.md +116 -0
  65. package/content/guidance/b2c-cli/b2c-cip/references/KNOWN_TABLES.md +105 -0
  66. package/content/guidance/b2c-cli/b2c-cip/references/SALES_ANALYSIS.md +50 -0
  67. package/content/guidance/b2c-cli/b2c-cip/references/STARTER_QUERIES.md +147 -0
  68. package/content/guidance/b2c-cli/b2c-code/SKILL.md +146 -0
  69. package/content/guidance/b2c-cli/b2c-config/SKILL.md +462 -0
  70. package/content/guidance/b2c-cli/b2c-content/SKILL.md +176 -0
  71. package/content/guidance/b2c-cli/b2c-debug/SKILL.md +138 -0
  72. package/content/guidance/b2c-cli/b2c-docs/SKILL.md +301 -0
  73. package/content/guidance/b2c-cli/b2c-ecdn/SKILL.md +135 -0
  74. package/content/guidance/b2c-cli/b2c-ecdn/references/ADVANCED.md +97 -0
  75. package/content/guidance/b2c-cli/b2c-ecdn/references/SECURITY.md +75 -0
  76. package/content/guidance/b2c-cli/b2c-import-set-migrations/SKILL.md +265 -0
  77. package/content/guidance/b2c-cli/b2c-job/SKILL.md +64 -0
  78. package/content/guidance/b2c-cli/b2c-job/references/EXPORT.md +121 -0
  79. package/content/guidance/b2c-cli/b2c-job/references/IMPORT.md +55 -0
  80. package/content/guidance/b2c-cli/b2c-job/references/RUN-AND-MONITOR.md +119 -0
  81. package/content/guidance/b2c-cli/b2c-logs/SKILL.md +249 -0
  82. package/content/guidance/b2c-cli/b2c-metrics/SKILL.md +315 -0
  83. package/content/guidance/b2c-cli/b2c-mrt/SKILL.md +207 -0
  84. package/content/guidance/b2c-cli/b2c-mrt/references/BUNDLE-COMMANDS.md +203 -0
  85. package/content/guidance/b2c-cli/b2c-mrt/references/ENVIRONMENT-COMMANDS.md +218 -0
  86. package/content/guidance/b2c-cli/b2c-mrt/references/PROJECT-COMMANDS.md +154 -0
  87. package/content/guidance/b2c-cli/b2c-sandbox/SKILL.md +112 -0
  88. package/content/guidance/b2c-cli/b2c-scapi-custom/SKILL.md +126 -0
  89. package/content/guidance/b2c-cli/b2c-scapi-schemas/SKILL.md +66 -0
  90. package/content/guidance/b2c-cli/b2c-scapi-schemas/references/CLI-EXAMPLES.md +108 -0
  91. package/content/guidance/b2c-cli/b2c-site-import-export/SKILL.md +57 -0
  92. package/content/guidance/b2c-cli/b2c-site-import-export/references/IMPORT-OPTIONS.md +118 -0
  93. package/content/guidance/b2c-cli/b2c-site-import-export/references/METADATA-XML.md +381 -0
  94. package/content/guidance/b2c-cli/b2c-site-import-export/references/WORKFLOWS.md +182 -0
  95. package/content/guidance/b2c-cli/b2c-sites/SKILL.md +112 -0
  96. package/content/guidance/b2c-cli/b2c-slas/SKILL.md +182 -0
  97. package/content/guidance/b2c-cli/b2c-webdav/SKILL.md +186 -0
  98. package/content/guidance/b2c-ops/b2c-checkout-triage/SKILL.md +68 -0
  99. package/content/guidance/b2c-ops/b2c-job-health/SKILL.md +75 -0
  100. package/content/guidance/b2c-ops/b2c-job-health/references/job-logs.md +21 -0
  101. package/content/guidance/b2c-ops/b2c-order-failure-triage/SKILL.md +66 -0
  102. package/content/guidance/b2c-ops/b2c-order-failure-triage/references/order-evidence.md +87 -0
  103. package/content/guidance/b2c-ops/b2c-production-triage/SKILL.md +87 -0
  104. package/content/guidance/b2c-ops/b2c-production-triage/references/escalation.md +57 -0
  105. package/content/guidance/index.json +1949 -0
  106. package/content/guidance/storefront-next/sfnext-accessibility/SKILL.md +103 -0
  107. package/content/guidance/storefront-next/sfnext-accessibility/references/checklist.md +64 -0
  108. package/content/guidance/storefront-next/sfnext-analytics-consent/SKILL.md +91 -0
  109. package/content/guidance/storefront-next/sfnext-analytics-consent/references/CUSTOM-ADAPTER.md +49 -0
  110. package/content/guidance/storefront-next/sfnext-authentication/SKILL.md +127 -0
  111. package/content/guidance/storefront-next/sfnext-authentication/references/COOKIES.md +39 -0
  112. package/content/guidance/storefront-next/sfnext-authentication/references/LOGIN-FLOWS.md +59 -0
  113. package/content/guidance/storefront-next/sfnext-commerce-features/SKILL.md +67 -0
  114. package/content/guidance/storefront-next/sfnext-commerce-features/references/FEATURE-PREREQUISITES.md +37 -0
  115. package/content/guidance/storefront-next/sfnext-components/SKILL.md +153 -0
  116. package/content/guidance/storefront-next/sfnext-components/references/COMPONENT-AUTHORING.md +118 -0
  117. package/content/guidance/storefront-next/sfnext-components/references/SHAPE-TOKENS.md +51 -0
  118. package/content/guidance/storefront-next/sfnext-components/references/STORYBOOK.md +54 -0
  119. package/content/guidance/storefront-next/sfnext-components/references/TOKEN-SYSTEM.md +53 -0
  120. package/content/guidance/storefront-next/sfnext-components/references/TROUBLESHOOTING.md +19 -0
  121. package/content/guidance/storefront-next/sfnext-configuration/SKILL.md +163 -0
  122. package/content/guidance/storefront-next/sfnext-configuration/references/ENV-VARIABLES.md +55 -0
  123. package/content/guidance/storefront-next/sfnext-configuration/references/MULTI-SITE-URLS.md +137 -0
  124. package/content/guidance/storefront-next/sfnext-data-fetching/SKILL.md +140 -0
  125. package/content/guidance/storefront-next/sfnext-data-fetching/references/ACTIONS.md +53 -0
  126. package/content/guidance/storefront-next/sfnext-data-fetching/references/API-CLIENTS.md +34 -0
  127. package/content/guidance/storefront-next/sfnext-data-fetching/references/LOADERS.md +73 -0
  128. package/content/guidance/storefront-next/sfnext-data-fetching/references/SCAPI-FETCHER.md +39 -0
  129. package/content/guidance/storefront-next/sfnext-deployment/SKILL.md +127 -0
  130. package/content/guidance/storefront-next/sfnext-deployment/references/MRT-DEPLOYMENT.md +59 -0
  131. package/content/guidance/storefront-next/sfnext-extensions/SKILL.md +118 -0
  132. package/content/guidance/storefront-next/sfnext-extensions/references/ACTION-HOOKS.md +57 -0
  133. package/content/guidance/storefront-next/sfnext-extensions/references/BASE-AUDIT.md +68 -0
  134. package/content/guidance/storefront-next/sfnext-extensions/references/CLI-AND-INSTALL.md +58 -0
  135. package/content/guidance/storefront-next/sfnext-extensions/references/EXTENSION-EXAMPLES.md +79 -0
  136. package/content/guidance/storefront-next/sfnext-hybrid-storefronts/SKILL.md +93 -0
  137. package/content/guidance/storefront-next/sfnext-hybrid-storefronts/references/HYBRID-PROXY-CONFIG.md +86 -0
  138. package/content/guidance/storefront-next/sfnext-i18n/SKILL.md +152 -0
  139. package/content/guidance/storefront-next/sfnext-i18n/references/locale-config.md +35 -0
  140. package/content/guidance/storefront-next/sfnext-overview/SKILL.md +101 -0
  141. package/content/guidance/storefront-next/sfnext-page-designer/SKILL.md +202 -0
  142. package/content/guidance/storefront-next/sfnext-page-designer/references/BUSINESS-MANAGER.md +46 -0
  143. package/content/guidance/storefront-next/sfnext-page-designer/references/COMPONENT-REGISTRY.md +111 -0
  144. package/content/guidance/storefront-next/sfnext-page-designer/references/DECORATOR-PATTERNS.md +168 -0
  145. package/content/guidance/storefront-next/sfnext-page-designer/references/REVIEW-CHECKLIST.md +83 -0
  146. package/content/guidance/storefront-next/sfnext-page-designer/references/TROUBLESHOOTING.md +35 -0
  147. package/content/guidance/storefront-next/sfnext-performance/SKILL.md +102 -0
  148. package/content/guidance/storefront-next/sfnext-performance/references/PERFORMANCE-REVIEW-CHECKLIST.md +85 -0
  149. package/content/guidance/storefront-next/sfnext-performance/references/SUSPENSE-AND-STREAMING.md +58 -0
  150. package/content/guidance/storefront-next/sfnext-project-setup/SKILL.md +147 -0
  151. package/content/guidance/storefront-next/sfnext-project-setup/references/PROJECT-STRUCTURE.md +61 -0
  152. package/content/guidance/storefront-next/sfnext-project-setup/references/SCRIPTS.md +47 -0
  153. package/content/guidance/storefront-next/sfnext-project-setup/references/SFNEXT-CLI.md +63 -0
  154. package/content/guidance/storefront-next/sfnext-quality-gates/SKILL.md +70 -0
  155. package/content/guidance/storefront-next/sfnext-quality-gates/references/lint-and-budgets.md +39 -0
  156. package/content/guidance/storefront-next/sfnext-revalidation/SKILL.md +121 -0
  157. package/content/guidance/storefront-next/sfnext-revalidation/references/POLICIES-AND-TAGS.md +55 -0
  158. package/content/guidance/storefront-next/sfnext-routing/SKILL.md +123 -0
  159. package/content/guidance/storefront-next/sfnext-routing/references/ROUTE-CONVENTIONS.md +65 -0
  160. package/content/guidance/storefront-next/sfnext-routing/references/URLS-AND-SEO-ROUTES.md +22 -0
  161. package/content/guidance/storefront-next/sfnext-scapi/SKILL.md +126 -0
  162. package/content/guidance/storefront-next/sfnext-scapi/references/WORKED-EXAMPLE.md +39 -0
  163. package/content/guidance/storefront-next/sfnext-security/SKILL.md +129 -0
  164. package/content/guidance/storefront-next/sfnext-security/references/COOKIE-DOMAIN.md +39 -0
  165. package/content/guidance/storefront-next/sfnext-security/references/TURNSTILE.md +43 -0
  166. package/content/guidance/storefront-next/sfnext-seo/SKILL.md +95 -0
  167. package/content/guidance/storefront-next/sfnext-seo/references/MULTI-DOMAIN-BASE-PATH.md +17 -0
  168. package/content/guidance/storefront-next/sfnext-seo/references/SEO-ROUTES.md +31 -0
  169. package/content/guidance/storefront-next/sfnext-state-management/SKILL.md +114 -0
  170. package/content/guidance/storefront-next/sfnext-state-management/references/PATTERNS.md +45 -0
  171. package/content/guidance/storefront-next/sfnext-testing/SKILL.md +151 -0
  172. package/content/guidance/storefront-next/sfnext-testing/references/E2E.md +38 -0
  173. package/content/guidance/storefront-next/sfnext-testing/references/STORYBOOK-PATTERNS.md +97 -0
  174. package/content/guidance/storefront-next/sfnext-testing/references/UNIT-AND-ROUTE-TESTS.md +60 -0
  175. package/content/guidance/storefront-next/sfnext-theming/SKILL.md +104 -0
  176. package/content/guidance/storefront-next/sfnext-theming/references/REBRAND-CHECKLIST.md +29 -0
  177. package/dist/commands/cap/install.d.ts +1 -0
  178. package/dist/commands/cap/list.d.ts +1 -0
  179. package/dist/commands/cap/pull.d.ts +1 -0
  180. package/dist/commands/cap/tasks.d.ts +1 -0
  181. package/dist/commands/cap/uninstall.d.ts +1 -0
  182. package/dist/commands/cip/describe.d.ts +1 -0
  183. package/dist/commands/cip/query.d.ts +1 -0
  184. package/dist/commands/cip/report/bot-traffic-share.d.ts +1 -0
  185. package/dist/commands/cip/report/checkout-funnel-dropoff.d.ts +1 -0
  186. package/dist/commands/cip/report/controller-error-rate-trend.d.ts +1 -0
  187. package/dist/commands/cip/report/controller-health-scorecard.d.ts +1 -0
  188. package/dist/commands/cip/report/customer-registration-trends.d.ts +1 -0
  189. package/dist/commands/cip/report/discount-depth-breakdown.d.ts +1 -0
  190. package/dist/commands/cip/report/inventory-stockout-by-location.d.ts +1 -0
  191. package/dist/commands/cip/report/new-vs-returning-buyer-revenue.d.ts +1 -0
  192. package/dist/commands/cip/report/ocapi-client-usage.d.ts +1 -0
  193. package/dist/commands/cip/report/ocapi-requests.d.ts +1 -0
  194. package/dist/commands/cip/report/payment-method-performance.d.ts +1 -0
  195. package/dist/commands/cip/report/product-co-purchase-analysis.d.ts +1 -0
  196. package/dist/commands/cip/report/promotion-discount-analysis.d.ts +1 -0
  197. package/dist/commands/cip/report/promotion-roi-leaderboard.d.ts +1 -0
  198. package/dist/commands/cip/report/recommender-effectiveness.d.ts +1 -0
  199. package/dist/commands/cip/report/remote-include-performance.d.ts +1 -0
  200. package/dist/commands/cip/report/revenue-by-channel.d.ts +1 -0
  201. package/dist/commands/cip/report/sales-analytics.d.ts +1 -0
  202. package/dist/commands/cip/report/sales-summary.d.ts +1 -0
  203. package/dist/commands/cip/report/scapi-cache-hit-ratio.d.ts +1 -0
  204. package/dist/commands/cip/report/scapi-error-rate-by-status.d.ts +1 -0
  205. package/dist/commands/cip/report/scapi-latency-distribution.d.ts +1 -0
  206. package/dist/commands/cip/report/scapi-traffic-latency.d.ts +1 -0
  207. package/dist/commands/cip/report/search-query-performance.d.ts +1 -0
  208. package/dist/commands/cip/report/top-referrers.d.ts +1 -0
  209. package/dist/commands/cip/report/top-selling-products.d.ts +1 -0
  210. package/dist/commands/cip/report/zero-result-searches.d.ts +1 -0
  211. package/dist/commands/cip/tables.d.ts +1 -0
  212. package/dist/commands/code/activate.d.ts +1 -0
  213. package/dist/commands/code/delete.d.ts +1 -0
  214. package/dist/commands/code/deploy.d.ts +1 -0
  215. package/dist/commands/code/download.d.ts +1 -0
  216. package/dist/commands/code/watch.d.ts +1 -0
  217. package/dist/commands/commands/search.d.ts +35 -0
  218. package/dist/commands/commands/search.js +85 -0
  219. package/dist/commands/commands/search.js.map +1 -0
  220. package/dist/commands/content/export.d.ts +1 -0
  221. package/dist/commands/content/list.d.ts +1 -0
  222. package/dist/commands/content/validate.d.ts +1 -0
  223. package/dist/commands/debug/cli.d.ts +1 -0
  224. package/dist/commands/debug/index.d.ts +1 -0
  225. package/dist/commands/docs/cache.d.ts +1 -0
  226. package/dist/commands/docs/download.d.ts +1 -0
  227. package/dist/commands/docs/read.d.ts +1 -0
  228. package/dist/commands/docs/schema.d.ts +1 -0
  229. package/dist/commands/docs/search.d.ts +1 -0
  230. package/dist/commands/docs/skill.d.ts +41 -0
  231. package/dist/commands/docs/skill.js +183 -0
  232. package/dist/commands/docs/skill.js.map +1 -0
  233. package/dist/commands/ecdn/cache/purge.d.ts +1 -0
  234. package/dist/commands/ecdn/certificates/add.d.ts +1 -0
  235. package/dist/commands/ecdn/certificates/delete.d.ts +1 -0
  236. package/dist/commands/ecdn/certificates/list.d.ts +1 -0
  237. package/dist/commands/ecdn/certificates/update.d.ts +1 -0
  238. package/dist/commands/ecdn/certificates/validate.d.ts +1 -0
  239. package/dist/commands/ecdn/cipher-suites/get.d.ts +1 -0
  240. package/dist/commands/ecdn/cipher-suites/update.d.ts +1 -0
  241. package/dist/commands/ecdn/firewall/create.d.ts +1 -0
  242. package/dist/commands/ecdn/firewall/delete.d.ts +1 -0
  243. package/dist/commands/ecdn/firewall/get.d.ts +1 -0
  244. package/dist/commands/ecdn/firewall/list.d.ts +1 -0
  245. package/dist/commands/ecdn/firewall/reorder.d.ts +1 -0
  246. package/dist/commands/ecdn/firewall/update.d.ts +1 -0
  247. package/dist/commands/ecdn/logpush/jobs/create.d.ts +1 -0
  248. package/dist/commands/ecdn/logpush/jobs/delete.d.ts +1 -0
  249. package/dist/commands/ecdn/logpush/jobs/get.d.ts +1 -0
  250. package/dist/commands/ecdn/logpush/jobs/list.d.ts +1 -0
  251. package/dist/commands/ecdn/logpush/jobs/update.d.ts +1 -0
  252. package/dist/commands/ecdn/logpush/ownership.d.ts +1 -0
  253. package/dist/commands/ecdn/mrt-rules/create.d.ts +1 -0
  254. package/dist/commands/ecdn/mrt-rules/delete.d.ts +1 -0
  255. package/dist/commands/ecdn/mrt-rules/get.d.ts +1 -0
  256. package/dist/commands/ecdn/mrt-rules/rules/delete.d.ts +1 -0
  257. package/dist/commands/ecdn/mrt-rules/rules/update.d.ts +1 -0
  258. package/dist/commands/ecdn/mrt-rules/update.d.ts +1 -0
  259. package/dist/commands/ecdn/mtls/create.d.ts +1 -0
  260. package/dist/commands/ecdn/mtls/delete.d.ts +1 -0
  261. package/dist/commands/ecdn/mtls/get.d.ts +1 -0
  262. package/dist/commands/ecdn/mtls/issue.d.ts +1 -0
  263. package/dist/commands/ecdn/mtls/list.d.ts +1 -0
  264. package/dist/commands/ecdn/mtls/setup.d.ts +1 -0
  265. package/dist/commands/ecdn/origin-headers/delete.d.ts +1 -0
  266. package/dist/commands/ecdn/origin-headers/get.d.ts +1 -0
  267. package/dist/commands/ecdn/origin-headers/set.d.ts +1 -0
  268. package/dist/commands/ecdn/page-shield/notifications/create.d.ts +1 -0
  269. package/dist/commands/ecdn/page-shield/notifications/delete.d.ts +1 -0
  270. package/dist/commands/ecdn/page-shield/notifications/list.d.ts +1 -0
  271. package/dist/commands/ecdn/page-shield/policies/create.d.ts +1 -0
  272. package/dist/commands/ecdn/page-shield/policies/delete.d.ts +1 -0
  273. package/dist/commands/ecdn/page-shield/policies/get.d.ts +1 -0
  274. package/dist/commands/ecdn/page-shield/policies/list.d.ts +1 -0
  275. package/dist/commands/ecdn/page-shield/policies/update.d.ts +1 -0
  276. package/dist/commands/ecdn/page-shield/scripts/get.d.ts +1 -0
  277. package/dist/commands/ecdn/page-shield/scripts/list.d.ts +1 -0
  278. package/dist/commands/ecdn/rate-limit/create.d.ts +1 -0
  279. package/dist/commands/ecdn/rate-limit/delete.d.ts +1 -0
  280. package/dist/commands/ecdn/rate-limit/get.d.ts +1 -0
  281. package/dist/commands/ecdn/rate-limit/list.d.ts +1 -0
  282. package/dist/commands/ecdn/rate-limit/update.d.ts +1 -0
  283. package/dist/commands/ecdn/security/get.d.ts +1 -0
  284. package/dist/commands/ecdn/security/update.d.ts +1 -0
  285. package/dist/commands/ecdn/speed/get.d.ts +1 -0
  286. package/dist/commands/ecdn/speed/update.d.ts +1 -0
  287. package/dist/commands/ecdn/waf/groups/list.d.ts +1 -0
  288. package/dist/commands/ecdn/waf/groups/update.d.ts +1 -0
  289. package/dist/commands/ecdn/waf/managed-rules/list.d.ts +1 -0
  290. package/dist/commands/ecdn/waf/managed-rules/update.d.ts +1 -0
  291. package/dist/commands/ecdn/waf/migrate.d.ts +1 -0
  292. package/dist/commands/ecdn/waf/owasp/get.d.ts +1 -0
  293. package/dist/commands/ecdn/waf/owasp/update.d.ts +1 -0
  294. package/dist/commands/ecdn/waf/rules/get.d.ts +1 -0
  295. package/dist/commands/ecdn/waf/rules/list.d.ts +1 -0
  296. package/dist/commands/ecdn/waf/rules/update.d.ts +1 -0
  297. package/dist/commands/ecdn/waf/rulesets/list.d.ts +1 -0
  298. package/dist/commands/ecdn/waf/rulesets/update.d.ts +1 -0
  299. package/dist/commands/ecdn/zones/create.d.ts +1 -0
  300. package/dist/commands/ecdn/zones/list.d.ts +1 -0
  301. package/dist/commands/job/execution/delete.d.ts +1 -0
  302. package/dist/commands/job/export.d.ts +1 -0
  303. package/dist/commands/job/import-set.d.ts +1 -0
  304. package/dist/commands/job/import.d.ts +1 -0
  305. package/dist/commands/job/log.d.ts +1 -0
  306. package/dist/commands/job/run.d.ts +1 -0
  307. package/dist/commands/job/search.d.ts +1 -0
  308. package/dist/commands/job/wait.d.ts +1 -0
  309. package/dist/commands/logs/get.d.ts +1 -0
  310. package/dist/commands/logs/list.d.ts +1 -0
  311. package/dist/commands/logs/tail.d.ts +1 -0
  312. package/dist/commands/metrics/controller.d.ts +1 -0
  313. package/dist/commands/metrics/ecdn.d.ts +1 -0
  314. package/dist/commands/metrics/mrt.d.ts +1 -0
  315. package/dist/commands/metrics/ocapi.d.ts +1 -0
  316. package/dist/commands/metrics/overall.d.ts +1 -0
  317. package/dist/commands/metrics/sales.d.ts +1 -0
  318. package/dist/commands/metrics/scapi-hooks.d.ts +1 -0
  319. package/dist/commands/metrics/scapi.d.ts +1 -0
  320. package/dist/commands/metrics/third-party.d.ts +1 -0
  321. package/dist/commands/mrt/bundle/delete.d.ts +1 -0
  322. package/dist/commands/mrt/bundle/deploy.d.ts +1 -0
  323. package/dist/commands/mrt/bundle/download.d.ts +1 -0
  324. package/dist/commands/mrt/bundle/history.d.ts +1 -0
  325. package/dist/commands/mrt/bundle/list.d.ts +1 -0
  326. package/dist/commands/mrt/bundle/save.d.ts +1 -0
  327. package/dist/commands/mrt/bundle/upload-v2.d.ts +1 -0
  328. package/dist/commands/mrt/env/access-control/list.d.ts +1 -0
  329. package/dist/commands/mrt/env/b2c.d.ts +1 -0
  330. package/dist/commands/mrt/env/clone.d.ts +1 -0
  331. package/dist/commands/mrt/env/create.d.ts +1 -0
  332. package/dist/commands/mrt/env/delete.d.ts +1 -0
  333. package/dist/commands/mrt/env/get.d.ts +1 -0
  334. package/dist/commands/mrt/env/invalidate.d.ts +1 -0
  335. package/dist/commands/mrt/env/list.d.ts +1 -0
  336. package/dist/commands/mrt/env/redirect/clone.d.ts +1 -0
  337. package/dist/commands/mrt/env/redirect/create.d.ts +1 -0
  338. package/dist/commands/mrt/env/redirect/delete.d.ts +1 -0
  339. package/dist/commands/mrt/env/redirect/list.d.ts +1 -0
  340. package/dist/commands/mrt/env/update.d.ts +1 -0
  341. package/dist/commands/mrt/env/var/delete.d.ts +1 -0
  342. package/dist/commands/mrt/env/var/list.d.ts +1 -0
  343. package/dist/commands/mrt/env/var/push.d.ts +1 -0
  344. package/dist/commands/mrt/env/var/set.d.ts +1 -0
  345. package/dist/commands/mrt/org/b2c.d.ts +1 -0
  346. package/dist/commands/mrt/org/cert/create.d.ts +1 -0
  347. package/dist/commands/mrt/org/cert/delete.d.ts +1 -0
  348. package/dist/commands/mrt/org/cert/get.d.ts +1 -0
  349. package/dist/commands/mrt/org/cert/list.d.ts +1 -0
  350. package/dist/commands/mrt/org/cert/restart-validation.d.ts +1 -0
  351. package/dist/commands/mrt/org/list.d.ts +1 -0
  352. package/dist/commands/mrt/org/member/add.d.ts +1 -0
  353. package/dist/commands/mrt/org/member/get.d.ts +1 -0
  354. package/dist/commands/mrt/org/member/list.d.ts +1 -0
  355. package/dist/commands/mrt/org/member/remove.d.ts +1 -0
  356. package/dist/commands/mrt/org/member/update.d.ts +1 -0
  357. package/dist/commands/mrt/project/create.d.ts +1 -0
  358. package/dist/commands/mrt/project/delete.d.ts +1 -0
  359. package/dist/commands/mrt/project/get.d.ts +1 -0
  360. package/dist/commands/mrt/project/list.d.ts +1 -0
  361. package/dist/commands/mrt/project/member/add.d.ts +1 -0
  362. package/dist/commands/mrt/project/member/get.d.ts +1 -0
  363. package/dist/commands/mrt/project/member/list.d.ts +1 -0
  364. package/dist/commands/mrt/project/member/remove.d.ts +1 -0
  365. package/dist/commands/mrt/project/member/update.d.ts +1 -0
  366. package/dist/commands/mrt/project/notification/delete.d.ts +1 -0
  367. package/dist/commands/mrt/project/notification/get.d.ts +1 -0
  368. package/dist/commands/mrt/project/update.d.ts +1 -0
  369. package/dist/commands/mrt/save-credentials.d.ts +1 -0
  370. package/dist/commands/mrt/tail-logs.d.ts +1 -0
  371. package/dist/commands/mrt/user/api-key.d.ts +1 -0
  372. package/dist/commands/mrt/user/email-prefs.d.ts +1 -0
  373. package/dist/commands/mrt/user/profile.d.ts +1 -0
  374. package/dist/commands/scaffold/init.js +2 -1
  375. package/dist/commands/scaffold/init.js.map +1 -1
  376. package/dist/commands/scapi/custom/status.d.ts +1 -0
  377. package/dist/commands/scapi/schemas/get.d.ts +1 -0
  378. package/dist/commands/scapi/schemas/list.d.ts +1 -0
  379. package/dist/commands/setup/ide/tsserver-plugin.d.ts +1 -0
  380. package/dist/commands/setup/ide/vscode-types.d.ts +1 -0
  381. package/dist/commands/setup/index.js +4 -3
  382. package/dist/commands/setup/index.js.map +1 -1
  383. package/dist/commands/setup/inspect.d.ts +1 -0
  384. package/dist/commands/setup/inspect.js +17 -2
  385. package/dist/commands/setup/inspect.js.map +1 -1
  386. package/dist/commands/setup/instance/create.d.ts +1 -0
  387. package/dist/commands/setup/instance/list.d.ts +1 -0
  388. package/dist/commands/setup/instance/remove.d.ts +1 -0
  389. package/dist/commands/setup/instance/set-active.d.ts +1 -0
  390. package/dist/commands/setup/openshell.d.ts +1 -0
  391. package/dist/commands/setup/skills.d.ts +1 -0
  392. package/dist/commands/slas/client/create.d.ts +1 -0
  393. package/dist/commands/slas/client/delete.d.ts +1 -0
  394. package/dist/commands/slas/client/get.d.ts +1 -0
  395. package/dist/commands/slas/client/list.d.ts +1 -0
  396. package/dist/commands/slas/client/open.d.ts +1 -0
  397. package/dist/commands/slas/client/update.d.ts +1 -0
  398. package/dist/commands/slas/token.d.ts +1 -0
  399. package/dist/commands/slas/token.js +2 -1
  400. package/dist/commands/slas/token.js.map +1 -1
  401. package/dist/commands/webdav/get.d.ts +1 -0
  402. package/dist/commands/webdav/mkdir.d.ts +2 -0
  403. package/dist/commands/webdav/mkdir.js +5 -3
  404. package/dist/commands/webdav/mkdir.js.map +1 -1
  405. package/dist/commands/webdav/put.d.ts +2 -0
  406. package/dist/commands/webdav/put.js +5 -3
  407. package/dist/commands/webdav/put.js.map +1 -1
  408. package/dist/commands/webdav/rm.d.ts +1 -0
  409. package/dist/help.d.ts +25 -0
  410. package/dist/help.js +96 -0
  411. package/dist/help.js.map +1 -0
  412. package/dist/lib/scaffold/generate-helper.js +3 -2
  413. package/dist/lib/scaffold/generate-helper.js.map +1 -1
  414. package/dist/lib/skills.d.ts +19 -0
  415. package/dist/lib/skills.js +75 -0
  416. package/dist/lib/skills.js.map +1 -0
  417. package/dist/utils/cip/command.d.ts +1 -0
  418. package/dist/utils/ecdn/zone-command.d.ts +1 -0
  419. package/dist/utils/slas/client.d.ts +1 -0
  420. package/oclif.manifest.json +10994 -7653
  421. package/package.json +11 -5
@@ -0,0 +1,202 @@
1
+ ---
2
+ name: sfnext-page-designer
3
+ description: >-
4
+ Build merchant-editable Page Designer content in a Storefront Next project: components decorated with @Component/@AttributeDefinition/@RegionDefinition, page routes decorated with @PageType, <Region> rendering, fetchPageWithComponentData, component loaders and fallbacks, the generated static registry, and the pnpm cartridge:generate / cartridge:validate / cartridge:deploy workflow. Use when adding or editing a Page Designer component, exposing a region on a route (home, PDP, PLP, search), fetching a page by pageId or aspectType, marking a Region critical, debugging a component that does not appear in the Business Manager palette or renders empty, or reviewing Page Designer code. Do not use for classic ISML/SFRA Page Designer (use `b2c:b2c-page-designer`), for turning a Figma frame into blocks (use `figma-to-sfnext-pagedesigner:figma-to-sfnext-pagedesigner`), or for plain React components that merchants never edit (use `storefront-next:sfnext-components`).
5
+ ---
6
+
7
+ # Page Designer in Storefront Next
8
+
9
+ In Storefront Next your React components are the Page Designer components. You annotate them with decorators, the build generates Business Manager metadata (JSON) from those decorators, and at runtime a route loader fetches the page structure from the Shopper Experience API and `<Region>` renders it.
10
+
11
+ The in-project guide is `docs/README-PAGE-DESIGNER.md` and the rules in `AGENTS.md` apply; this skill is the task-oriented path through them. Where the guide and the source disagree, trust the source.
12
+
13
+ ## Pieces and where they live
14
+
15
+ | Piece | Location in your project |
16
+ |-------|--------------------------|
17
+ | Decorators (`Component`, `AttributeDefinition`, `RegionDefinition`, `PageType`) | `@/lib/decorators` (`src/lib/decorators/`) |
18
+ | Page fetch with per-component loader promises | `fetchPageWithComponentData` in `@/lib/page-designer/page-loader.server` |
19
+ | Single-component fetch (preview, embedded) | `fetchComponentWithComponentData` in `@/lib/page-designer/component-loader.server` |
20
+ | Region renderer | `Region` from `@/components/region` |
21
+ | Generated registry (do not hand-edit) | `src/lib/page-designer/static-registry.ts` |
22
+ | Runtime hooks (`usePageDesignerMode`) | `@salesforce/storefront-next-runtime/design/react/core` |
23
+ | Design/preview mode detection in loaders | `isDesignModeActive` / `isPreviewModeActive` from `@salesforce/storefront-next-runtime/design/mode` |
24
+ | Generated Business Manager metadata | `cartridges/app_storefrontnext_base/cartridge/experience/{components,pages,aspects}` |
25
+
26
+ The runtime package's `/design` entry exports only the registry. Decorators come from your project, not from `@salesforce/storefront-next-runtime`.
27
+
28
+ ## Workflow
29
+
30
+ 1. Create or edit the component (metadata class, props, default export, `fallback`, optional `loader`) under `src/components/<name>/index.tsx`.
31
+ 2. Run `pnpm dev` (or `pnpm build`). The Vite plugin scans `src/components` for `@Component` and rewrites the registry between the `STATIC_REGISTRY_START/END` markers.
32
+ 3. `pnpm cartridge:generate` writes the metadata JSON (`pnpm build` runs it for you).
33
+ 4. `pnpm cartridge:validate` checks the generated JSON against the schemas. Generation also fails on an invalid attribute config (for example a bad `searching` combination).
34
+ 5. `pnpm cartridge:deploy` uploads the cartridge to the B2C instance (`pnpm cartridge:deploy -- --delete` removes old cartridge files first). The optional MCP tool `cartridge_deploy` does the same. See `b2c-cli:b2c-code` for credentials and code-version handling.
35
+ 6. In Business Manager, merchants build pages from the new palette entries. See [Business Manager](references/BUSINESS-MANAGER.md).
36
+
37
+ ## A component
38
+
39
+ ```tsx
40
+ // src/components/promo-banner/index.tsx
41
+ import { AttributeDefinition, Component, RegionDefinition } from '@/lib/decorators';
42
+ import { DynamicImage } from '@/components/dynamic-image';
43
+ import { type Image } from '@/types';
44
+ import { cn } from '@/lib/utils';
45
+
46
+ @Component('promoBanner', {
47
+ name: 'Promo Banner',
48
+ description: 'Headline and optional image. Headline text and alignment are editable.',
49
+ group: 'Content',
50
+ })
51
+ @RegionDefinition([])
52
+ export class PromoBannerMetadata {
53
+ @AttributeDefinition({ id: 'headline', name: 'Headline', type: 'string', required: true, defaultValue: 'Spring sale' })
54
+ headline?: string;
55
+
56
+ @AttributeDefinition({ id: 'image', name: 'Image', type: 'image' })
57
+ image?: Image;
58
+
59
+ @AttributeDefinition({ id: 'align', name: 'Alignment', type: 'enum', values: ['left', 'center'], defaultValue: 'left' })
60
+ align?: 'left' | 'center';
61
+ }
62
+
63
+ interface PromoBannerProps {
64
+ headline?: string;
65
+ image?: Image;
66
+ align?: 'left' | 'center';
67
+ className?: string;
68
+ }
69
+
70
+ // Page Designer also injects component, data, designMetadata and regionId.
71
+ // Never spread them onto a DOM element; destructure them out first if you forward ...rest.
72
+ export default function PromoBanner({ headline = 'Spring sale', image, align = 'left', className }: PromoBannerProps) {
73
+ return (
74
+ <section className={cn('relative', align === 'center' && 'text-center', className)}>
75
+ {image?.url && <DynamicImage src={image.url} alt="" />}
76
+ <h2>{headline}</h2>
77
+ </section>
78
+ );
79
+ }
80
+
81
+ // REQUIRED. Rendered in a Suspense boundary; receives the same attribute props. Keep it light.
82
+ export function fallback() {
83
+ return <div className="h-48 animate-pulse bg-muted" />;
84
+ }
85
+ ```
86
+
87
+ Rules that matter:
88
+
89
+ - Attribute values arrive as props named after the class field, so the field name and `id` must agree.
90
+ - The metadata class must be `export`ed; the generator only sees exported classes.
91
+ - The `typeId` must be a string literal. It is stored as `<group>.<typeId>` (default group `storefrontnext_base`), for example `Content.promoBanner`. Use the `group` option (`Content`, `Layout`, ...) so related components sit together in the palette.
92
+ - An `image` attribute delivers an object (`{ url, focalPoint?, metaData? }`), typed `Image` from `@/types`, not a string. Check `src/components/hero/index.tsx` for the pattern.
93
+ - `name` and `description` are what merchants read in Business Manager; omitted ones fall back to the raw id.
94
+ - Full option tables, attribute types, enums, nested regions and cross-group refs: [Decorator Patterns](references/DECORATOR-PATTERNS.md).
95
+
96
+ ## A loader
97
+
98
+ Export a callable named `loader` when the component needs data (products, categories, your own API). It receives `{ componentData, context, request }`; `componentData` is the whole SCAPI component object, so merchant-set attributes live at `componentData.data`.
99
+
100
+ ```tsx
101
+ // src/components/promo-products/loaders.ts
102
+ import type { LoaderFunctionArgs } from 'react-router';
103
+ import type { ShopperExperience } from '@/scapi';
104
+ import { fetchProductsByIds } from '@/lib/api/products.server';
105
+
106
+ export const loader = async (args: { componentData: unknown; context: LoaderFunctionArgs['context'] }) => {
107
+ const comp = args.componentData as ShopperExperience.schemas['Component'];
108
+ const { productIds } = (comp.data ?? {}) as { productIds?: string };
109
+ if (!productIds) return null;
110
+ return fetchProductsByIds(args.context, productIds.split(','));
111
+ };
112
+ ```
113
+
114
+ ```tsx
115
+ // src/components/promo-products/index.tsx
116
+ export { loader } from './loaders';
117
+ export function fallback() { /* skeleton with reserved height */ }
118
+ export default function PromoProducts({ data }: { data?: Product[] | null }) { /* ... */ }
119
+ ```
120
+
121
+ - The exported `loader` must be a function. An object such as `{ server: fn }` is silently ignored and `data` stays undefined; unwrap it (`export const loader = loaders.server`), as `product-tile/index.tsx` does.
122
+ - The loader runs on the server only (stripped from the client bundle), so it may import `*.server` modules; the component file must not. An optional `clientLoader` export is client-only.
123
+ - Return `null` when nothing is configured, fetch in parallel with `Promise.all`, and let errors propagate so only that component is hidden.
124
+ - The registry records `{ loader: 'loader' }` and `{ fallback: 'fallback' }` capability flags for you on regeneration.
125
+
126
+ More on the registry shape, group-qualified ids, preload manifest and entry wiring: [Registry and Loading](references/COMPONENT-REGISTRY.md).
127
+
128
+ ## A page route
129
+
130
+ Routes bind a URL to a Page Designer page template and render its regions. Routes with `@PageType` today: home (`_app._index.tsx`), PLP (`_app.c.$.tsx`), PDP (`_app.p.$.tsx`), search (`_app.search.tsx`), about-us (`_app.about-us.tsx`), and the component preview route. Cart, checkout, account and auth do not use Page Designer.
131
+
132
+ ```tsx
133
+ import { Region } from '@/components/region';
134
+ import { PageType } from '@/lib/decorators/page-type';
135
+ import { RegionDefinition } from '@/lib/decorators/region-definition';
136
+ import { fetchPageWithComponentData } from '@/lib/page-designer/page-loader.server';
137
+
138
+ @PageType({
139
+ name: 'Landing Page',
140
+ description: 'Campaign landing page with a banner and a main content area',
141
+ supportedAspectTypes: [],
142
+ })
143
+ @RegionDefinition([
144
+ { id: 'banner', name: 'Banner Region', maxComponents: 1 },
145
+ { id: 'main', name: 'Main Region' },
146
+ ])
147
+ export class LandingPageMetadata {}
148
+
149
+ export function loader(args: Route.LoaderArgs) {
150
+ return { page: fetchPageWithComponentData(args, { pageId: 'landing' }) };
151
+ }
152
+
153
+ export default function Landing({ loaderData }: Route.ComponentProps) {
154
+ return (
155
+ <>
156
+ <Region page={loaderData.page} regionId="banner" />
157
+ <Region page={loaderData.page} regionId="main" />
158
+ </>
159
+ );
160
+ }
161
+ ```
162
+
163
+ - Fetch by `{ pageId }` for a fixed page, or by aspect: `{ aspectType: 'pdp', productId, categoryId? }` and `{ aspectType: 'plp', categoryId }`. The `aspectType` passed to the fetch must agree with `@PageType.supportedAspectTypes` (`['pdp']`, `['plp']`, or `[]` for fixed-page routes such as home); a mismatch shows the wrong template in Business Manager with no error.
164
+ - `fetchPageWithComponentData` attaches the per-component loader promises to the page (`page.componentData`). `<Region>` reads them itself; it takes no `componentData` prop, and the loader returns just `{ page }`. It resolves to `null` when the page is missing (404) or SCAPI errors, so empty regions are the normal unconfigured state.
165
+ - `<Region page={...}>` accepts a promise and renders in Suspense. Add `fallbackElement` only for a visible skeleton.
166
+ - Add `critical` to a page-level region only for above-the-fold or LCP content, and only with an awaited page (`page: await fetchPageWithComponentData(...)`), as `_app._index.tsx` does. Never on below-the-fold or catch-all regions. Details in [Registry and Loading](references/COMPONENT-REGISTRY.md).
167
+ - Nested regions inside a component use component mode, synchronously: `<Region component={component} regionId="content" />`. No Suspense wrapper, no `fallbackElement`, no promises. See `src/components/grid/index.tsx`.
168
+ - Do not use `errorElement` to render hard-coded content for an unconfigured page: it defeats merchant control and forces loaders to fetch data only for the fallback. The home route still contains such an `errorElement`; do not copy it. Use `fallbackElement` for loading and render nothing for empty.
169
+ - Metadata classes on routes are empty, exported, and never carry `@AttributeDefinition`.
170
+
171
+ ## Design and preview mode
172
+
173
+ Business Manager loads your storefront in an iframe. In loaders use `isDesignModeActive(request)` / `isPreviewModeActive(request)` (the page loader already does and switches to the `pageId`/`pdToken` passed by Business Manager). In components use `usePageDesignerMode()` from `@salesforce/storefront-next-runtime/design/react/core`. `PageDesignerInit` (`src/page-designer-init.tsx`, rendered by `root.tsx`) blocks link navigation while editing and loads design-mode styles; do not remove it.
174
+
175
+ ## Verify before you finish
176
+
177
+ - [ ] Metadata class exported; `typeId` literal; `group` set; every attribute has `name`, `description`, correct `type`
178
+ - [ ] Default export is the component; `fallback` exported and lightweight; `loader` (if any) is a function
179
+ - [ ] Every `<Region regionId>` matches a `@RegionDefinition` id, and vice versa
180
+ - [ ] `pnpm cartridge:generate && pnpm cartridge:validate` pass; the component is in `static-registry.ts`
181
+ - [ ] Review against [Review Checklist](references/REVIEW-CHECKLIST.md); symptoms in [Troubleshooting](references/TROUBLESHOOTING.md)
182
+
183
+ ## Reference Documentation
184
+
185
+ - [Decorator Patterns](references/DECORATOR-PATTERNS.md) - options, attribute types, regions, page types
186
+ - [Registry and Loading](references/COMPONENT-REGISTRY.md) - static registry, loaders, critical regions, entry wiring
187
+ - [Business Manager](references/BUSINESS-MANAGER.md) - `route` and `aspectTypeIds`, page setup, palette
188
+ - [Review Checklist](references/REVIEW-CHECKLIST.md) - what to check in a component or page route
189
+ - [Troubleshooting](references/TROUBLESHOOTING.md) - component missing, empty, or wrong data
190
+
191
+ ## Related Skills
192
+
193
+ - `storefront-next:sfnext-components` - component conventions, shadcn primitives, Storybook
194
+ - `storefront-next:sfnext-data-fetching` - loaders, `createApiClients`, streaming with Suspense/Await
195
+ - `storefront-next:sfnext-scapi` - calling SCAPI from loaders and actions
196
+ - `storefront-next:sfnext-performance` - LCP, preload, critical data
197
+ - `storefront-next:sfnext-theming` - tokens and brand styling for new components
198
+ - `storefront-next:sfnext-testing` - component and story tests
199
+ - `storefront-next:sfnext-deployment` - shipping the storefront bundle
200
+ - `figma-to-sfnext-pagedesigner:figma-to-sfnext-pagedesigner` - Figma frame to Page Designer blocks
201
+ - `b2c:b2c-page-designer` - classic (ISML/SFRA) Page Designer
202
+ - `b2c-cli:b2c-code` - deploying cartridges and code versions
@@ -0,0 +1,46 @@
1
+ # Business Manager Integration
2
+
3
+ How code becomes something merchants can use. The long-form version is the "Business Manager Integration" section of `docs/README-PAGE-DESIGNER.md`.
4
+
5
+ ## What gets generated
6
+
7
+ `pnpm cartridge:generate` (also part of `pnpm build`) scans your decorators and writes JSON under `cartridges/app_storefrontnext_base/cartridge/experience/`:
8
+
9
+ | Folder | Source | Content |
10
+ |--------|--------|---------|
11
+ | `components/<group>/<typeId>.json` | `@Component` + `@AttributeDefinition` + `@RegionDefinition` | Palette entries and their attribute editors |
12
+ | `pages/<name>.json` | `@PageType` + `@RegionDefinition` on a route | Page templates, with `route` and `aspectTypeIds` |
13
+ | `aspects/*.json` | aspect types (`pdp`, `plp`) | Aspect definitions for product and category pages |
14
+
15
+ Never hand-edit these files; regenerate. `pnpm cartridge:validate` checks them against the schemas. `pnpm cartridge:deploy` uploads the cartridge; `pnpm cartridge:deploy -- --delete` clears old cartridge files first.
16
+
17
+ The first time you set up an instance, `sfnext setup-base-cartridge --slas-client-id <id>` registers what the base cartridge needs on your SLAS client (see `docs/README-EMAIL-CARTRIDGE.md` in your project and `b2c-cli:b2c-slas`).
18
+
19
+ ## `route` and `aspectTypeIds`
20
+
21
+ The generated page JSON carries two fields that tie a route to Business Manager:
22
+
23
+ - `route`: the URL pattern Business Manager loads in its preview iframe, with `:param` placeholders replaced by the product, category or search term the merchant selected (for example `/:siteId/:localeId/product/:productId`, `/:siteId/:localeId/category/:categoryId`, `/:siteId/:localeId`). It comes from the route file, so a renamed route file changes it on the next generate.
24
+ - `aspectTypeIds`: from `@PageType.supportedAspectTypes`. It decides which page templates are offered when a merchant creates a page for a product (`pdp`) or category (`plp`). An empty array means the template is for a fixed page.
25
+
26
+ The route's loader must fetch with the same aspect the decorator advertises (`fetchPageWithComponentData(args, { aspectType: 'pdp', productId })` with `supportedAspectTypes: ['pdp']`). This cross-file agreement is the most common page-level bug and produces no error, only the wrong template in Business Manager.
27
+
28
+ ## Merchant setup checklist
29
+
30
+ After deploying the cartridge:
31
+
32
+ 1. Make sure the cartridge is on the cartridge path of the site and the active code version holds the upload.
33
+ 2. If the storefront was created outside Business Manager, connect it to Business Manager so Page Designer and Storefront Preview can see it (Administration > Sites > Storefronts > Connect Existing; needs an administrator role; confirm current steps in the Salesforce Storefront Next documentation).
34
+ 3. Merchant Tools > Content > Page Designer: create a page, choose the template (page type) that matches the route, add components to its regions.
35
+ 4. For aspect pages, assign the page to the product or category; for fixed pages, use the `pageId` your loader requests (for example `homepage`, `aboutus`).
36
+ 5. Publish, then check the storefront. A `null` page (missing or unpublished) renders empty regions rather than an error.
37
+
38
+ ## Design vs preview mode
39
+
40
+ Business Manager opens your storefront with `mode` and `pdToken` query parameters. `fetchPageFromLoader` switches to the page Business Manager specifies while in these modes, and `PageDesignerInit` blocks link navigation in edit mode. Keep both; neither needs changes when you add components.
41
+
42
+ ## Where to look next
43
+
44
+ - Component missing or empty: [Troubleshooting](TROUBLESHOOTING.md)
45
+ - Deploy credentials and code versions: `b2c-cli:b2c-code`, `b2c-cli:b2c-webdav`
46
+ - Pushing the storefront bundle itself: `storefront-next:sfnext-deployment`
@@ -0,0 +1,111 @@
1
+ # Registry and Loading
2
+
3
+ ## The static registry
4
+
5
+ `src/lib/page-designer/static-registry.ts` maps fully-qualified component ids to lazy importers. The `staticRegistry` Vite plugin generates it from every `@Component` under `src/components` when `pnpm dev` or `pnpm build` starts, and again on hot updates. Everything between `// STATIC_REGISTRY_START` and `// STATIC_REGISTRY_END` is overwritten; do not edit it by hand and do not add entries manually.
6
+
7
+ Shape of the generated code (your file lists your project's components):
8
+
9
+ ```typescript
10
+ const staticRegistryImporters = [
11
+ () => import('../../components/hero/index'),
12
+ () => import('../../components/product-carousel/index'),
13
+ // ...
14
+ ] as const;
15
+
16
+ export function initializeRegistry(targetRegistry = registry): void {
17
+ targetRegistry.registerImporter('Content.hero', staticRegistryImporters[0]);
18
+ targetRegistry.registerImporter('Layout.productCarousel', staticRegistryImporters[1], {
19
+ loader: 'loader',
20
+ fallback: 'fallback',
21
+ });
22
+ }
23
+ ```
24
+
25
+ - Ids are `<group>.<typeId>`, so the `typeId` in `@Component('hero', ...)` plus `group: 'Content'` becomes `Content.hero`. These ids must match what Business Manager sends.
26
+ - The third argument records which named exports the module has: `loader` (server data) and `fallback` (Suspense skeleton). The plugin derives them from your exports.
27
+ - To see which components your project registers, read the generated file rather than relying on a fixed list.
28
+
29
+ If a new component is missing from the registry, the usual cause is that it is outside `src/components`, the `@Component` first argument is not a string literal, or the dev server was not running when the file changed. Restart `pnpm dev` or run `pnpm build`.
30
+
31
+ ## Entry wiring (do not break)
32
+
33
+ `initializeRegistry()` is called once at module top level in `src/entry.server.tsx` and synchronously in `src/entry.client.tsx`, before any component markers are scanned. Keep it there; moving it into a React render function breaks registration ordering.
34
+
35
+ `vite-plugins/storefront-next.ts` enables the plugin with:
36
+
37
+ ```typescript
38
+ staticRegistry: {
39
+ componentPath: 'src/components',
40
+ registryPath: 'src/lib/page-designer/static-registry.ts',
41
+ preloadManifest: true,
42
+ }
43
+ ```
44
+
45
+ `preloadManifest: true` builds the resource-hint manifest that `critical` regions and per-component preload hints depend on. Keep it on.
46
+
47
+ ## How a page renders
48
+
49
+ 1. The route loader calls `fetchPageWithComponentData(args, params)`.
50
+ 2. It fetches the page (SCAPI Shopper Experience, resolved from the Data Store when that middleware is active), walks every region and nested region, and for each component whose registry entry has a `loader` calls it with `{ componentData, context, request }`. The resulting promises are stored as `page.componentData[component.id]`.
51
+ 3. `<Region page={page} regionId="..." />` resolves each component's module from the registry, wraps it in Suspense (using the module's `fallback`), awaits that component's promise, and renders the default export with the attribute props plus `data`, `component`, `designMetadata` and `regionId`.
52
+
53
+ Because data is attached to the page, there is no separate `componentData` return key and no `componentData` prop on `<Region>`.
54
+
55
+ ## Fetching a page
56
+
57
+ ```typescript
58
+ import { fetchPageWithComponentData } from '@/lib/page-designer/page-loader.server';
59
+
60
+ // Fixed page by id
61
+ fetchPageWithComponentData(args, { pageId: 'homepage' });
62
+
63
+ // By aspect (page assigned to a product or category in Business Manager)
64
+ fetchPageWithComponentData(args, { aspectType: 'pdp', productId, categoryId });
65
+ fetchPageWithComponentData(args, { aspectType: 'plp', categoryId });
66
+ ```
67
+
68
+ Return the promise unawaited for a non-critical page (React Router streams it), or `await` it when a `critical` region needs the page synchronously. `fetchPageFromLoader` (same module) is the lower-level call that returns the raw page without `componentData`; routes use `fetchPageWithComponentData`.
69
+
70
+ Single components (for example an embedded content block or the preview route) use `fetchComponentWithComponentData` from `@/lib/page-designer/component-loader.server`.
71
+
72
+ ## Module contract
73
+
74
+ | Export | Required | Notes |
75
+ |--------|----------|-------|
76
+ | default | yes | The React component. `forwardRef` components are fine. |
77
+ | `fallback` | yes | Lightweight skeleton; gets the same attribute props; reserve dimensions to avoid layout shift. No hooks that suspend, no fetching. |
78
+ | `loader` | optional | Must be a function `({ componentData, context, request }) => Promise`. Server only (stripped from the client bundle). Attributes are at `componentData.data`. |
79
+ | `clientLoader` | optional | Client-only counterpart (stripped from the server bundle). |
80
+
81
+ A `loader` that is an object (`{ server, client }`) is not callable; the loader never runs and `data` is undefined. Export the function: `export const loader = loaders.server`.
82
+
83
+ Loader guidance: return `null` when nothing is configured, fetch independent resources with `Promise.all`, do not swallow errors (the component's error boundary hides only that component), and reuse the shared `@/lib/api/*.server` helpers rather than building SCAPI calls inline.
84
+
85
+ ## Critical regions
86
+
87
+ By default regions stream: the shell renders, then each component swaps in. For above-the-fold content that must be in the first HTML (hero, LCP image), make the region critical.
88
+
89
+ ```tsx
90
+ export async function loader(args: Route.LoaderArgs) {
91
+ const page = await fetchPageWithComponentData(args, { pageId: 'homepage' }); // must be resolved
92
+ const recommendations = fetchRecommendations(args.context); // stay deferred
93
+ return { page, recommendations };
94
+ }
95
+
96
+ export default function Home({ loaderData }: Route.ComponentProps) {
97
+ return <Region page={loaderData.page} regionId="headerbanner" critical />;
98
+ }
99
+ ```
100
+
101
+ - `critical` is page mode only; nested component regions inherit it.
102
+ - The page must already be resolved; omit `fallbackElement` on a critical region.
103
+ - Each component's own `loader` data still streams inside its local Suspense boundary.
104
+ - Use it sparingly; it delays the initial shell. Never on below-the-fold or catch-all regions.
105
+ - Stylesheets added through a route's `links` export should use `createStorefrontStylesheetLink` from `@salesforce/storefront-next-runtime/design/react/preload` so critical component styles keep a stable cascade order.
106
+
107
+ See "Critical Page Regions" in `docs/README-PAGE-DESIGNER.md` for the full behavior.
108
+
109
+ ## Error handling
110
+
111
+ Do not pass `errorElement` to show hard-coded content when a page is unconfigured. That hides setup problems, forces extra fetches in the loader, and bypasses merchant control. Use `fallbackElement` for loading states, or render nothing when a region is empty. The home route's existing `errorElement` is a legacy pattern; do not copy it to new routes.
@@ -0,0 +1,168 @@
1
+ # Decorator Patterns
2
+
3
+ All four decorators are imported from your project, not from the runtime package:
4
+
5
+ ```typescript
6
+ import { AttributeDefinition, Component, PageType, RegionDefinition } from '@/lib/decorators';
7
+ ```
8
+
9
+ Deep imports (`@/lib/decorators/component`, `.../attribute-definition`, `.../page-type`, `.../region-definition`) also work and are used by some shipped files.
10
+
11
+ Decorators only attach metadata. They are stripped of behavior at runtime and read at build time by `pnpm cartridge:generate` (and by the registry plugin for `@Component`). Mistakes therefore show up as a missing or broken editor in Business Manager, not as a runtime error.
12
+
13
+ ## `@Component(typeId, options)`
14
+
15
+ ```typescript
16
+ @Component('productCarousel', {
17
+ name: 'Product Carousel',
18
+ description: 'Scrollable row of product cards. Pick a category or add product tiles.',
19
+ group: 'Layout',
20
+ })
21
+ ```
22
+
23
+ | Option | Meaning |
24
+ |--------|---------|
25
+ | `typeId` (first arg) | String literal, camelCase by convention (`hero`, `heroCarousel`, `megaMenu`). The registry plugin rejects non-literals. |
26
+ | `name`, `description` | Shown to merchants in the palette. Be specific about which attributes drive which behavior. |
27
+ | `group` | Palette folder. Default `storefrontnext_base`. Shipped components use `Content` and `Layout`. |
28
+ | `embedded`, `component_id` | Marks a singleton content block that is referenced rather than dropped into regions. The component preview route and `fetchComponentWithComponentData` use this path. |
29
+
30
+ The stored, fully-qualified id is `<group>.<typeId>` (`Layout.productCarousel`). That is the id that appears in SCAPI responses, in the registry, and in region include/exclude lists.
31
+
32
+ ## `@AttributeDefinition(config)`
33
+
34
+ Put it on a field of the exported metadata class. The field name is the prop name your component receives.
35
+
36
+ ```typescript
37
+ @AttributeDefinition({
38
+ id: 'limit',
39
+ name: 'Product Limit',
40
+ description: 'Maximum number of products to show.',
41
+ type: 'integer',
42
+ required: false,
43
+ defaultValue: 12,
44
+ })
45
+ limit?: number;
46
+ ```
47
+
48
+ | Option | Notes |
49
+ |--------|-------|
50
+ | `id` | Attribute id written to metadata. Keep it identical to the field name; a mismatch means merchant values never reach the prop. |
51
+ | `name`, `description` | Merchant-facing label and help text. Without them Business Manager shows the raw id. |
52
+ | `type` | One of the types below. Default is a string attribute. An unknown string (such as `'number'`) is not type-checked and produces a broken editor. |
53
+ | `required` | Match the component: `required: true` only if it cannot render without a value. |
54
+ | `defaultValue` | Prefilled in the editor. Keep it equal to the component's destructuring default so editor and runtime agree. For `enum` it must be one of `values`. |
55
+ | `values` | Required for `enum`: the option list. |
56
+ | `editorDefinition` | For `type: 'custom'`: `{ type, configuration? }` selecting a custom editor. |
57
+ | `searching` | `{ searchable, refinable, boostFactor?, sortable? }`. Makes the attribute searchable in Business Manager. Both booleans are required. |
58
+ | `dynamicLookup` | `{ aspectAttributeAlias }`. Sources the value from an aspect attribute at render time instead of a stored value. Allowed on all types. |
59
+
60
+ ### Attribute types
61
+
62
+ `string`, `text`, `markup`, `integer`, `boolean`, `product`, `category`, `file`, `page`, `image`, `url`, `enum`, `custom`, `cms_record`.
63
+
64
+ | Type | Value your component receives |
65
+ |------|-------------------------------|
66
+ | `string`, `text` | string (`text` is multi-line) |
67
+ | `markup` | raw HTML string; render with `dangerouslySetInnerHTML` only after deciding it is trusted content |
68
+ | `integer`, `boolean` | number, boolean |
69
+ | `enum` | one of `values` |
70
+ | `image` | object `{ url, focalPoint?, metaData? }` (`Image` from `@/types`) |
71
+ | `url` | string |
72
+ | `product`, `category` | the id (string); fetch details in a `loader` |
73
+ | `file`, `page`, `custom`, `cms_record` | reference values; check the generated JSON and a live SCAPI payload before relying on the shape |
74
+
75
+ ### `searching` combinations
76
+
77
+ Generation fails (and `cartridge:validate` reports it) when the combination is invalid:
78
+
79
+ - `string`, `text`, `product`, `category`: all fields allowed.
80
+ - `markup`: `sortable` must be omitted or `false`.
81
+ - `custom`, `cms_record`: `refinable` must be `false`; `boostFactor` and `sortable` are not allowed.
82
+ - `integer`, `boolean`, `file`, `page`, `image`, `url`, `enum`: searching is not allowed.
83
+
84
+ ## `@RegionDefinition(regions)`
85
+
86
+ Declares the slots a component (or a page route) exposes to merchants.
87
+
88
+ ```typescript
89
+ @RegionDefinition([
90
+ {
91
+ id: 'products',
92
+ name: 'Products',
93
+ description: 'Add Product Tile components to populate this carousel.',
94
+ maxComponents: 12,
95
+ componentTypeInclusions: ['Content.productTile'],
96
+ },
97
+ ])
98
+ ```
99
+
100
+ | Field | Notes |
101
+ |-------|-------|
102
+ | `id`, `name` | Required. The `id` must match the `regionId` passed to `<Region>`. |
103
+ | `description` | Shown to merchants. |
104
+ | `maxComponents` | Set only when the layout structurally limits children. |
105
+ | `componentTypeInclusions`, `componentTypeExclusions` | Allow-list / deny-list of component types. Unqualified ids are prefixed with the host component's group; refer to another group with the full id (`'Content.productTile'` from a `Layout.*` host). |
106
+ | `defaultComponentConstructors` | `[{ id, typeId, data }]` components created when a merchant adds the region to a new page. `typeId` follows the same qualification rule. |
107
+
108
+ Leaf components may use `@RegionDefinition([])` or omit the decorator. A declared region that the implementation never renders is invisible to shoppers even when merchants fill it.
109
+
110
+ ## `@PageType(config)`
111
+
112
+ ```typescript
113
+ @PageType({
114
+ name: 'Product Detail Page',
115
+ description: 'Product detail page with promotional and engagement regions',
116
+ supportedAspectTypes: ['pdp'],
117
+ })
118
+ @RegionDefinition([{ id: 'pdpPromo', name: 'Promo Content Region', maxComponents: 1 }])
119
+ export class ProductPageMetadata {}
120
+ ```
121
+
122
+ | Field | Notes |
123
+ |-------|-------|
124
+ | `name`, `description` | Human-readable. `name` is the template label merchants pick. |
125
+ | `supportedAspectTypes` | `['pdp']`, `['plp']`, or `[]` for routes not bound to an aspect (home, about-us, component preview). Must agree with the `aspectType` the route loader fetches. |
126
+ | `preview` | Only `'default'` is valid. Used by the component preview route. |
127
+
128
+ The class must be exported and empty. Never put `@AttributeDefinition` on a page type.
129
+
130
+ `sfnext generate-cartridge` parses decorators statically, so decorator arguments must be literals, not imported constants.
131
+
132
+ ## Nested regions in a container
133
+
134
+ ```tsx
135
+ import { Region } from '@/components/region';
136
+
137
+ @Component('twoColumn', { name: 'Two Column', description: 'Two side-by-side regions.', group: 'Layout' })
138
+ @RegionDefinition([
139
+ { id: 'left', name: 'Left' },
140
+ { id: 'right', name: 'Right' },
141
+ ])
142
+ export class TwoColumnMetadata {}
143
+
144
+ export default function TwoColumn({ component }: { component: ComponentType }) {
145
+ return (
146
+ <div className="grid grid-cols-2 gap-4">
147
+ <Region component={component} regionId="left" />
148
+ <Region component={component} regionId="right" />
149
+ </div>
150
+ );
151
+ }
152
+ ```
153
+
154
+ `ComponentType` is exported from `@/components/region`. Use `className` on `<Region>` for layout; the design-mode wrapper uses `display: contents`, so children stay direct grid/flex items. `src/components/grid/index.tsx` is the reference implementation.
155
+
156
+ ## Images
157
+
158
+ ```tsx
159
+ import { DynamicImage } from '@/components/dynamic-image';
160
+
161
+ <DynamicImage src={image.url} alt="" />
162
+ ```
163
+
164
+ Always provide `alt` (empty for decorative images). Set `priority="high"` only on the LCP image, never on every image.
165
+
166
+ ## Text that is not merchant content
167
+
168
+ Button labels and fallback messages that developers (not merchants) own should use `useTranslation()`. Text in attributes is edited and localized by merchants in Business Manager, so leave it raw.
@@ -0,0 +1,83 @@
1
+ # Review Checklist
2
+
3
+ Use when reviewing or self-checking a Page Designer component (`@Component`) or page route (`@PageType`). Read-only: report findings with `file:line`, say why each matters, and group them as Bugs (break at runtime or in Business Manager), Conventions (drift from the rest of your project) and Polish.
4
+
5
+ A file is in scope if it uses `@Component` or `@PageType`, or is listed in `src/lib/page-designer/static-registry.ts`. Components use sections 1-4; page routes use sections 1 (page type part), 3 (region rendering) and 5.
6
+
7
+ ## 1. Metadata
8
+
9
+ **`@Component`**
10
+ - `typeId` is a literal that matches the registry suffix (`Content.hero` -> `'hero'`). A mismatch means the component never resolves.
11
+ - `name` is human-readable and differs from `typeId`; `description` says which attributes drive which behavior.
12
+ - `group` is set (`Content` or `Layout`) rather than defaulting to `storefrontnext_base`.
13
+ - The metadata class is `export`ed.
14
+
15
+ **`@AttributeDefinition`** (every merchant-configurable prop needs one)
16
+ - `type` is one of the valid types (`integer`, not `number`). An invalid string is not caught by TypeScript.
17
+ - `enum` has `values`, and its `defaultValue` is one of them.
18
+ - `image` props are typed as the `Image` object (`image.url`), not `string`.
19
+ - `id` equals the field name; a mismatch means merchant values never reach the prop.
20
+ - `required` matches the component: a prop with a destructuring default should be `required: false`; a prop that crashes on `undefined` should be `true`.
21
+ - `defaultValue` equals the component's destructuring default (otherwise the editor pre-fills one value and runtime falls back to another).
22
+ - `name` and `description` present.
23
+
24
+ **`@RegionDefinition`**
25
+ - Every declared region id is rendered by a matching `<Region regionId>`, and every rendered id is declared.
26
+ - `componentTypeInclusions`/`Exclusions` are fully qualified when they cross groups (`'Content.productTile'` from a `Layout.*` host); unqualified ids take the host's group.
27
+ - `maxComponents` only where the layout limits children.
28
+ - `@RegionDefinition([])` or no decorator on a leaf is fine.
29
+
30
+ **`@PageType`** (routes)
31
+ - Exported, empty class with `name`, `description`, `supportedAspectTypes`. `[]` is valid for fixed-page routes.
32
+ - `supportedAspectTypes` agrees with the loader's `aspectType`. Flag a loader aspect not in the list, a listed aspect the loader never fetches, and aspect ids that do not fit the aspect (for example `productId` with a category aspect). This is the most valuable cross-file check: Business Manager shows the wrong template with no error.
33
+ - No `@AttributeDefinition` on page types.
34
+
35
+ ## 2. Module contract (components)
36
+
37
+ - Default export is the component.
38
+ - Named `fallback` export exists, is light (no `useState`/`useEffect`, no fetching, nothing that suspends), uses the same attribute props, and reserves dimensions.
39
+ - Skeletons live in `fallback`, not in the main component.
40
+ - A `loader` export is a function with the `{ componentData, context, request }` signature; the registry has `{ loader: 'loader' }` (regenerate if not); attributes are read from `componentData.data`; `null` is returned for "nothing configured"; independent fetches use `Promise.all`; errors are not swallowed.
41
+ - Server-only imports (`*.server.ts`) appear only in loader files, never in the component body.
42
+
43
+ ## 3. Rendering
44
+
45
+ - Injected props (`component`, `data`, `designMetadata`, `regionId`, and rarely `componentData`) are destructured out before any `...rest` spread onto a DOM element. Unused ones are prefixed with `_`.
46
+ - Nested regions use component mode: `<Region component={component} regionId="x" />`, with no `page` prop, no `fallbackElement`, no Suspense wrapper, no promises.
47
+ - Route regions use page mode: `<Region page={loaderData.page} regionId="x" />`. `critical` only on above-the-fold regions with an awaited page and no local `fallbackElement`.
48
+ - Instance-specific `<style>` output is scoped (for example with `useId()`); unscoped CSS collides when two instances share a page.
49
+ - Images: `Image` object, `DynamicImage` for responsive widths, `alt` always present (empty for decorative), `priority="high"` only on LCP candidates.
50
+ - No `'use client'` directives; this is React Router, not React Server Components.
51
+ - Developer-owned strings go through `useTranslation()`; merchant attribute text stays raw.
52
+ - `memo` only on components with stable props.
53
+
54
+ ## 4. Anti-patterns
55
+
56
+ 1. `errorElement` on a `<Region>` used to render hard-coded content for an unconfigured page.
57
+ 2. A loader fetching data that only an `errorElement` uses.
58
+ 3. Skeleton markup inside the main component.
59
+ 4. `fetchPriority="high"` on every image.
60
+ 5. An `@Component` that is not in `static-registry.ts` (registry not regenerated, or file outside `src/components`).
61
+
62
+ ## 5. Do not flag
63
+
64
+ - `@PageType` with `supportedAspectTypes: []` on fixed-page routes.
65
+ - `@RegionDefinition([])` on leaves, or an omitted decorator on a leaf.
66
+ - Missing comments that justify valid choices.
67
+ - Formatting and import-order nits the linter already enforces.
68
+
69
+ ## Report format
70
+
71
+ ```
72
+ ## Bugs
73
+ 1. `src/components/foo/index.tsx:42` - Page Designer props leak to the DOM.
74
+ `designMetadata` and `component` are not destructured before `...rest` reaches a <div>; React warns in dev and emits `designmetadata="[object Object]"` in production.
75
+
76
+ ## Conventions
77
+ 2. `src/components/foo/index.tsx:18` - `@Component` has no `group`; peers use 'Content' or 'Layout'.
78
+
79
+ ## Polish
80
+ 3. `src/components/foo/index.tsx:25` - Description is generic.
81
+ ```
82
+
83
+ End with a one-line count per severity. A clean review should say so explicitly.