@thorprovider/storefront 0.3.1 → 0.4.0-mvp.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 (179) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +248 -99
  3. package/assets/templates/app/app/[[...slug]]/page.tsx +13 -2
  4. package/assets/templates/app/app/layout.tsx +18 -4
  5. package/assets/templates/app/app/robots.ts +13 -0
  6. package/assets/templates/app/app/sitemap.ts +12 -0
  7. package/assets/templates/app/lib/__STOREFRONT__/actions.ts +542 -0
  8. package/assets/templates/app/lib/__STOREFRONT__/cart-state.ts +66 -0
  9. package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +40 -57
  10. package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +608 -0
  11. package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +165 -0
  12. package/assets/templates/app/lib/__STOREFRONT__/handlers.ts +5 -36
  13. package/assets/templates/app/lib/__STOREFRONT__/icon-map.ts +27 -0
  14. package/assets/templates/app/lib/__STOREFRONT__/mappers.ts +13 -11
  15. package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +232 -51
  16. package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +3 -3
  17. package/assets/templates/app/lib/__STOREFRONT__/schema.ts +121 -0
  18. package/assets/templates/app/lib/__STOREFRONT__/session.core.ts +470 -0
  19. package/assets/templates/app/lib/__STOREFRONT__/session.ts +169 -0
  20. package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +96 -0
  21. package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +34 -0
  22. package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +100 -11
  23. package/assets/templates/app/lib/__STOREFRONT__/spec/page-root.ts +5 -1
  24. package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +26 -0
  25. package/assets/templates/app/lib/__STOREFRONT__/state.core.ts +257 -0
  26. package/assets/templates/app/lib/__STOREFRONT__/state.ts +7 -31
  27. package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +142 -0
  28. package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.client.tsx +25 -0
  29. package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.tsx +16 -0
  30. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.client.tsx +24 -0
  31. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.tsx +5 -18
  32. package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +17 -0
  33. package/assets/templates/app/lib/__STOREFRONT__/views/sections/logoCloud.tsx +26 -14
  34. package/assets/templates/app/lib/__STOREFRONT__/views/sections/pricing.tsx +36 -18
  35. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.cta.ts +19 -0
  36. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.faq.ts +18 -0
  37. package/assets/templates/app/lib/__STOREFRONT__/views/sections/statistics.tsx +35 -14
  38. package/assets/templates/app/lib/__STOREFRONT__/views/sections/testimonials.tsx +31 -13
  39. package/assets/templates/app/lib/__STOREFRONT__/views/types.ts +5 -3
  40. package/assets/templates/features/checkout/handlers.ts +153 -0
  41. package/assets/templates/features/checkout/state.ts +61 -0
  42. package/assets/templates/features/customer-auth/handlers.ts +59 -0
  43. package/assets/templates/features/customer-auth/state.ts +36 -0
  44. package/assets/templates/features/recommendations/state.ts +11 -0
  45. package/assets/templates/views/account/content.ts +35 -0
  46. package/assets/templates/views/account/index.tsx +114 -0
  47. package/assets/templates/views/account/registry.tsx +378 -0
  48. package/assets/templates/views/account/schema.ts +52 -0
  49. package/assets/templates/views/category/assembly/assembly.json +28 -0
  50. package/assets/templates/views/category/assembly/index.skeleton.tsx +193 -0
  51. package/assets/templates/views/category/assembly/registry.skeleton.tsx +33 -0
  52. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/catalog.part +5 -0
  53. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/element.part +34 -0
  54. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/registry.part +71 -0
  55. package/assets/templates/views/category/content.ts +30 -0
  56. package/assets/templates/views/category/data.ts +12 -0
  57. package/assets/templates/views/category/index.tsx +230 -0
  58. package/assets/templates/views/category/props.ts +16 -0
  59. package/assets/templates/views/category/registry.tsx +103 -0
  60. package/assets/templates/views/category/schema.ts +78 -0
  61. package/assets/templates/views/checkout/content.ts +105 -0
  62. package/assets/templates/views/checkout/data.ts +23 -0
  63. package/assets/templates/views/checkout/index.tsx +111 -0
  64. package/assets/templates/views/checkout/props.ts +49 -0
  65. package/assets/templates/views/checkout/registry.tsx +217 -0
  66. package/assets/templates/views/checkout/schema.ts +229 -0
  67. package/assets/templates/views/home/assembly/assembly.json +24 -0
  68. package/assets/templates/views/home/assembly/index.skeleton.tsx +72 -0
  69. package/assets/templates/views/home/assembly/registry.skeleton.tsx +25 -0
  70. package/assets/templates/views/home/assembly/slots/benefits/Feature1/binding.json +5 -0
  71. package/assets/templates/views/home/assembly/slots/benefits/Feature1/catalog.part +4 -0
  72. package/assets/templates/views/home/assembly/slots/benefits/Feature1/element.part +4 -0
  73. package/assets/templates/views/home/assembly/slots/benefits/Feature1/registry.part +11 -0
  74. package/assets/templates/views/home/assembly/slots/benefits/Feature2/binding.json +5 -0
  75. package/assets/templates/views/home/assembly/slots/benefits/Feature2/catalog.part +4 -0
  76. package/assets/templates/views/home/assembly/slots/benefits/Feature2/element.part +4 -0
  77. package/assets/templates/views/home/assembly/slots/benefits/Feature2/registry.part +10 -0
  78. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/binding.json +5 -0
  79. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/catalog.part +4 -0
  80. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/element.part +5 -0
  81. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/registry.part +16 -0
  82. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/binding.json +5 -0
  83. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/catalog.part +4 -0
  84. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +5 -0
  85. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +26 -0
  86. package/assets/templates/views/home/content.ts +70 -0
  87. package/assets/templates/views/home/data.ts +15 -0
  88. package/assets/templates/views/home/index.tsx +38 -84
  89. package/assets/templates/views/home/props.ts +22 -0
  90. package/assets/templates/views/home/registry.tsx +38 -22
  91. package/assets/templates/views/home/schema.ts +93 -0
  92. package/assets/templates/views/product/content.ts +25 -0
  93. package/assets/templates/views/product/data.ts +48 -0
  94. package/assets/templates/views/product/index.tsx +104 -0
  95. package/assets/templates/views/product/props.ts +20 -0
  96. package/assets/templates/views/product/registry.tsx +78 -0
  97. package/assets/templates/views/product/schema.ts +108 -0
  98. package/assets/templates/views/static/content.ts +99 -0
  99. package/assets/templates/views/static/index.tsx +60 -0
  100. package/assets/templates/views/static/registry.tsx +66 -0
  101. package/assets/templates/views/static/schema.ts +45 -0
  102. package/{skills/sf-view-gen/assets → assets}/templates/views/user-panel/index.tsx +8 -70
  103. package/{skills/sf-view-gen/assets → assets}/templates/views/user-panel/registry.tsx +23 -18
  104. package/assets/templates/views/user-panel/schema.ts +79 -0
  105. package/bin/storefront.js +1278 -79
  106. package/commands/sf-add-view.md +44 -13
  107. package/commands/sf-feature.md +28 -0
  108. package/commands/sf-theme.md +14 -8
  109. package/commands/sf-view.md +54 -12
  110. package/package.json +6 -4
  111. package/recipes/archetypes.json +28 -1
  112. package/recipes/catalog.json +361 -0
  113. package/recipes/catalog.mjs +128 -0
  114. package/recipes/catalog.schema.json +64 -0
  115. package/recipes/feature.schema.json +89 -0
  116. package/recipes/features.json +139 -0
  117. package/recipes/manifest.schema.json +69 -0
  118. package/recipes/operations.schema.json +43 -0
  119. package/recipes/recipe.schema.json +11 -2
  120. package/recipes/recipes.json +60 -5
  121. package/recipes/sections.json +24 -5
  122. package/recipes/validate.mjs +784 -32
  123. package/scripts/candidate.mjs +236 -0
  124. package/scripts/composers.mjs +140 -0
  125. package/scripts/file-classes.mjs +116 -0
  126. package/scripts/logical-controls.mjs +61 -0
  127. package/scripts/manifest.mjs +298 -0
  128. package/scripts/operation-registry.mjs +256 -0
  129. package/scripts/operations.mjs +720 -0
  130. package/scripts/ownership-conflict.mjs +356 -0
  131. package/scripts/pinning.mjs +504 -0
  132. package/scripts/plan-apply.mjs +843 -0
  133. package/scripts/preflight.mjs +51 -0
  134. package/scripts/reconcile.mjs +511 -0
  135. package/scripts/render-templates.mjs +245 -28
  136. package/scripts/schema-validate.mjs +132 -0
  137. package/scripts/section-compat.mjs +300 -0
  138. package/scripts/section-operations.mjs +508 -0
  139. package/scripts/sync-assets.mjs +22 -15
  140. package/scripts/view-assembly.mjs +291 -0
  141. package/scripts/view-selection.mjs +211 -0
  142. package/skills/sf-feature/SKILL.md +67 -0
  143. package/skills/sf-theme-gen/SKILL.md +12 -5
  144. package/skills/sf-view-gen/SKILL.md +123 -93
  145. package/skills/sf-view-gen/assets/recipes/archetypes.json +28 -1
  146. package/skills/sf-view-gen/assets/recipes/catalog.json +361 -0
  147. package/skills/sf-view-gen/assets/recipes/catalog.schema.json +64 -0
  148. package/skills/sf-view-gen/assets/recipes/features.json +139 -0
  149. package/skills/sf-view-gen/assets/recipes/recipes.json +60 -5
  150. package/skills/sf-view-gen/assets/recipes/sections.json +24 -5
  151. package/skills/sf-view-gen/assets/templates/views/sections/cta.client.tsx +25 -0
  152. package/skills/sf-view-gen/assets/templates/views/sections/cta.tsx +16 -0
  153. package/skills/sf-view-gen/assets/templates/views/sections/faq.client.tsx +13 -9
  154. package/skills/sf-view-gen/assets/templates/views/sections/faq.tsx +2 -7
  155. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.client.tsx +12 -8
  156. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.tsx +2 -7
  157. package/skills/sf-view-gen/assets/templates/views/sections/pricing.client.tsx +14 -10
  158. package/skills/sf-view-gen/assets/templates/views/sections/pricing.tsx +2 -9
  159. package/skills/sf-view-gen/assets/templates/views/sections/schema.cta.ts +19 -0
  160. package/skills/sf-view-gen/assets/templates/views/sections/schema.faq.ts +18 -0
  161. package/skills/sf-view-gen/assets/templates/views/sections/schema.logoCloud.ts +19 -0
  162. package/skills/sf-view-gen/assets/templates/views/sections/schema.pricing.ts +26 -0
  163. package/skills/sf-view-gen/assets/templates/views/sections/schema.statistics.ts +25 -0
  164. package/skills/sf-view-gen/assets/templates/views/sections/schema.testimonials.ts +23 -0
  165. package/skills/sf-view-gen/assets/templates/views/sections/statistics.client.tsx +16 -8
  166. package/skills/sf-view-gen/assets/templates/views/sections/statistics.tsx +2 -7
  167. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.client.tsx +13 -9
  168. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.tsx +2 -5
  169. package/skills/sf-view-gen/references/views.md +44 -10
  170. package/skills/sf-view-gen/scripts/catalog.mjs +128 -0
  171. package/skills/sf-view-gen/scripts/schema-validate.mjs +132 -0
  172. package/skills/sf-view-gen/scripts/validate.mjs +784 -32
  173. package/skills/storefront-workflow/SKILL.md +116 -0
  174. package/skills/sf-view-gen/assets/templates/views/category/index.tsx +0 -86
  175. package/skills/sf-view-gen/assets/templates/views/category/registry.tsx +0 -48
  176. package/skills/sf-view-gen/assets/templates/views/checkout/index.tsx +0 -192
  177. package/skills/sf-view-gen/assets/templates/views/checkout/registry.tsx +0 -45
  178. package/skills/sf-view-gen/assets/templates/views/product/index.tsx +0 -122
  179. package/skills/sf-view-gen/assets/templates/views/product/registry.tsx +0 -49
