@thorprovider/storefront 0.3.1 → 0.4.0-mvp.2

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 (263) hide show
  1. package/README.md +321 -152
  2. package/VENDOR.md +69 -0
  3. package/assets/openspec/config.yaml +38 -0
  4. package/assets/templates/AGENTS.md +76 -0
  5. package/assets/templates/app/app/[[...slug]]/page.tsx +54 -2
  6. package/assets/templates/app/app/[[...slug]]/renderer.tsx +13 -2
  7. package/assets/templates/app/app/globals.css +5 -8
  8. package/assets/templates/app/app/layout.tsx +17 -7
  9. package/assets/templates/app/app/robots.ts +19 -0
  10. package/assets/templates/app/app/sitemap.ts +71 -0
  11. package/assets/templates/app/lib/__STOREFRONT__/actions.ts +612 -0
  12. package/assets/templates/app/lib/__STOREFRONT__/cart-state.ts +66 -0
  13. package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +50 -57
  14. package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +679 -0
  15. package/assets/templates/app/lib/__STOREFRONT__/composition.ts +26 -0
  16. package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +197 -0
  17. package/assets/templates/app/lib/__STOREFRONT__/handlers.ts +5 -36
  18. package/assets/templates/app/lib/__STOREFRONT__/icon-map.ts +27 -0
  19. package/assets/templates/app/lib/__STOREFRONT__/mappers.ts +13 -11
  20. package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +264 -51
  21. package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +6 -3
  22. package/assets/templates/app/lib/__STOREFRONT__/schema.ts +136 -0
  23. package/assets/templates/app/lib/__STOREFRONT__/section-observation.ts +182 -0
  24. package/assets/templates/app/lib/__STOREFRONT__/section-sources.ts +130 -0
  25. package/assets/templates/app/lib/__STOREFRONT__/session.core.ts +470 -0
  26. package/assets/templates/app/lib/__STOREFRONT__/session.ts +169 -0
  27. package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +120 -0
  28. package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +38 -0
  29. package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +129 -10
  30. package/assets/templates/app/lib/__STOREFRONT__/spec/page-root.ts +5 -1
  31. package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +191 -0
  32. package/assets/templates/app/lib/__STOREFRONT__/state.core.ts +257 -0
  33. package/assets/templates/app/lib/__STOREFRONT__/state.ts +7 -31
  34. package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +161 -0
  35. package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.client.tsx +25 -0
  36. package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.tsx +16 -0
  37. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.client.tsx +24 -0
  38. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.tsx +5 -18
  39. package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +18 -0
  40. package/assets/templates/app/lib/__STOREFRONT__/views/sections/logoCloud.tsx +26 -14
  41. package/assets/templates/app/lib/__STOREFRONT__/views/sections/pricing.tsx +36 -18
  42. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.client.tsx +100 -0
  43. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.tsx +19 -0
  44. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.cta.ts +19 -0
  45. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.faq.ts +18 -0
  46. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.products.ts +39 -0
  47. package/assets/templates/app/lib/__STOREFRONT__/views/sections/statistics.tsx +35 -14
  48. package/assets/templates/app/lib/__STOREFRONT__/views/sections/testimonials.tsx +31 -13
  49. package/assets/templates/app/lib/__STOREFRONT__/views/types.ts +5 -3
  50. package/assets/templates/features/checkout/handlers.ts +159 -0
  51. package/assets/templates/features/checkout/state.ts +61 -0
  52. package/assets/templates/features/customer-auth/handlers.ts +59 -0
  53. package/assets/templates/features/customer-auth/state.ts +45 -0
  54. package/assets/templates/features/recommendations/state.ts +11 -0
  55. package/assets/templates/views/account/content.ts +35 -0
  56. package/assets/templates/views/account/index.tsx +114 -0
  57. package/assets/templates/views/account/registry.tsx +439 -0
  58. package/assets/templates/views/account/schema.ts +53 -0
  59. package/assets/templates/views/cart/content.ts +11 -0
  60. package/assets/templates/views/cart/data.ts +23 -0
  61. package/assets/templates/views/cart/index.tsx +53 -0
  62. package/assets/templates/views/cart/props.ts +12 -0
  63. package/assets/templates/views/cart/registry.tsx +81 -0
  64. package/assets/templates/views/cart/schema.ts +52 -0
  65. package/assets/templates/views/category/assembly/assembly.json +29 -0
  66. package/assets/templates/views/category/assembly/index.skeleton.tsx +251 -0
  67. package/assets/templates/views/category/assembly/registry.skeleton.tsx +34 -0
  68. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/binding.json +5 -0
  69. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/catalog.part +4 -0
  70. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/element.part +7 -0
  71. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/registry.part +18 -0
  72. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/catalog.part +5 -0
  73. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/element.part +34 -0
  74. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/registry.part +71 -0
  75. package/assets/templates/views/category/content.ts +30 -0
  76. package/assets/templates/views/category/data.ts +12 -0
  77. package/assets/templates/views/category/index.tsx +299 -0
  78. package/assets/templates/views/category/props.ts +23 -0
  79. package/assets/templates/views/category/registry.tsx +122 -0
  80. package/assets/templates/views/category/schema.ts +91 -0
  81. package/assets/templates/views/checkout/content.ts +105 -0
  82. package/assets/templates/views/checkout/data.ts +23 -0
  83. package/assets/templates/views/checkout/index.tsx +111 -0
  84. package/assets/templates/views/checkout/props.ts +49 -0
  85. package/assets/templates/views/checkout/registry.tsx +217 -0
  86. package/assets/templates/views/checkout/schema.ts +229 -0
  87. package/assets/templates/views/home/assembly/assembly.json +33 -0
  88. package/assets/templates/views/home/assembly/index.skeleton.tsx +90 -0
  89. package/assets/templates/views/home/assembly/registry.skeleton.tsx +25 -0
  90. package/assets/templates/views/home/assembly/slots/benefits/Feature1/binding.json +5 -0
  91. package/assets/templates/views/home/assembly/slots/benefits/Feature1/catalog.part +4 -0
  92. package/assets/templates/views/home/assembly/slots/benefits/Feature1/element.part +4 -0
  93. package/assets/templates/views/home/assembly/slots/benefits/Feature1/registry.part +11 -0
  94. package/assets/templates/views/home/assembly/slots/benefits/Feature2/binding.json +5 -0
  95. package/assets/templates/views/home/assembly/slots/benefits/Feature2/catalog.part +4 -0
  96. package/assets/templates/views/home/assembly/slots/benefits/Feature2/element.part +4 -0
  97. package/assets/templates/views/home/assembly/slots/benefits/Feature2/registry.part +10 -0
  98. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/binding.json +5 -0
  99. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/catalog.part +4 -0
  100. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/element.part +5 -0
  101. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/registry.part +16 -0
  102. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/binding.json +5 -0
  103. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/catalog.part +4 -0
  104. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +5 -0
  105. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +26 -0
  106. package/assets/templates/views/home/content.ts +70 -0
  107. package/assets/templates/views/home/data.ts +24 -0
  108. package/assets/templates/views/home/index.tsx +57 -85
  109. package/assets/templates/views/home/props.ts +22 -0
  110. package/assets/templates/views/home/registry.tsx +38 -22
  111. package/assets/templates/views/home/schema.ts +93 -0
  112. package/assets/templates/views/product/content.ts +25 -0
  113. package/assets/templates/views/product/data.ts +50 -0
  114. package/assets/templates/views/product/index.tsx +148 -0
  115. package/assets/templates/views/product/props.ts +27 -0
  116. package/assets/templates/views/product/registry.tsx +103 -0
  117. package/assets/templates/views/product/schema.ts +121 -0
  118. package/assets/templates/views/static/content.ts +99 -0
  119. package/assets/templates/views/static/index.tsx +60 -0
  120. package/assets/templates/views/static/registry.tsx +66 -0
  121. package/assets/templates/views/static/schema.ts +45 -0
  122. package/bin/storefront.js +2424 -148
  123. package/examples/merchandising/category-led.json +37 -0
  124. package/examples/merchandising/collection-led.json +37 -0
  125. package/package.json +15 -9
  126. package/recipes/archetypes.json +28 -14
  127. package/recipes/catalog.json +400 -0
  128. package/recipes/catalog.mjs +128 -0
  129. package/recipes/catalog.schema.json +64 -0
  130. package/recipes/feature.schema.json +89 -0
  131. package/recipes/features.json +162 -0
  132. package/recipes/manifest.schema.json +69 -0
  133. package/recipes/operations.schema.json +43 -0
  134. package/recipes/recipe.schema.json +11 -2
  135. package/recipes/recipes.json +76 -19
  136. package/recipes/sections.json +34 -5
  137. package/recipes/validate.mjs +838 -32
  138. package/scripts/candidate.mjs +236 -0
  139. package/scripts/composers.mjs +160 -0
  140. package/scripts/env-file.mjs +86 -0
  141. package/scripts/file-classes.mjs +121 -0
  142. package/scripts/logical-controls.mjs +61 -0
  143. package/scripts/manifest.mjs +298 -0
  144. package/scripts/migrate-english-routes.mjs +276 -0
  145. package/scripts/operation-registry.mjs +256 -0
  146. package/scripts/operations.mjs +720 -0
  147. package/scripts/ownership-conflict.mjs +356 -0
  148. package/scripts/pinning.mjs +573 -0
  149. package/scripts/plan-apply.mjs +987 -0
  150. package/scripts/preflight.mjs +51 -0
  151. package/scripts/reconcile.mjs +549 -0
  152. package/scripts/render-templates.mjs +338 -68
  153. package/scripts/schema-validate.mjs +132 -0
  154. package/scripts/section-compat.mjs +300 -0
  155. package/scripts/section-config.mjs +253 -0
  156. package/scripts/section-operations.mjs +508 -0
  157. package/scripts/starter.mjs +17 -0
  158. package/scripts/sync-assets.mjs +22 -15
  159. package/scripts/theme/color.mjs +185 -0
  160. package/scripts/theme/css-theme.mjs +233 -0
  161. package/scripts/theme/fonts.mjs +123 -0
  162. package/scripts/theme/import-export.mjs +125 -0
  163. package/scripts/theme/preset.mjs +93 -0
  164. package/scripts/theme/theme-plan.mjs +292 -0
  165. package/scripts/theme/tokens.mjs +137 -0
  166. package/scripts/view-assembly.mjs +337 -0
  167. package/scripts/view-selection.mjs +211 -0
  168. package/skills/sf-feature/SKILL.md +80 -0
  169. package/skills/sf-setup/SKILL.md +79 -0
  170. package/skills/sf-theme-gen/SKILL.md +100 -30
  171. package/skills/sf-theme-gen/references/shadcn-theming.md +87 -0
  172. package/skills/{shadcn-theming/references/templates → sf-theme-gen/references}/theme-setup.md +15 -19
  173. package/skills/{shadcn-theming → sf-theme-gen}/references/theming-guide.md +6 -11
  174. package/skills/sf-view-gen/SKILL.md +146 -93
  175. package/skills/sf-view-gen/assets/recipes/archetypes.json +28 -14
  176. package/skills/sf-view-gen/assets/recipes/catalog.json +400 -0
  177. package/skills/sf-view-gen/assets/recipes/catalog.schema.json +64 -0
  178. package/skills/sf-view-gen/assets/recipes/features.json +162 -0
  179. package/skills/sf-view-gen/assets/recipes/recipes.json +76 -19
  180. package/skills/sf-view-gen/assets/recipes/sections.json +34 -5
  181. package/skills/sf-view-gen/assets/templates/views/sections/cta.client.tsx +25 -0
  182. package/skills/sf-view-gen/assets/templates/views/sections/cta.tsx +16 -0
  183. package/skills/sf-view-gen/assets/templates/views/sections/faq.client.tsx +13 -9
  184. package/skills/sf-view-gen/assets/templates/views/sections/faq.tsx +2 -7
  185. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.client.tsx +12 -8
  186. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.tsx +2 -7
  187. package/skills/sf-view-gen/assets/templates/views/sections/pricing.client.tsx +14 -10
  188. package/skills/sf-view-gen/assets/templates/views/sections/pricing.tsx +2 -9
  189. package/skills/sf-view-gen/assets/templates/views/sections/products.client.tsx +100 -0
  190. package/skills/sf-view-gen/assets/templates/views/sections/products.tsx +19 -0
  191. package/skills/sf-view-gen/assets/templates/views/sections/schema.cta.ts +19 -0
  192. package/skills/sf-view-gen/assets/templates/views/sections/schema.faq.ts +18 -0
  193. package/skills/sf-view-gen/assets/templates/views/sections/schema.logoCloud.ts +19 -0
  194. package/skills/sf-view-gen/assets/templates/views/sections/schema.pricing.ts +26 -0
  195. package/skills/sf-view-gen/assets/templates/views/sections/schema.products.ts +39 -0
  196. package/skills/sf-view-gen/assets/templates/views/sections/schema.statistics.ts +25 -0
  197. package/skills/sf-view-gen/assets/templates/views/sections/schema.testimonials.ts +23 -0
  198. package/skills/sf-view-gen/assets/templates/views/sections/statistics.client.tsx +16 -8
  199. package/skills/sf-view-gen/assets/templates/views/sections/statistics.tsx +2 -7
  200. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.client.tsx +13 -9
  201. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.tsx +2 -5
  202. package/skills/{json-render-core/SKILL.md → sf-view-gen/references/json-render-core.md} +4 -27
  203. package/skills/{json-render-next/SKILL.md → sf-view-gen/references/json-render-next.md} +5 -8
  204. package/skills/{json-render-react/SKILL.md → sf-view-gen/references/json-render-react.md} +3 -5
  205. package/skills/{json-render-shadcn/SKILL.md → sf-view-gen/references/json-render-shadcn.md} +3 -5
  206. package/skills/sf-view-gen/references/views.md +58 -33
  207. package/skills/sf-view-gen/scripts/catalog.mjs +128 -0
  208. package/skills/sf-view-gen/scripts/schema-validate.mjs +132 -0
  209. package/skills/sf-view-gen/scripts/validate.mjs +838 -32
  210. package/skills/storefront-workflow/SKILL.md +179 -0
  211. package/skills/{storefront-best-practices/reference/design.md → storefront-workflow/references/design-consistency.md} +13 -29
  212. package/CHANGELOG.md +0 -61
  213. package/commands/sf-add-view.md +0 -26
  214. package/commands/sf-theme.md +0 -20
  215. package/commands/sf-view.md +0 -25
  216. package/skills/building-storefronts/SKILL.md +0 -178
  217. package/skills/building-storefronts/references/frontend-integration.md +0 -229
  218. package/skills/json-render-remotion/SKILL.md +0 -111
  219. package/skills/json-render-solid/SKILL.md +0 -204
  220. package/skills/nextjs-shadcn/SKILL.md +0 -367
  221. package/skills/nextjs-shadcn/references/architecture.md +0 -499
  222. package/skills/nextjs-shadcn/references/project-setup.md +0 -162
  223. package/skills/nextjs-shadcn/references/shadcn-platform.md +0 -261
  224. package/skills/nextjs-shadcn/references/sidebar.md +0 -274
  225. package/skills/nextjs-shadcn/references/styling.md +0 -555
  226. package/skills/sf-view-gen/assets/templates/views/category/index.tsx +0 -86
  227. package/skills/sf-view-gen/assets/templates/views/category/registry.tsx +0 -48
  228. package/skills/sf-view-gen/assets/templates/views/checkout/index.tsx +0 -192
  229. package/skills/sf-view-gen/assets/templates/views/checkout/registry.tsx +0 -45
  230. package/skills/sf-view-gen/assets/templates/views/product/index.tsx +0 -122
  231. package/skills/sf-view-gen/assets/templates/views/product/registry.tsx +0 -49
  232. package/skills/sf-view-gen/assets/templates/views/user-panel/index.tsx +0 -168
  233. package/skills/sf-view-gen/assets/templates/views/user-panel/registry.tsx +0 -49
  234. package/skills/shadcn-component-discovery/SKILL.md +0 -273
  235. package/skills/shadcn-component-discovery/references/registries.md +0 -226
  236. package/skills/shadcn-theming/SKILL.md +0 -104
  237. package/skills/storefront-best-practices/SKILL.md +0 -421
  238. package/skills/storefront-best-practices/reference/components/breadcrumbs.md +0 -123
  239. package/skills/storefront-best-practices/reference/components/cart-popup.md +0 -189
  240. package/skills/storefront-best-practices/reference/components/country-selector.md +0 -298
  241. package/skills/storefront-best-practices/reference/components/footer.md +0 -112
  242. package/skills/storefront-best-practices/reference/components/hero.md +0 -241
  243. package/skills/storefront-best-practices/reference/components/megamenu.md +0 -239
  244. package/skills/storefront-best-practices/reference/components/navbar.md +0 -397
  245. package/skills/storefront-best-practices/reference/components/popups.md +0 -221
  246. package/skills/storefront-best-practices/reference/components/product-card.md +0 -125
  247. package/skills/storefront-best-practices/reference/components/product-reviews.md +0 -217
  248. package/skills/storefront-best-practices/reference/components/product-slider.md +0 -174
  249. package/skills/storefront-best-practices/reference/components/search.md +0 -101
  250. package/skills/storefront-best-practices/reference/connecting-to-backend.md +0 -391
  251. package/skills/storefront-best-practices/reference/features/promotions.md +0 -307
  252. package/skills/storefront-best-practices/reference/features/wishlist.md +0 -230
  253. package/skills/storefront-best-practices/reference/layouts/account.md +0 -380
  254. package/skills/storefront-best-practices/reference/layouts/cart.md +0 -316
  255. package/skills/storefront-best-practices/reference/layouts/checkout.md +0 -486
  256. package/skills/storefront-best-practices/reference/layouts/home-page.md +0 -264
  257. package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +0 -231
  258. package/skills/storefront-best-practices/reference/layouts/product-details.md +0 -527
  259. package/skills/storefront-best-practices/reference/layouts/product-listing.md +0 -520
  260. package/skills/storefront-best-practices/reference/layouts/static-pages.md +0 -356
  261. package/skills/storefront-best-practices/reference/medusa.md +0 -307
  262. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/mobile-responsiveness.md +0 -0
  263. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/seo.md +0 -0