@@ -1,116 +1,146 @@
1
1
  ---
2
2
  name: sf-view-gen
3
- description: Add a new view or modify an existing view in a Thor Commerce storefront, selecting block variants and optional sections from this package's recipes
3
+ description: Add or remove a Thor Commerce storefront view, compose a view's faq/cta sections, or customize a view's user-owned files. Use when the user runs /sf-add-view or /sf-view, or asks to add/remove a view, add/remove/reorder a section, or change a view's content, props, or data. Delegates every structural write to the `storefront` CLI.
4
4
  ---
5
5
 
6
6
  # sf-view-gen
7
7
 
8
- Generate or modify a single view, driven by the **recipe registry inside this skill's own directory**, so storefronts are not identical.
8
+ Choose a view's composition **by meaning**, delegate the structural write to the
9
+ `storefront` CLI, and edit only the user-owned files it leaves behind. The
10
+ manifest (`storefront.config.json`) is the structural authority; the CLI is the
11
+ only writer of derived files.
9
12
 
10
- ## Source of truth
13
+ ## Read the registry through the CLI (never from memory)
11
14
 
12
- The workflow uses only the assets in this skill directory. There is no external project to consult and no package to locate: the recipes and templates it reads live under `assets/`, and the validator under `scripts/`. When a required choice is not determined by those files, ask the user.
15
+ Run from the storefront project root. The CLI resolves the project from the
16
+ current directory; do not pass a directory.
13
17
 
14
- ## Inputs (the only target-project facts this skill reads)
15
-
16
- - the existence of the target project's `package.json`
17
- - the existence of `lib/<name>/`
18
- - the existence of the project's `.opencode/`
19
- - the values returned by the wizard (view name, description of what the view should contain or emphasize)
20
-
21
- ## Outputs (what this skill produces)
18
+ ```bash
19
+ npx @thorprovider/storefront views --json # views, routes, supported intents/variants, installed
20
+ npx @thorprovider/storefront recipes --json # whole curated registry (views + features)
21
+ npx @thorprovider/storefront features --json # feature bundles and what they own
22
+ ```
22
23
 
23
- - generated files: the view module under `lib/<name>/views/<view>/` and one barrel entry in `lib/<name>/views/index.ts`
24
- - declared setup commands: the typecheck command it reports
25
- - a summary listing every file created and every command run
24
+ The `--json` output is the same registry the CLI validates against, so it cannot
25
+ advertise a selection the reconciler will not honour. `views --json` and
26
+ `recipes --json` also report, per view, `registeredSlots` (the non-removable core
27
+ slots), `allowedSections` (the section types the view may host), and
28
+ `maxSections` (the editorial-instance bound), plus a top-level `sectionTypes`
29
+ list marking each type `executable` or `held` with a reason. Do not read the JSON
30
+ files directly; they are an implementation detail.
31
+
32
+ ## Curated MVP set
33
+
34
+ - **Standalone views:** `home`, `category`, `product` — add with `view add`.
35
+ - **Feature-owned view:** `checkout` is owned by the `checkout` feature. A direct
36
+ `view add checkout` is rejected; run `feature add checkout` instead (see the
37
+ `sf-feature` skill).
38
+ - **Held post-MVP:** `user-panel` and recommendations are rejected before any
39
+ write. Do not try to install them. (Sections are composable — see below.)
40
+
41
+ ## Add a view
42
+
43
+ 1. Read `views --json`. If the view is held or feature-owned, stop and explain
44
+ the correct path.
45
+ 2. Choose an **intent by meaning** for each slot from the view's
46
+ `supportedIntents` (e.g. a "dense grid" description → the matching intent).
47
+ If the description is silent, use the default. Only use
48
+ `--variant slot=Name` when the user names an exact supported variant.
49
+ 3. Delegate the write:
50
+
51
+ ```bash
52
+ npx @thorprovider/storefront view add <name> [--intent slot=intent] [--variant slot=Variant]
53
+ ```
26
54
 