@@ -0,0 +1,400 @@
1
+ {
2
+ "$schema": "./catalog.schema.json",
3
+ "catalogVersion": 1,
4
+ "description": "Initial semantic executable catalog (E1 tasks 1.2-1.3), classified by source-shape inspection of the @thorprovider/blocks prop contracts and the view templates. Scope: every slot the curated MVP views (home, product, category, checkout, account) render by default, covering both the selectable archetype slots declared by recipes/archetypes.json and the nested default slots that have no archetype (category.productCard, checkout.confirmation) so no legacy default support is dropped. The account view (change: storefront-customer-auth) declares the login/register slots: LoginPage1/SignUpPage1 are executable while LoginPage2 is held for its divergent callback contract. A variant is `executable` only when the slot's canonical data shape can feed its block props without a prop bridge and without inventing data; every other non-default variant is `held` with the specific reason. The one executable non-default swap is home.benefits Feature1 -> Feature2 (identical item contract, no bridge). E2 task 6.1 declares the base listing slot category.filter CategoryFilter2 as the single controlled listing (dataShape `CatalogQuery & CatalogResults`, state controlled) with the search/filter controls contributed by the logical mounts category.search / category.filters (no second block). Source-shape compatibility is not end-to-end backend behavior proof: the render is executable, but the category.search.apply / category.filter.apply actions remain held under the capability catalog (owner: storefront-catalog-search-filters, E2) until a real runtime proof. The `capabilities` map declares those operational capabilities with their requirements and verification state; E1 declares the held category.filter.apply action, E2 adds the held category.search.apply action and the held category.price.range capability that gates the category-filters showPrice option; no capability is claimed verified.",
5
+ "slots": {
6
+ "home.hero": {
7
+ "default": "StorefrontHero1",
8
+ "variants": {
9
+ "StorefrontHero1": {
10
+ "status": "executable",
11
+ "block": "StorefrontHero",
12
+ "dataShape": "FeaturedProduct[]",
13
+ "events": ["addToCart", "buyNow", "selectColor"],
14
+ "state": "uncontrolled",
15
+ "requirements": ["heroProducts"]
16
+ },
17
+ "StorefrontHero2": {
18
+ "status": "held",
19
+ "reason": "block requires a product with reviews/trending/tag and emits a search event; the canonical FeaturedProduct shape carries rating/reviewCount but not trending/tag, and no verified prop bridge exists",
20
+ "block": "StorefrontHero",
21
+ "dataShape": "StorefrontHero2FeaturedProduct[]",
22
+ "events": ["search", "productClick"],
23
+ "state": "uncontrolled",
24
+ "requirements": ["heroProducts"]
25
+ },
26
+ "StorefrontHero5": {
27
+ "status": "held",
28
+ "reason": "featuredDeals[].description is absent from the canonical product shape and Department.count would require inventing counts; no truthful prop bridge can be derived from source",
29
+ "block": "StorefrontHero",
30
+ "dataShape": "FeaturedDeal[] & Department[]",
31
+ "events": ["departmentClick", "shopNow", "locateStores"],
32
+ "state": "uncontrolled",
33
+ "requirements": ["featuredDeals", "departments"]
34
+ },
35
+ "StorefrontHero6": {
36
+ "status": "held",
37
+ "reason": "block requires deals with discount/endTime and categories with a required count; the canonical shape supplies neither without invention and no verified prop bridge exists",
38
+ "block": "StorefrontHero",
39
+ "dataShape": "HeroDeal[] & HeroCategory[]",
40
+ "events": ["shopNow", "categoryClick"],
41
+ "state": "uncontrolled",
42
+ "requirements": ["deals", "categories"]
43
+ }
44
+ }
45
+ },
46
+ "home.benefits": {
47
+ "default": "Feature1",
48
+ "variants": {
49
+ "Feature1": {
50
+ "status": "executable",
51
+ "block": "Feature",
52
+ "dataShape": "BenefitsItem[]",
53
+ "events": ["ctaClick", "learnMore"],
54
+ "state": "uncontrolled",
55
+ "requirements": []
56
+ },
57
+ "Feature2": {
58
+ "status": "executable",
59
+ "block": "Feature",
60
+ "dataShape": "BenefitsItem[]",
61
+ "events": [],
62
+ "state": "uncontrolled",
63
+ "requirements": []
64
+ }
65
+ }
66
+ },
67
+ "home.categories": {
68
+ "default": "ProductCategory2",
69
+ "variants": {
70
+ "ProductCategory2": {
71
+ "status": "executable",
72
+ "block": "CategoryCards",
73
+ "dataShape": "CategoryCard[]",
74
+ "events": ["browse"],
75
+ "state": "uncontrolled",
76
+ "requirements": ["categories"]
77
+ },
78
+ "ProductCategory1": {
79
+ "status": "held",
80
+ "reason": "block requires categories with a subtitle and a named icon, which the canonical CategoryCard shape does not supply; no verified prop bridge exists",
81
+ "block": "CategoryCards",
82
+ "dataShape": "ProductCategory1Category[]",
83
+ "events": [],
84
+ "state": "uncontrolled",
85
+ "requirements": ["categories"]
86
+ },
87
+ "ProductCategory4": {
88
+ "status": "held",
89
+ "reason": "block requires categories with title/icon/customers/products/avatarImages, which the canonical CategoryCard shape does not supply; no verified prop bridge exists",
90
+ "block": "CategoryCards",
91
+ "dataShape": "ProductCategory4Category[]",
92
+ "events": ["categoryClick"],
93
+ "state": "uncontrolled",
94
+ "requirements": ["categories"]
95
+ },
96
+ "ProductCategory5": {
97
+ "status": "held",
98
+ "reason": "block requires categories with subcategories and a required itemCount plus a search control, which the canonical CategoryCard shape does not supply; no verified prop bridge exists",
99
+ "block": "CategoryCards",
100
+ "dataShape": "ProductCategory5Category[]",
101
+ "events": ["browse", "viewAll"],
102
+ "state": "uncontrolled",
103
+ "requirements": ["categories"]
104
+ }
105
+ }
106
+ },
107
+ "product.overview": {
108
+ "default": "ProductOverview1",
109
+ "variants": {
110
+ "ProductOverview1": {
111
+ "status": "executable",
112
+ "block": "ProductOverview",
113
+ "dataShape": "ProductDetailProduct",
114
+ "events": ["selectVariant", "quantityChange", "addToCart", "toggleWishlist"],
115
+ "state": "controlled",
116
+ "requirements": ["selectedProduct", "selectedVariantId"]
117
+ },
118
+ "ProductOverview2": {
119
+ "status": "held",
120
+ "reason": "block requires a product with brand/sku/sizes/colors and wishlist events, a shape the canonical ProductDetailProduct does not supply; no verified prop bridge exists",
121
+ "block": "ProductOverview",
122
+ "dataShape": "ProductOverview2Product",
123
+ "events": ["addToCart", "toggleWishlist"],
124
+ "state": "uncontrolled",
125
+ "requirements": ["selectedProduct"]
126
+ },
127
+ "ProductOverview5": {
128
+ "status": "held",
129
+ "reason": "block requires specifications/features/mainImages/galleryImages/userReviews, a shape the canonical ProductDetailProduct does not supply; no verified prop bridge exists",
130
+ "block": "ProductOverview",
131
+ "dataShape": "ProductOverview5Product",
132
+ "events": ["addToCart", "toggleWishlist"],
133
+ "state": "uncontrolled",
134
+ "requirements": ["selectedProduct"]
135
+ }
136
+ }
137
+ },
138
+ "product.reviews": {
139
+ "default": "ReviewRating1",
140
+ "variants": {
141
+ "ReviewRating1": {
142
+ "status": "executable",
143
+ "block": "Reviews",
144
+ "dataShape": "Review[]",
145
+ "events": ["toggleReviewLike", "submitReview"],
146
+ "state": "uncontrolled",
147
+ "requirements": ["productReviews"]
148
+ },
149
+ "ReviewRating2": {
150
+ "status": "held",
151
+ "reason": "block requires an aggregate averageRating/ratingDistribution and carousel controls the canonical Review[] shape does not supply; no verified prop bridge exists",
152
+ "block": "Reviews",
153
+ "dataShape": "Review[] & RatingSummary",
154
+ "events": ["like", "comment"],
155
+ "state": "uncontrolled",
156
+ "requirements": ["productReviews"]
157
+ }
158
+ }
159
+ },
160
+ "product.breadcrumb": {
161
+ "default": "Breadcrumb",
162
+ "variants": {
163
+ "Breadcrumb": {
164
+ "status": "executable",
165
+ "block": "Breadcrumb",
166
+ "dataShape": "BreadcrumbItemData[]",
167
+ "events": [],
168
+ "state": "uncontrolled",
169
+ "requirements": ["productBreadcrumb"]
170
+ }
171
+ }
172
+ },
173
+ "cart.view": {
174
+ "default": "ShoppingCart4",
175
+ "variants": {
176
+ "ShoppingCart4": {
177
+ "status": "executable",
178
+ "block": "ShoppingCart4",
179
+ "dataShape": "CartLineState[] & CartTotalsState",
180
+ "events": ["refreshCart", "updateQuantity", "removeFromCart", "checkout", "continueShopping"],
181
+ "state": "controlled",
182
+ "requirements": ["cart", "cartTotals"]
183
+ }
184
+ }
185
+ },
186
+ "category.filter": {
187
+ "default": "CategoryFilter2",
188
+ "variants": {
189
+ "CategoryFilter2": {
190
+ "status": "executable",
191
+ "block": "CategoryFilter",
192
+ "dataShape": "CatalogQuery & CatalogResults",
193
+ "events": [
194
+ "productClick",
195
+ "pageChange",
196
+ "pageSizeChange",
197
+ "sortChange",
198
+ "filterChange",
199
+ "searchChange",
200
+ "clearFilters",
201
+ "repair",
202
+ "retry",
203
+ "removeFilter"
204
+ ],
205
+ "state": "controlled",
206
+ "requirements": ["categoryProducts", "categoryQuery", "facetSupport"]
207
+ },
208
+ "CategoryFilter1": {
209
+ "status": "held",
210
+ "reason": "block requires categories/priceRanges/sortOptions with counts the canonical CardProduct[] listing does not supply; no verified prop bridge exists",
211
+ "block": "CategoryFilter",
212
+ "dataShape": "CategoryFilter1Filters",
213
+ "events": ["filterChange"],
214
+ "state": "uncontrolled",
215
+ "requirements": ["categoryProducts"]
216
+ },
217
+ "CategoryFilter4": {
218
+ "status": "held",
219
+ "reason": "block requires an advanced facets contract (modelTypes/rangeBands/frameSizes/colourways/features) the canonical listing does not supply; no verified prop bridge exists",
220
+ "block": "CategoryFilter",
221
+ "dataShape": "CategoryFilter4Facets",
222
+ "events": ["filterChange"],
223
+ "state": "uncontrolled",
224
+ "requirements": ["categoryProducts"]
225
+ }
226
+ }
227
+ },
228
+ "category.productCard": {
229
+ "default": "ProductCard1",
230
+ "variants": {
231
+ "ProductCard1": {
232
+ "status": "executable",
233
+ "block": "ProductCard",
234
+ "dataShape": "CardProduct[]",
235
+ "events": ["addToCart", "openProduct"],
236
+ "state": "uncontrolled",
237
+ "requirements": ["categoryProducts"]
238
+ },
239
+ "ProductCard2": {
240
+ "status": "held",
241
+ "reason": "block requires a product with a required rating, a shape the canonical CardProduct listing does not supply; no verified prop bridge exists",
242
+ "block": "ProductCard",
243
+ "dataShape": "ProductCard2Product[]",
244
+ "events": [],
245
+ "state": "uncontrolled",
246
+ "requirements": ["categoryProducts"]
247
+ },
248
+ "ProductCard3": {
249
+ "status": "held",
250
+ "reason": "block requires a product with subtitle/originalPrice/reviewCount/discountPercentage and a badge, a shape the canonical CardProduct listing does not supply; no verified prop bridge exists",
251
+ "block": "ProductCard",
252
+ "dataShape": "ProductCard3Product[]",
253
+ "events": ["toggleWishlist", "viewSimilar", "productClick"],
254
+ "state": "controlled",
255
+ "requirements": ["categoryProducts"]
256
+ },
257
+ "ProductCard5": {
258
+ "status": "held",
259
+ "reason": "block requires a product with name/category/stock/specifications/brand, a shape the canonical CardProduct listing does not supply; no verified prop bridge exists",
260
+ "block": "ProductCard",
261
+ "dataShape": "ProductCard5Product[]",
262
+ "events": ["toggleWishlist", "addToCart", "quickView", "compare", "export"],
263
+ "state": "controlled",
264
+ "requirements": ["categoryProducts"]
265
+ }
266
+ }
267
+ },
268
+ "checkout.checkout": {
269
+ "default": "CheckoutForm1",
270
+ "variants": {
271
+ "CheckoutForm1": {
272
+ "status": "executable",
273
+ "block": "Checkout",
274
+ "dataShape": "CheckoutForm1FormData",
275
+ "events": ["retryTotals", "selectShippingOption", "applyPromo", "submit"],
276
+ "state": "controlled",
277
+ "requirements": ["checkoutCountries", "checkoutPaymentMethods"]
278
+ },
279
+ "CheckoutForm2": {
280
+ "status": "held",
281
+ "reason": "unproven checkout variant: E1 keeps only the default CheckoutForm1, whose behavior is unchanged; no verified prop bridge exists",
282
+ "block": "Checkout",
283
+ "dataShape": "CheckoutForm2Data",
284
+ "events": ["submit"],
285
+ "state": "controlled",
286
+ "requirements": ["checkoutCountries", "checkoutPaymentMethods"]
287
+ },
288
+ "CheckoutForm3": {
289
+ "status": "held",
290
+ "reason": "unproven checkout variant: E1 keeps only the default CheckoutForm1, whose behavior is unchanged; no verified prop bridge exists",
291
+ "block": "Checkout",
292
+ "dataShape": "CheckoutForm3FormData",
293
+ "events": ["submit"],
294
+ "state": "controlled",
295
+ "requirements": ["checkoutCountries", "checkoutPaymentMethods"]
296
+ }
297
+ }
298
+ },
299
+ "checkout.confirmation": {
300
+ "default": "CheckoutConfirmation",
301
+ "variants": {
302
+ "CheckoutConfirmation": {
303
+ "status": "executable",
304
+ "block": "CheckoutConfirmation",
305
+ "dataShape": "Receipt",
306
+ "events": ["continueShopping"],
307
+ "state": "uncontrolled",
308
+ "requirements": ["lastReceipt"]
309
+ }
310
+ }
311
+ },
312
+ "account.login": {
313
+ "default": "LoginPage1",
314
+ "variants": {
315
+ "LoginPage1": {
316
+ "status": "executable",
317
+ "block": "LoginPage1",
318
+ "dataShape": "LoginPage1FormData & AccountAuthResult",
319
+ "events": ["submit", "createAccount"],
320
+ "state": "controlled",
321
+ "requirements": ["accountCustomer", "accountAuthenticated"]
322
+ },
323
+ "LoginPage2": {
324
+ "status": "held",
325
+ "reason": "block's onSubmit takes positional (email, password) with no remember-me field and renders a 2-column social grid; no verified prop bridge from the account element contract exists",
326
+ "block": "LoginPage2",
327
+ "dataShape": "LoginPage2Credentials & AccountAuthResult",
328
+ "events": ["submit", "forgotPassword", "signUp"],
329
+ "state": "controlled",
330
+ "requirements": ["accountCustomer", "accountAuthenticated"]
331
+ }
332
+ }
333
+ },
334
+ "account.register": {
335
+ "default": "SignUpPage1",
336
+ "variants": {
337
+ "SignUpPage1": {
338
+ "status": "executable",
339
+ "block": "SignUpPage1",
340
+ "dataShape": "SignUpPage1Data & AccountAuthResult",
341
+ "events": ["submit", "signIn"],
342
+ "state": "controlled",
343
+ "requirements": ["accountCustomer", "accountAuthenticated"]
344
+ }
345
+ }
346
+ },
347
+ "account.orders": {
348
+ "default": "OrderHistory2",
349
+ "variants": {
350
+ "OrderHistory2": {
351
+ "status": "executable",
352
+ "block": "OrderHistory2",
353
+ "dataShape": "AccountOrderView[] & OrderHistory2Labels",
354
+ "events": ["viewDetails"],
355
+ "state": "controlled",
356
+ "requirements": ["accountCustomer", "accountAuthenticated"]
357
+ },
358
+ "OrderHistory1": {
359
+ "status": "held",
360
+ "reason": "block models one row per order item (an item-row list), the wrong shape for an order-level history; the canonical order list cannot feed it without inventing an aggregate row",
361
+ "block": "OrderHistory1",
362
+ "dataShape": "OrderHistory1Order[]",
363
+ "events": [],
364
+ "state": "controlled",
365
+ "requirements": ["accountCustomer", "accountAuthenticated"]
366
+ }
367
+ }
368
+ },
369
+ "category.breadcrumb": {
370
+ "default": "Breadcrumb",
371
+ "variants": {
372
+ "Breadcrumb": {
373
+ "status": "executable",
374
+ "block": "Breadcrumb",
375
+ "dataShape": "BreadcrumbItemData[]",
376
+ "events": [],
377
+ "state": "uncontrolled",
378
+ "requirements": ["categoryBreadcrumb"]
379
+ }
380
+ }
381
+ }
382
+ },
383
+ "capabilities": {
384
+ "category.search.apply": {
385
+ "requirements": ["adapter.getCatalogProducts(query).text"],
386
+ "verification": "unverified",
387
+ "owner": "storefront-catalog-search-filters"
388
+ },
389
+ "category.filter.apply": {
390
+ "requirements": ["adapter.getProducts(query)"],
391
+ "verification": "unverified",
392
+ "owner": "storefront-catalog-search-filters"
393
+ },
394
+ "category.price.range": {
395
+ "requirements": ["adapter.getAvailableFilters(query).priceRange"],
396
+ "verification": "unverified",
397
+ "owner": "storefront-catalog-search-filters"
398
+ }
399
+ }
400
+ }
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://thorcommerce.dev/storefront/catalog.schema.json",
4
+ "title": "Thor Commerce Storefront Semantic Catalog",
5
+ "description": "The semantic executable catalog: per view slot and candidate variant, its execution status (executable | held), the block it renders, its canonical data shape, emitted events, controlled/uncontrolled state, requirements, and an optional prop bridge. Held entries carry a reason. Also declares operational capabilities with their backend/data requirements and a verification state (verified | unverified); an unverified capability names its owning change in `owner`. Versioned by catalogVersion.",
6
+ "type": "object",
7
+ "required": ["catalogVersion", "slots"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "$schema": { "type": "string" },
11
+ "catalogVersion": { "type": "number", "enum": [1] },
12
+ "description": { "type": "string" },
13
+ "slots": {
14
+ "type": "object",
15
+ "additionalProperties": {
16
+ "type": "object",
17
+ "required": ["default", "variants"],
18
+ "additionalProperties": false,
19
+ "properties": {
20
+ "default": { "type": "string", "minLength": 1 },
21
+ "variants": {
22
+ "type": "object",
23
+ "additionalProperties": {
24
+ "type": "object",
25
+ "required": ["status", "block", "dataShape", "events", "state", "requirements"],
26
+ "additionalProperties": false,
27
+ "properties": {
28
+ "status": { "type": "string", "enum": ["executable", "held"] },
29
+ "reason": { "type": "string", "minLength": 1 },
30
+ "block": { "type": "string", "minLength": 1 },
31
+ "dataShape": { "type": "string", "minLength": 1 },
32
+ "events": { "type": "array", "items": { "type": "string" } },
33
+ "state": { "type": "string", "enum": ["controlled", "uncontrolled"] },
34
+ "requirements": { "type": "array", "items": { "type": "string" } },
35
+ "bridge": {
36
+ "type": "object",
37
+ "required": ["from", "to"],
38
+ "additionalProperties": false,
39
+ "properties": {
40
+ "from": { "type": "string", "minLength": 1 },
41
+ "to": { "type": "string", "minLength": 1 }
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ },
50
+ "capabilities": {
51
+ "type": "object",
52
+ "additionalProperties": {
53
+ "type": "object",
54
+ "required": ["requirements", "verification"],
55
+ "additionalProperties": false,
56
+ "properties": {
57
+ "requirements": { "type": "array", "items": { "type": "string" } },
58
+ "verification": { "type": "string", "enum": ["verified", "unverified"] },
59
+ "owner": { "type": "string", "minLength": 1 }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,162 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "features": [
4
+ {
5
+ "name": "checkout",
6
+ "mvp": true,
7
+ "description": "Multi-step checkout: cart validation, shipping, payment, and order placement.",
8
+ "route": "/checkout",
9
+ "views": ["checkout"],
10
+ "state": [
11
+ "checkout",
12
+ "checkoutCountries",
13
+ "checkoutPaymentMethods",
14
+ "checkoutPending",
15
+ "checkoutError",
16
+ "lastOrder",
17
+ "lastOrderLines",
18
+ "orderPlaced"
19
+ ],
20
+ "handlers": ["placeOrder", "goToCheckout", "continueShopping"],
21
+ "adapter": { "needs": ["createCart", "completeCheckout"] },
22
+ "config": { "type": "object", "additionalProperties": false, "properties": {} },
23
+ "configRejection": {
24
+ "code": "E_UNPROVEN_CHECKOUT_OPTION",
25
+ "message": "checkout variants and route options are not proven; the checkout feature accepts an empty config only"
26
+ },
27
+ "surfaces": []
28
+ },
29
+ {
30
+ "name": "customer-auth",
31
+ "mvp": true,
32
+ "description": "Customer authentication: registration, login, logout, and a minimal signed-in state. Owns the account view.",
33
+ "route": "/account",
34
+ "views": ["account"],
35
+ "state": [
36
+ "accountCustomer",
37
+ "accountAuthenticated",
38
+ "accountMode",
39
+ "accountPending",
40
+ "accountError"
41
+ ],
42
+ "handlers": ["logout", "showRegister", "showLogin"],
43
+ "adapter": { "needs": [] },
44
+ "config": { "type": "object", "additionalProperties": false, "properties": {} },
45
+ "surfaces": []
46
+ },
47
+ {
48
+ "name": "static-pages",
49
+ "mvp": true,
50
+ "description": "Content-driven static pages (contact, legal, about) served with per-page metadata. Owns the static view.",
51
+ "route": "/contact",
52
+ "views": ["static"],
53
+ "state": [],
54
+ "handlers": [],
55
+ "adapter": { "needs": [] },
56
+ "config": { "type": "object", "additionalProperties": false, "properties": {} },
57
+ "surfaces": []
58
+ },
59
+ {
60
+ "name": "benefits",
61
+ "mvp": true,
62
+ "description": "Bounded editorial benefits feature that mounts at the home benefits slot; config selects the Feature1/Feature2 presentation while the item content stays user-owned.",
63
+ "views": [],
64
+ "state": [],
65
+ "handlers": [],
66
+ "adapter": { "needs": [] },
67
+ "config": {
68
+ "type": "object",
69
+ "additionalProperties": false,
70
+ "properties": {
71
+ "presentation": { "type": "string", "enum": ["compact", "detailed"], "default": "compact" }
72
+ }
73
+ },
74
+ "presentations": { "compact": "Feature1", "detailed": "Feature2" },
75
+ "mounts": [{ "id": "home-benefits", "host": "home", "slot": "benefits" }],
76
+ "surfaces": []
77
+ },
78
+ {
79
+ "name": "catalog-search",
80
+ "mvp": true,
81
+ "description": "Optional category-listing search control (E2). Installation is presence, not an enabled flag; the config only supplies an optional placeholder. The search input contributes to the single controlled CategoryFilter2 at the category.search logical slot and never adds a block.",
82
+ "views": [],
83
+ "state": [],
84
+ "handlers": [],
85
+ "adapter": { "needs": [] },
86
+ "config": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "properties": {
90
+ "placeholder": { "type": "string" }
91
+ }
92
+ },
93
+ "mounts": [{ "id": "category-search", "host": "category", "slot": "search" }],
94
+ "surfaces": []
95
+ },
96
+ {
97
+ "name": "category-filters",
98
+ "mvp": true,
99
+ "description": "Optional category-listing filter controls (E2). Installation is presence, not an enabled flag; the config selects which demonstrated basic filters show (filterSet) and whether the price control is shown (showPrice). The controls contribute to the single controlled CategoryFilter2 at the category.filters logical slot and never add a block.",
100
+ "views": [],
101
+ "state": [],
102
+ "handlers": [],
103
+ "adapter": { "needs": [] },
104
+ "config": {
105
+ "type": "object",
106
+ "additionalProperties": false,
107
+ "properties": {
108
+ "filterSet": {
109
+ "type": "array",
110
+ "items": { "type": "string", "enum": ["categories", "collections"] },
111
+ "default": ["categories", "collections"]
112
+ },
113
+ "showPrice": { "type": "boolean", "default": false }
114
+ }
115
+ },
116
+ "configRejection": {
117
+ "code": "E_UNVERIFIED_PRICE_RANGE",
118
+ "message": "showPrice: true requires a verified price range for the listing scope; capability \"category.price.range\" is unverified (held), so configure showPrice: false until a real range is demonstrated",
119
+ "when": { "field": "showPrice", "equals": true },
120
+ "capability": "category.price.range"
121
+ },
122
+ "mounts": [{ "id": "category-filters", "host": "category", "slot": "filters" }],
123
+ "surfaces": []
124
+ },
125
+ {
126
+ "name": "navigability",
127
+ "mvp": true,
128
+ "description": "Cross-cutting navigability feature: entity-derived breadcrumbs with BreadcrumbList structured data on the product and category surfaces, plus skip-to-content, current-page indication, back-to-top, and a mobile bottom navigation in the shared shell.",
129
+ "views": [],
130
+ "state": [],
131
+ "handlers": [],
132
+ "adapter": { "needs": [] },
133
+ "config": {
134
+ "type": "object",
135
+ "additionalProperties": false,
136
+ "properties": {
137
+ "presentation": { "type": "string", "enum": ["default"], "default": "default" },
138
+ "showBackToTop": { "type": "boolean", "default": true }
139
+ }
140
+ },
141
+ "presentations": { "default": "Breadcrumb" },
142
+ "mounts": [
143
+ { "id": "category-breadcrumb", "host": "category", "slot": "breadcrumb" },
144
+ { "id": "product-breadcrumb", "host": "product", "slot": "breadcrumb" }
145
+ ],
146
+ "surfaces": []
147
+ },
148
+ {
149
+ "name": "recommendations",
150
+ "mvp": false,
151
+ "description": "Related-products surface injected into the product view.",
152
+ "views": [],
153
+ "state": ["recommendedProducts"],
154
+ "handlers": [],
155
+ "adapter": { "needs": ["getRelatedProducts"], "fallback": "catalog" },
156
+ "config": { "type": "object", "additionalProperties": false, "properties": {} },
157
+ "surfaces": [
158
+ { "host": "product", "slot": "product.related", "element": "ProductCard" }
159
+ ]
160
+ }
161
+ ]
162
+ }