27
- ## Assets (inside this skill)
55
+ The CLI validates, updates the manifest, and reconciles the derived files.
56
+ If it exits non-zero, show its message and the supported options; do not
57
+ hand-edit derived files to work around it.
58
+ 4. Report the created view and its route.
28
59
 
29
- - `assets/recipes/recipes.json` — per view: `route`, `loader`, `requiredDataKeys`, `defaultVariants`, `allowedVariants`, `optionalSections`
30
- - `assets/recipes/sections.json` — optional sections → `elementType`, `variants`, `defaultVariant`, `propsHint`
31
- - `assets/recipes/archetypes.json` — the **archetype (intent) layer**: per slot (`<view>.<slot>`) a set of named **intents**, each with a `variant` and a `when` usage hint, plus the slot `default` intent
32
- - `assets/templates/views/<view>/` — the view modules it copies
33
- - `assets/templates/views/sections/` — the optional-section fragments
34
- - `scripts/validate.mjs` — the validator
35
- - `references/views.md` — the view composition contract
36
- - `../json-render-core/` — vendored catalog/schema/spec reference
37
- - `../json-render-react/` — vendored registry/action reference
60
+ ## Remove a view
38
61
 
39
- Paths are relative to this `SKILL.md`. Run the validator from here:
40
62
  ```bash
41
- node scripts/validate.mjs list
42
- node scripts/validate.mjs show <view>
43
- node scripts/validate.mjs archetypes
44
- node scripts/validate.mjs resolve <view> <slot> <intent>
45
- node scripts/validate.mjs validate '<composition-json>'
63
+ npx @thorprovider/storefront view remove <name>
46
64
  ```
47
65
 
48
- ## Allowed views
49
- `home`, `product`, `category`, `checkout`, `user-panel`
66
+ The CLI removes the derived wrapper and wiring. If it prints a preserved
67
+ user-owned file, relay that path to the user.
68
+
69
+ ## Compose sections (the agent selects, the CLI writes)
70
+
71
+ A view's ordered composition mixes its **registered slots** (core presentation
72
+ slots, mandatory and non-removable) with **editorial section instances**. The
73
+ agent selects the options and runs the CLI; the user describes the change in
74
+ natural language and never types a command.
75
+
76
+ 1. Read `views --json`. For the target view use `registeredSlots` (the core
77
+ slots), `allowedSections` (the host allowlist), and `maxSections`; read the
78
+ top-level `sectionTypes` list for which types are available.
79
+ 2. Only `faq` and `cta` are `executable`. Held types (`testimonials`,
80
+ `statistics`, `logoCloud`, `pricing`) are refused with a bounded reason. Never
81
+ present a held type as available, and never invent a reason — relay the
82
+ `reason` the registry reports.
83
+ 3. Compose through the CLI's section operations, carried in the operations
84
+ envelope `{ "formatVersion": 1, "operations": [ ... ] }`. Write the envelope
85
+ to a file, plan it, then apply the plan:
86
+
87
+ ```bash
88
+ npx @thorprovider/storefront plan --operations operations.json --out plan.json
89
+ npx @thorprovider/storefront apply --plan plan.json
90
+ ```
50
91
 
51
- Reject any other name with the full allowed list.
92
+ - `{ "op": "section.add", "view": "home", "type": "faq" }` — optional `id`,
93
+ `variant`, and `at` (0-based insertion index, default append). Omit `id` to
94
+ auto-mint one; a `variant` must be declared by the section type.
95
+ - `{ "op": "section.remove", "view": "home", "id": "home-faq-1" }` — removes an
96
+ editorial instance. A registered slot (`slot:<slot>`) cannot be removed.
97
+ - `{ "op": "section.reorder", "view": "home", "order": ["slot:hero", "home-faq-1", "slot:benefits", "slot:categories"] }` —
98
+ `order` must be a permutation of the view's current entry ids: every current
99
+ id exactly once, and every registered slot present.
100
+
101
+ 4. `plan` is read-only and validates the whole intended state; `apply --plan` is
102
+ the only writer. On success the CLI updates the manifest and reconciles derived
103
+ files. If either exits non-zero, relay its diagnostic (it names the offending
104
+ field) and the supported options; do not hand-edit the manifest or derived
105
+ files to work around it.
106
+ 5. A removed instance's content is preserved in the view's user-owned
107
+ `content.ts`; re-adding the same `id` rebinds it, while an auto-minted id never
108
+ rebinds preserved content.
109
+
110
+ ## Customize a view (the `/sf-view` workflow)
111
+
112
+ Only **user-owned** files may be edited by hand. They are consumed by thin
113
+ derived wrappers, so `sync` regenerates the wrappers around your edits.
114
+
115
+ User-owned (edit these): `lib/<name>/views/<view>/content.ts`, `props.ts`,
116
+ `data.ts`, `schema.ts`, and `lib/<name>/mappers.ts`.
117
+
118
+ Derived (never edit; the CLI regenerates them): `index.tsx`, `registry.tsx`,
119
+ `lib/<name>/views/index.ts`, `lib/<name>/views/registry.ts`, `catalog.ts`,
120
+ `runtime.ts`, `spec/index.ts`, `state.ts`, `handlers.ts`.
121
+
122
+ 1. Read the current user-owned files for the view.
123
+ 2. Apply the requested change there. Preserve exported names, element ids, and
124
+ the event/prop contracts the derived wrapper imports.
125
+ 3. Regenerate the wrappers: `npx @thorprovider/storefront sync`.
126
+ 4. If the change needs a different block variant or a new view, use the CLI
127
+ (`view add`/`remove`); never edit the derived `index.tsx` to swap a variant.
52
128
 
53
- ## json-render references (consult when authoring element types)
129
+ ## Stack constraints
54
130
 
55
- When adding a view you register new element `type`s in the view module: the zod
56
- catalog entry in `views/<view>/index.tsx` and the component mapping in
57
- `views/<view>/registry.tsx` ('use client'). Consult the vendored references
58
- instead of working from memory:
131
+ - UI components only from `@thorprovider/blocks`.
132
+ - Data only through `@thorprovider/adapters` + `@thorprovider/runtime/commerce`.
133
+ - Never `@thorprovider/medusa-extended`.
134
+ - No custom blocks without explicit user authorization.
59
135
 
60
- - `../json-render-core/` — catalog/schema/spec authoring (the shape of a catalog entry and a spec element)
61
- - `../json-render-react/` — registry mapping and action wiring (`defineRegistry`, `Renderer`, `useStateStore`)
136
+ ## Workflow model
62
137
 
63
- **Selection rule:** choose an **intent by meaning** from the user's description (never a raw variant ordinal), then resolve the intent to its variant via `assets/recipes/archetypes.json`. The resolved variant must be allowed by the recipe for that slot. Choose optional sections the same way. Never invent an intent, variant, or component. If the description is silent, use the archetype slot `default`.
138
+ Read `../storefront-workflow/SKILL.md` for the four phases (bootstrap → compose
139
+ → customize → verify), the ownership classes, and the CLI/agent authority.
64
140
 
65
- ```bash
66
- node scripts/validate.mjs resolve home hero showcase # -> StorefrontHero2
67
- ```
68
-
69
- ## Stack constraints
70
- - UI: only `@thorprovider/blocks`
71
- - Data: only `@thorprovider/adapters` + `@thorprovider/runtime/commerce`
72
- - Never `@thorprovider/medusa-extended`
73
- - No custom blocks without explicit user authorization
74
- - **Two-file view split (RSC):** `index.tsx` is a pure Server Component module (name, elements, zod catalog, route, loader) and imports `registry` from the sibling `registry.tsx` as data. `registry.tsx` is the **`'use client'`** module and is the only place that imports values from `@json-render/react` or `@thorprovider/blocks`. Never add those imports to `index.tsx` — `@json-render/react` calls `createContext` at module scope, which breaks the RSC server graph.
75
- - Every element `type` used in a spec MUST be registered in the view's zod catalog (`index.tsx`) AND its client registry (`registry.tsx`); the shared `catalog.ts` / `registry.tsx` compose both through the barrel and are never hand-edited
76
-
77
- ## Add-view workflow
78
-
79
- 1. Parse `$ARGUMENTS` → view name (+ optional description). Validate name. Confirm `lib/<name>/` exists (else run `npx @thorprovider/storefront create` to scaffold it).
80
- 2. `node scripts/validate.mjs show <view>` — read slots, allowed variants + sections. `node scripts/validate.mjs archetypes` — read slots with their named intents.
81
- 3. Choose an intent per slot by meaning from the description (defaults if silent), resolve each with `node scripts/validate.mjs resolve <view> <slot> <intent>`, choose optional sections the same way, then validate the full composition with `node scripts/validate.mjs validate '<json>'` **before writing anything**. On failure → stop, show the allowed options.
82
- 4. Copy the view template: `assets/templates/views/<view>/` → `lib/<name>/views/<view>/`. Each template is the two-file split: `index.tsx` (server spec) + `registry.tsx` ('use client' component mappings).
83
- 5. Register it in the barrel `lib/<name>/views/index.ts` — ONE import and ONE array entry:
84
- ```ts
85
- import { <view> } from './<view>'
86
- export const views: ViewModule[] = [home, <view>]
87
- ```
88
- 6. Substitute variants: each slot's block is imported under a stable alias (e.g. `ProductOverview1 as ProductOverviewBlock`), shipping the recipe default. Replace **only the imported identifier** with the resolved variant — never the alias, never the props:
89
- ```ts
90
- import { ProductOverview5 as ProductOverviewBlock } from '@thorprovider/blocks'
91
- ```
92
- 7. Include any chosen optional section — fragments live at `assets/templates/views/sections/` as a two-file split:
93
- - `<section>.tsx` (server): exports the section object with `elementType`, the zod `catalog` entry, and `registryModule: './<section>.client'`.
94
- - `<section>.client.tsx` ('use client'): exports `registry: ViewRegistry` with the component mapping.
95
- Import the section object from `<section>` into `index.tsx` and merge its elements/catalog into the view; put the component mapping from `<section>.client` into the view's `registry.tsx` (re-exporting the fragment's registry).
96
- 8. Verify: `yarn typecheck`. Do NOT hand-edit `catalog.ts`, `registry.tsx`, `runtime.ts`, or `spec/index.ts` — they compose whatever the barrel exports.
97
-
98
- ## Modify workflow (`/sf-view`)
99
-
100
- 1. Parse view name + change. Validate name.
101
- 2. Read `lib/<name>/views/<view>/index.tsx` (server spec) and `registry.tsx` (client mappings) fully.
102
- 3. Apply ONLY the change. Preserve ids, `type`, untouched `props`, `$state`, `on` wiring, route/loader, and the barrel entry.
103
- 4. If new blocks are needed, choose intents from `archetypes.json`, resolve to allowed variants, and validate before writing.
104
- 5. Re-run `yarn typecheck`.
105
-
106
- ## Generation headers
107
- Every generated/edited file gets:
108
- ```
109
- // Generated by @thorprovider/storefront (sf-view-gen) from recipes/<view>
110
- ```
141
+ ## Completion criteria
111
142
 
112
- ## Error handling
113
- - Unknown view → error + allowed list, stop.
114
- - Missing `lib/<name>/` → run `npx @thorprovider/storefront create`, stop.
115
- - Undeclared variant/section → reject via `recipes/validate.mjs`, show allowed options.
116
- - Type errors → fix before finishing; never leave broken specs.
143
+ - The manifest reflects the requested composition and `sync` reports no drift.
144
+ - Only user-owned files were hand-edited; every derived file was written by the
145
+ CLI.
146
+ - The CLI command's exit status was checked and reported.
@@ -4,7 +4,7 @@
4
4
  "description": "Archetype (intent) layer. Maps a slot to named intents so selection is by meaning, not by variant ordinal. Each intent resolves to a variant that MUST be allowed by the recipe for that slot.",
5
5
  "slots": {
6
6
  "home.hero": {
7
- "default": "showcase",
7
+ "default": "compact",
8
8
  "intents": {
9
9
  "compact": {
10
10
  "variant": "StorefrontHero1",
@@ -122,6 +122,33 @@
122
122
  }
123
123
  }
124
124
  },
125
+ "account.login": {
126
+ "default": "signIn",
127
+ "intents": {
128
+ "signIn": {
129
+ "variant": "LoginPage1",
130
+ "when": "A full sign-in page with email and password and a link to create an account."
131
+ }
132
+ }
133
+ },
134
+ "account.register": {
135
+ "default": "signUp",
136
+ "intents": {
137
+ "signUp": {
138
+ "variant": "SignUpPage1",
139
+ "when": "A full registration page with name, email, and password and a link to sign in."
140
+ }
141
+ }
142
+ },
143
+ "account.orders": {
144
+ "default": "viewOrders",
145
+ "intents": {
146
+ "viewOrders": {
147
+ "variant": "OrderHistory2",
148
+ "when": "A signed-in shopper's order history with per-order totals and detail on demand."
149
+ }
150
+ }
151
+ },
125
152
  "user-panel.shell": {
126
153
  "default": "standard",
127
154
  "intents": {
@@ -0,0 +1,361 @@
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). user-panel (non-MVP view) is outside this initial catalog scope. 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
+ "category.filter": {
161
+ "default": "CategoryFilter2",
162
+ "variants": {
163
+ "CategoryFilter2": {
164
+ "status": "executable",
165
+ "block": "CategoryFilter",
166
+ "dataShape": "CatalogQuery & CatalogResults",
167
+ "events": [
168
+ "productClick",
169
+ "pageChange",
170
+ "pageSizeChange",
171
+ "sortChange",
172
+ "filterChange",
173
+ "searchChange",
174
+ "clearFilters",
175
+ "repair",
176
+ "retry",
177
+ "removeFilter"
178
+ ],
179
+ "state": "controlled",
180
+ "requirements": ["categoryProducts", "categoryQuery", "facetSupport"]
181
+ },
182
+ "CategoryFilter1": {
183
+ "status": "held",
184
+ "reason": "block requires categories/priceRanges/sortOptions with counts the canonical CardProduct[] listing does not supply; no verified prop bridge exists",
185
+ "block": "CategoryFilter",
186
+ "dataShape": "CategoryFilter1Filters",
187
+ "events": ["filterChange"],
188
+ "state": "uncontrolled",
189
+ "requirements": ["categoryProducts"]
190
+ },
191
+ "CategoryFilter4": {
192
+ "status": "held",
193
+ "reason": "block requires an advanced facets contract (modelTypes/rangeBands/frameSizes/colourways/features) the canonical listing does not supply; no verified prop bridge exists",
194
+ "block": "CategoryFilter",
195
+ "dataShape": "CategoryFilter4Facets",
196
+ "events": ["filterChange"],
197
+ "state": "uncontrolled",
198
+ "requirements": ["categoryProducts"]
199
+ }
200
+ }
201
+ },
202
+ "category.productCard": {
203
+ "default": "ProductCard1",
204
+ "variants": {
205
+ "ProductCard1": {
206
+ "status": "executable",
207
+ "block": "ProductCard",
208
+ "dataShape": "CardProduct[]",
209
+ "events": ["addToCart", "openProduct"],
210
+ "state": "uncontrolled",
211
+ "requirements": ["categoryProducts"]
212
+ },
213
+ "ProductCard2": {
214
+ "status": "held",
215
+ "reason": "block requires a product with a required rating, a shape the canonical CardProduct listing does not supply; no verified prop bridge exists",
216
+ "block": "ProductCard",
217
+ "dataShape": "ProductCard2Product[]",
218
+ "events": [],
219
+ "state": "uncontrolled",
220
+ "requirements": ["categoryProducts"]
221
+ },
222
+ "ProductCard3": {
223
+ "status": "held",
224
+ "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",
225
+ "block": "ProductCard",
226
+ "dataShape": "ProductCard3Product[]",
227
+ "events": ["toggleWishlist", "viewSimilar", "productClick"],
228
+ "state": "controlled",
229
+ "requirements": ["categoryProducts"]
230
+ },
231
+ "ProductCard5": {
232
+ "status": "held",
233
+ "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",
234
+ "block": "ProductCard",
235
+ "dataShape": "ProductCard5Product[]",
236
+ "events": ["toggleWishlist", "addToCart", "quickView", "compare", "export"],
237
+ "state": "controlled",
238
+ "requirements": ["categoryProducts"]
239
+ }
240
+ }
241
+ },
242
+ "checkout.checkout": {
243
+ "default": "CheckoutForm1",
244
+ "variants": {
245
+ "CheckoutForm1": {
246
+ "status": "executable",
247
+ "block": "Checkout",
248
+ "dataShape": "CheckoutForm1FormData",
249
+ "events": ["retryTotals", "selectShippingOption", "applyPromo", "submit"],
250
+ "state": "controlled",
251
+ "requirements": ["checkoutCountries", "checkoutPaymentMethods"]
252
+ },
253
+ "CheckoutForm2": {
254
+ "status": "held",
255
+ "reason": "unproven checkout variant: E1 keeps only the default CheckoutForm1, whose behavior is unchanged; no verified prop bridge exists",
256
+ "block": "Checkout",
257
+ "dataShape": "CheckoutForm2Data",
258
+ "events": ["submit"],
259
+ "state": "controlled",
260
+ "requirements": ["checkoutCountries", "checkoutPaymentMethods"]
261
+ },
262
+ "CheckoutForm3": {
263
+ "status": "held",
264
+ "reason": "unproven checkout variant: E1 keeps only the default CheckoutForm1, whose behavior is unchanged; no verified prop bridge exists",
265
+ "block": "Checkout",
266
+ "dataShape": "CheckoutForm3FormData",
267
+ "events": ["submit"],
268
+ "state": "controlled",
269
+ "requirements": ["checkoutCountries", "checkoutPaymentMethods"]
270
+ }
271
+ }
272
+ },
273
+ "checkout.confirmation": {
274
+ "default": "CheckoutConfirmation",
275
+ "variants": {
276
+ "CheckoutConfirmation": {
277
+ "status": "executable",
278
+ "block": "CheckoutConfirmation",
279
+ "dataShape": "Receipt",
280
+ "events": ["continueShopping"],
281
+ "state": "uncontrolled",
282
+ "requirements": ["lastReceipt"]
283
+ }
284
+ }
285
+ },
286
+ "account.login": {
287
+ "default": "LoginPage1",
288
+ "variants": {
289
+ "LoginPage1": {
290
+ "status": "executable",
291
+ "block": "LoginPage1",
292
+ "dataShape": "LoginPage1FormData & AccountAuthResult",
293
+ "events": ["submit", "createAccount"],
294
+ "state": "controlled",
295
+ "requirements": ["accountCustomer", "accountAuthenticated"]
296
+ },
297
+ "LoginPage2": {
298
+ "status": "held",
299
+ "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",
300
+ "block": "LoginPage2",
301
+ "dataShape": "LoginPage2Credentials & AccountAuthResult",
302
+ "events": ["submit", "forgotPassword", "signUp"],
303
+ "state": "controlled",
304
+ "requirements": ["accountCustomer", "accountAuthenticated"]
305
+ }
306
+ }
307
+ },
308
+ "account.register": {
309
+ "default": "SignUpPage1",
310
+ "variants": {
311
+ "SignUpPage1": {
312
+ "status": "executable",
313
+ "block": "SignUpPage1",
314
+ "dataShape": "SignUpPage1Data & AccountAuthResult",
315
+ "events": ["submit", "signIn"],
316
+ "state": "controlled",
317
+ "requirements": ["accountCustomer", "accountAuthenticated"]
318
+ }
319
+ }
320
+ },
321
+ "account.orders": {
322
+ "default": "OrderHistory2",
323
+ "variants": {
324
+ "OrderHistory2": {
325
+ "status": "executable",
326
+ "block": "OrderHistory2",
327
+ "dataShape": "AccountOrderView[] & OrderHistory2Labels",
328
+ "events": ["viewDetails"],
329
+ "state": "controlled",
330
+ "requirements": ["accountCustomer", "accountAuthenticated"]
331
+ },
332
+ "OrderHistory1": {
333
+ "status": "held",
334
+ "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",
335
+ "block": "OrderHistory1",
336
+ "dataShape": "OrderHistory1Order[]",
337
+ "events": [],
338
+ "state": "controlled",
339
+ "requirements": ["accountCustomer", "accountAuthenticated"]
340
+ }
341
+ }
342
+ }
343
+ },
344
+ "capabilities": {
345
+ "category.search.apply": {
346
+ "requirements": ["adapter.getCatalogProducts(query).text"],
347
+ "verification": "unverified",
348
+ "owner": "storefront-catalog-search-filters"
349
+ },
350
+ "category.filter.apply": {
351
+ "requirements": ["adapter.getProducts(query)"],
352
+ "verification": "unverified",
353
+ "owner": "storefront-catalog-search-filters"
354
+ },
355
+ "category.price.range": {
356
+ "requirements": ["adapter.getAvailableFilters(query).priceRange"],
357
+ "verification": "unverified",
358
+ "owner": "storefront-catalog-search-filters"
359
+ }
360
+ }
361
+ }