@tanqory/dev-mcp 0.0.0-stage → 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +94 -2
- package/bin/tanqory-dev-mcp.mjs +16 -0
- package/bundled/examples/blank/config/settings.json +3 -0
- package/bundled/examples/blank/locales/en.json +22 -0
- package/bundled/examples/blank/locales/th.json +22 -0
- package/bundled/examples/blank/regions/footer.json +35 -0
- package/bundled/examples/blank/regions/header.json +13 -0
- package/bundled/examples/blank/src/bundle.ts +15 -0
- package/bundled/examples/blank/src/components/parts.tsx +32 -0
- package/bundled/examples/blank/src/entry.client.tsx +6 -0
- package/bundled/examples/blank/src/entry.server.tsx +13 -0
- package/bundled/examples/blank/src/sections/chrome.tsx +132 -0
- package/bundled/examples/blank/src/sections/commerce.tsx +242 -0
- package/bundled/examples/blank/src/sections/content.tsx +111 -0
- package/bundled/examples/blank/src/sections/editorial.tsx +107 -0
- package/bundled/examples/blank/src/settings.ts +15 -0
- package/bundled/examples/blank/src/styles/theme.css +195 -0
- package/bundled/examples/blank/src/templates.ts +14 -0
- package/bundled/examples/blank/src/theme.tsx +76 -0
- package/bundled/examples/blank/template.json +24 -0
- package/bundled/examples/blank/templates/404.json +14 -0
- package/bundled/examples/blank/templates/account.json +14 -0
- package/bundled/examples/blank/templates/article.json +14 -0
- package/bundled/examples/blank/templates/blog.json +14 -0
- package/bundled/examples/blank/templates/cart.json +14 -0
- package/bundled/examples/blank/templates/collection.json +14 -0
- package/bundled/examples/blank/templates/index.json +36 -0
- package/bundled/examples/blank/templates/page.contact.json +18 -0
- package/bundled/examples/blank/templates/page.json +14 -0
- package/bundled/examples/blank/templates/product.json +18 -0
- package/bundled/examples/blank/templates/search.json +14 -0
- package/bundled/examples/blank/tests/theme.test.ts +49 -0
- package/bundled/theme-abi/contracts/render-context.v2.d.ts +849 -0
- package/bundled/theme-abi/contracts/theme-abi-v2.md +1833 -0
- package/bundled/theme-abi/generated/capabilities.json +80 -0
- package/bundled/theme-abi/generated/field-types.json +869 -0
- package/bundled/theme-abi/generated/limits.json +173 -0
- package/bundled/theme-abi/schemas/conformance-report.v2.schema.json +482 -0
- package/bundled/theme-abi/schemas/theme-artifact.v1.schema.json +230 -0
- package/bundled/theme-abi/schemas/theme-manifest.v2.schema.json +254 -0
- package/bundled/theme-conformance/docs/storefront-tier.md +221 -0
- package/bundled/theme-conformance/schema/conformance-report.v2.schema.json +482 -0
- package/bundled/theme-conformance/schema/storefront-report.v1.schema.json +110 -0
- package/bundled/theme-conformance/schema/x-tanqory-ai.v0.schema.json +70 -0
- package/bundled/theme-kit/docs/headless-api.md +220 -0
- package/bundled/theme-kit/generated/kit-strings.json +180 -0
- package/bundled/theme-kit/types/headless/index.d.ts +36 -0
- package/bundled/theme-kit/types/headless/theme.d.ts +99 -0
- package/bundled/theme-kit/types/sdk/schema.d.ts +197 -0
- package/bundled/versions.json +8 -0
- package/package.json +53 -3
- package/src/bm25.mjs +69 -0
- package/src/browser.mjs +123 -0
- package/src/cli.mjs +127 -0
- package/src/devserver.mjs +82 -0
- package/src/docs.mjs +329 -0
- package/src/index.mjs +33 -0
- package/src/kit.mjs +150 -0
- package/src/manifest.mjs +82 -0
- package/src/prompts.mjs +97 -0
- package/src/scaffold.mjs +471 -0
- package/src/schemas.mjs +127 -0
- package/src/server.mjs +595 -0
- package/src/theme.mjs +91 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Storefront-ready tier and budgets tier
|
|
2
|
+
|
|
3
|
+
`theme-conformance run <theme-dir> --tier storefront` checks that a theme **works like a full storefront**:
|
|
4
|
+
a shopper can pick a variant, add to the cart, change and remove lines, reach checkout, sort, filter, page,
|
|
5
|
+
search, switch market and language, use a keyboard, and the merchant can live-edit it in the editor.
|
|
6
|
+
`--tier budgets` checks size budgets and a lab approximation of the mobile performance and accessibility
|
|
7
|
+
scores. `--tier security` (the default) is the nine ABI suites and is unchanged. `--tier all` runs all three.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
theme-conformance run ./dist --tier storefront # → ./storefront-report.json
|
|
11
|
+
theme-conformance run ./dist --tier all --evidence ./evidence --storefront-out ./reports/storefront.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The storefront tier is **theme-agnostic**. It never reads a class name or the markup of a particular
|
|
15
|
+
theme. It relies only on:
|
|
16
|
+
|
|
17
|
+
1. the ABI contract (`theme-abi-v2.md`): route kinds, `ctx.data`, `/_tq/data/*`, `/_tq/actions/*`,
|
|
18
|
+
the CSRF meta tag, `data-tq-form`, `data-tq-slot="account"`, `data-tq-node`, `?variant=`, `?country=`,
|
|
19
|
+
`?locale=`, the editor's canvas protocol;
|
|
20
|
+
2. the theme's `manifest.json` (routes, components, presets, capabilities, forms);
|
|
21
|
+
3. semantic HTML and ARIA (roles, accessible names, `aria-expanded`, `disabled`);
|
|
22
|
+
4. the theme's **own locale strings** (`locales/<lang>.json`), found by key patterns (§4);
|
|
23
|
+
5. optional `data-tq-*` hooks (§5) — a theme that emits them is found exactly; a theme that does not is
|
|
24
|
+
found through 1–4.
|
|
25
|
+
|
|
26
|
+
## 1. How it runs
|
|
27
|
+
|
|
28
|
+
- The theme's `server/entry.mjs` runs under **workerd** with the conformance harness shim, in storefront mode
|
|
29
|
+
(`job.storefront: true`): data answers depend on their arguments (sort, filters, cursors, `page`, search
|
|
30
|
+
query) and cart actions keep real state (versions, conflicts, `checkoutUrl`). The nine security suites
|
|
31
|
+
never use this mode; their behaviour and their report are unchanged.
|
|
32
|
+
- A **gateway** in the runner plays the platform edge for headless Chromium (one browser): route
|
|
33
|
+
resolution (§2.1), templates (`templateSuffix` → `routes[kind]` → platform minimal page), market and
|
|
34
|
+
language from `?country=` / `?locale=` remembered for the session, the cart cookie, the native form POST
|
|
35
|
+
decision order (§1), `/_tq/data/*`, `/_tq/actions/*` (the `x-tq-csrf` header is required), `/_tq/csrf`,
|
|
36
|
+
the CSRF meta tag and hidden form fields, the platform CSP, `/checkout*` interception (the hand-off URL
|
|
37
|
+
is recorded), and the preview channel (`/_tq/editor-bridge.js`, `?tq_section_preview=`).
|
|
38
|
+
- Nothing leaves the machine: DNS and every connection are locked down (as suite 5), and every request is
|
|
39
|
+
answered by the gateway.
|
|
40
|
+
- **Fixture store** (`tq-storefront-fixture/1`, generated by `src/storefront/fixture.ts`): 60 products in
|
|
41
|
+
`all` (more than one page of 50), collection `linen`, two vendors (`Atelier North`, `Field & Co`), a hero
|
|
42
|
+
product `linen-shirt` with option `Colour` = Ivory 37.50 (in stock), Charcoal 52.25 (3 left), Moss 44.75
|
|
43
|
+
(sold out), each with its own image; markets `m_us` USD (default), `m_th` THB (2 decimals), `m_jp` JPY
|
|
44
|
+
(0 decimals); languages `en` (default) and `th`; page `about`, blog `journal` with two articles, two
|
|
45
|
+
policies, menus `main-menu` and `footer`; consent unknown (a banner is required) for consent checks.
|
|
46
|
+
- Viewports: desktop 1280×900, mobile 390×844 (DPR 2, touch).
|
|
47
|
+
|
|
48
|
+
## 2. Checks
|
|
49
|
+
|
|
50
|
+
Status per check: `pass`, `fail`, `warn` (reported, never fails the tier), `not_applicable`, `incomplete`
|
|
51
|
+
(the runner could not perform it; fails the tier). Every non-pass check carries a `fixHint`.
|
|
52
|
+
|
|
53
|
+
| Group | Check id | Passes when |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| templates | `templates:home` `…:collection` `…:product` `…:search` `…:cart` `…:page` `…:blog` `…:article` | the theme maps the route kind and renders `200 text/html` with the fixture |
|
|
56
|
+
| templates | `templates:404` | `routes.not_found` renders with status 404 |
|
|
57
|
+
| templates | `templates:404-missing-resource` | `/products/no-such-product` answers 404 |
|
|
58
|
+
| templates | `templates:password` | always `not_applicable`: the password gate is platform-owned in ABI v2 |
|
|
59
|
+
| templates | `templates:account-slot` | `/account` holds `<iframe data-tq-slot="account" src="<data.customer.accountUrl()>" sandbox="allow-scripts allow-forms allow-same-origin">` (server HTML or after hydration), or the theme has no `routes.customer` (the platform shell holds it) |
|
|
60
|
+
| seo | `seo:title`, `seo:meta-description` | every page has a `<title>`; home/product/collection/page/article have a meta description |
|
|
61
|
+
| seo | `seo:canonical` | exactly one canonical link, equal to `ctx.route.canonical` (no query) — also with `?variant=` / `?sort=` |
|
|
62
|
+
| seo | `seo:hreflang` | home, product and collection link every published language (`<link rel="alternate" hreflang>` with absolute same-origin hrefs) |
|
|
63
|
+
| seo | `seo:product-jsonld` | exactly one Product JSON-LD, valid JSON, `name`, offers with a real variant price, `priceCurrency` of the market, a schema.org availability URL |
|
|
64
|
+
| seo | `seo:sitemap-robots` | no custom route claims `/sitemap*.xml` or `/robots.txt` |
|
|
65
|
+
| product | `product:variant-url-ssr` | `?variant=<Charcoal id>` shows Charcoal's price in the server HTML (JS off) |
|
|
66
|
+
| product | `product:variant-price` | choosing Charcoal shows its price in the product section |
|
|
67
|
+
| product | `product:variant-image` | after choosing Charcoal the largest visible product image is Charcoal's |
|
|
68
|
+
| product | `product:variant-stock` | choosing the sold-out Moss shows a sold-out state (sold-out string, disabled add) or the option itself is disabled |
|
|
69
|
+
| product | `product:oos-disables-add` | with `?variant=<Moss id>` the add control is disabled/absent and sends no cart action |
|
|
70
|
+
| cart | `cart:add` | the add control adds the selected variant (browser `cart.create`/`cart.addLines`, or a declared form post that calls `ctx.actions.cart`) |
|
|
71
|
+
| cart | `cart:totals` | text that appears after the add (a drawer) or `/cart` shows the line title and the cart total |
|
|
72
|
+
| cart | `cart:quantity` | setting the quantity to 2 on `/cart` updates the cart and shows the new total |
|
|
73
|
+
| cart | `cart:checkout-handoff` | the checkout control loads exactly `Cart.checkoutUrl` |
|
|
74
|
+
| cart | `cart:remove` | the remove control empties the cart and the page no longer shows the line |
|
|
75
|
+
| collection | `collection:sort` | some sort choice yields a product list that is correctly sorted (price, title, date or best-selling, either direction) and differs from the default |
|
|
76
|
+
| collection | `collection:filter` | choosing vendor `Field & Co` narrows the list to that vendor (facets behind ARIA disclosures are opened) |
|
|
77
|
+
| collection | `collection:pagination` | products beyond the first page are reachable (`?page=2` link, `rel=next`, load-more, infinite scroll), or all 60 are on the first page |
|
|
78
|
+
| search | `search:results` | `/search?q=linen` links the three linen products |
|
|
79
|
+
| search | `search:predictive` | `not_applicable` unless `capabilities.browser` has `data:search`; then typing `lin` makes a `catalog.predictive`/`catalog.search` call and a "Linen Shirt" option/link appears |
|
|
80
|
+
| i18n | `i18n:locales` | `locales/en.json` and `locales/th.json` exist |
|
|
81
|
+
| i18n | `i18n:keys` | (warn) every en key exists in th |
|
|
82
|
+
| i18n | `i18n:hardcoded-text` | no visible string is identical in en and th unless it is store data, merchant content (the theme's defaults/presets), a number/price/punctuation, a language/country name, or identical in both locale files |
|
|
83
|
+
| i18n | `i18n:html-lang` | `<html lang>` follows `?locale=` |
|
|
84
|
+
| i18n | `i18n:currency` | the hero price shows in USD (2 decimals), THB (2) and JPY (0 decimals) with a currency mark |
|
|
85
|
+
| i18n | `i18n:market-selector` | from the UI the shopper reaches `?country=JP` and prices switch to JPY |
|
|
86
|
+
| a11y | `a11y:axe` | axe-core (wcag2a/aa, wcag21a/aa, wcag22aa) reports no serious/critical violation on home, product, collection, cart (with a line), search, 404 — desktop and mobile |
|
|
87
|
+
| a11y | `a11y:keyboard-desktop` `a11y:keyboard-mobile` | Tab from the top focuses the cart control and the menu within 80 presses |
|
|
88
|
+
| a11y | `a11y:touch-targets` | on mobile every standalone interactive element's real hit area (hit-tested, so stretched links count) covers 44×44 CSS px; links inside running text and targets with a large-enough equivalent link to the same href are exempt |
|
|
89
|
+
| editor | `editor:data-tq-node` | every section of the page document has a `data-tq-node` element in the live DOM |
|
|
90
|
+
| editor | `editor:live-edit` | framed by an editor page on the editor origin, the platform bridge forwards `tanqory-preview-update-section` (then `tq:set-content`) and the section shows the new text without a reload (rendered text, compared case-insensitively, so CSS `text-transform` does not matter) |
|
|
91
|
+
| editor | `editor:section-preview` | every addable section (role ≠ block, with `presets[0]`) renders with height > 0 under `?tq_section_preview=<type>` (on home, or on a page whose template ships it) |
|
|
92
|
+
| editor | `editor:preview-hides-consent` | the consent banner shown on a live first visit does not show on the preview channel |
|
|
93
|
+
| consent | `consent:banner` | (warn) a consent banner shows when `consent.get` answers `bannerRequired: true` |
|
|
94
|
+
| ai | `ai:profile` | (warn) `manifest["x-tanqory-ai"]` is declared and valid (§7) |
|
|
95
|
+
|
|
96
|
+
## 3. Budgets tier
|
|
97
|
+
|
|
98
|
+
| Check id | Measures | Budget |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `budgets:client-initial` | gzip -9 of the client manifest entry + its `imports` + `css` (the same set suite 4 measures as `clientInitialGzipBytes`); JS and CSS reported separately | ABI §8 `clientInitialGzipBytes.budget` (256,000) |
|
|
101
|
+
| `budgets:ssr-entry` | gzip of `server/entry.mjs` | `ssrEntryGzipBytes.budget` |
|
|
102
|
+
| (measurement) `clientInitialTransferredGzipBytes` | script/style the browser fetched before the home page settled | reported |
|
|
103
|
+
| `budgets:performance` | lab performance score, mobile, min over home and product | ≥ 85 |
|
|
104
|
+
| `budgets:accessibility` | lab accessibility score, mobile, min over home and product | ≥ 95 |
|
|
105
|
+
|
|
106
|
+
**What is approximated (exactly).** Lighthouse is not run: its own navigation would bypass the gateway
|
|
107
|
+
that answers every request in-process. The runner measures in its own Chromium instead:
|
|
108
|
+
- mobile 390×844 DPR 2; CPU slowed 4× (`Emulation.setCPUThrottlingRate`); every response delayed by
|
|
109
|
+
150 ms + (bytes on the wire × 8 / 1638.4 kbps) by the gateway, text counted gzipped (Lighthouse's
|
|
110
|
+
"slow 4G" figures applied per response, not a simulated connection pool);
|
|
111
|
+
- FCP, LCP and CLS from `PerformanceObserver`; TBT = Σ(long task − 50 ms) after FCP over load + 3 s;
|
|
112
|
+
- **Speed Index is not measured** (no filmstrip); it is taken as (FCP + LCP) / 2;
|
|
113
|
+
- the score uses Lighthouse's mobile log-normal curves and weights (FCP 10, SI 10, LCP 25, TBT 30, CLS 25);
|
|
114
|
+
- the accessibility score is the share of applicable axe-core rules that pass, all rules weighted equally
|
|
115
|
+
(Lighthouse weights its audits individually).
|
|
116
|
+
|
|
117
|
+
Both scores are labelled `lab-approximation` in the report and are not Lighthouse scores.
|
|
118
|
+
|
|
119
|
+
## 4. Locale-key patterns (the semantic fallback)
|
|
120
|
+
|
|
121
|
+
When no hook is present, a control is a button/link (by ARIA role) whose accessible name equals one of the
|
|
122
|
+
theme's own `locales/en.json` values whose key matches the pattern (last key segment or whole key,
|
|
123
|
+
case-insensitive; `{placeholder}` matches anything):
|
|
124
|
+
|
|
125
|
+
| Control | Key pattern |
|
|
126
|
+
|---|---|
|
|
127
|
+
| add to cart | `add[._-]?to[._-]?(cart\|bag\|basket)`, `addtocart` |
|
|
128
|
+
| sold out | `sold[._-]?out`, `out[._-]?of[._-]?stock`, `unavailable` |
|
|
129
|
+
| checkout | `check[._-]?out`, `proceed[._-]?to[._-]?checkout` |
|
|
130
|
+
| remove line | `remove`, `delete`, `remove[._-]?(item\|line\|product)` |
|
|
131
|
+
| increase quantity | `increase`, `increment`, `plus`, `increase[._-]?quantity` |
|
|
132
|
+
| cart control | `cart`, `bag`, `basket`, `cart[._-]?count`, `open[._-]?cart`, `view[._-]?cart` |
|
|
133
|
+
| menu | `menu`, `open[._-]?menu`, `main[._-]?menu`, `navigation` |
|
|
134
|
+
| search | `search`, `open[._-]?search` |
|
|
135
|
+
| sort | `sort`, `sort[._-]?by` |
|
|
136
|
+
| filter | `filter(s)`, `show[._-]?filters` |
|
|
137
|
+
| apply / update | `apply`, `show[._-]?results`, `done`, `update`, `save`, `confirm` |
|
|
138
|
+
| next page | `next`, `next[._-]?page`, `load[._-]?more`, `show[._-]?more` |
|
|
139
|
+
| country / market | `country`, `country[._-]?region`, `region`, `market`, `currency`, `region[._-]?language` |
|
|
140
|
+
| empty cart | `cart[._-]?empty`, `empty`, `your[._-]?cart[._-]?is[._-]?empty` |
|
|
141
|
+
| consent | accept: `accept`, `accept[._-]?all`, `allow`, `agree`; reject: `reject`, `decline`, `necessary[._-]?only` |
|
|
142
|
+
|
|
143
|
+
Platform semantics used without any key: a link to `/cart`, links inside `<nav>`, `a[href*="country="]`,
|
|
144
|
+
`select[name="country"]`, `a[href*="sort="]`, `a[rel~=next]`, `a[href*="page=2"]`, `input[type=search]`,
|
|
145
|
+
`input[name=q]`, ARIA `searchbox`/`combobox`/`spinbutton`/`radio`/`option`, `aria-expanded="false"`
|
|
146
|
+
disclosures and `<details>`, a link whose href is `Cart.checkoutUrl`, links to `/products/<handle>`.
|
|
147
|
+
|
|
148
|
+
## 5. Hooks — `tq-storefront-hooks/1`
|
|
149
|
+
|
|
150
|
+
All optional, all plain data attributes, no styling meaning. Each costs one attribute on an element the
|
|
151
|
+
theme already renders. A theme that emits a hook is found by it first; the hook does not change what a
|
|
152
|
+
check requires.
|
|
153
|
+
|
|
154
|
+
| Hook | On | Meaning |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `data-tq-action="cart:add"` | the add-to-cart `<button>` (or `<input type=submit>`) | adds the selected variant |
|
|
157
|
+
| `data-tq-action="cart:checkout"` | the checkout link/button | hands off to `Cart.checkoutUrl` |
|
|
158
|
+
| `data-tq-action="cart:remove"` | a line's remove control | removes that line |
|
|
159
|
+
| `data-tq-action="cart:increment"` | a line's "+" control | quantity + 1 |
|
|
160
|
+
| `data-tq-action="cart:open"` | the header cart link/button | opens the cart (drawer or page) |
|
|
161
|
+
| `data-tq-action="menu:open"` | the menu button (mobile) | opens the main menu |
|
|
162
|
+
| `data-tq-quantity` | a line's quantity `<input>`/`<select>` | the line quantity |
|
|
163
|
+
| `data-tq-price` | the element showing the selected variant's price | updated on variant change |
|
|
164
|
+
| `data-tq-variant-picker` | the container of the option controls | scope for option values |
|
|
165
|
+
| `data-tq-sort` | the sort `<select>`, a container holding it, or the container of sort links | sort choices |
|
|
166
|
+
| `data-tq-filter` | the container of facet controls | facet values |
|
|
167
|
+
| `data-tq-pagination` | the container of the next-page / load-more control | paging |
|
|
168
|
+
| `data-tq-localization` | the container of the market/language selector | `?country=` / `?locale=` |
|
|
169
|
+
| `data-tq-search` | the search `<input>` | predictive search |
|
|
170
|
+
| `data-tq-consent-banner` | the consent banner root | shown when `bannerRequired` |
|
|
171
|
+
|
|
172
|
+
The hooks are versioned by `hooksVersion` in the report. A new hook is additive (same version); changing
|
|
173
|
+
the meaning of one is a new version.
|
|
174
|
+
|
|
175
|
+
## 6. The live-edit protocol (what the check sends)
|
|
176
|
+
|
|
177
|
+
The check uses the platform's own canvas bridge (vendored byte-for-byte from storefront-edge
|
|
178
|
+
`core/editor-bridge.client.mjs`, `src/storefront/vendor/`), injected on the preview channel exactly as the
|
|
179
|
+
edge does. The bridge hands each editor message to the page as a window `CustomEvent('tq:canvas',
|
|
180
|
+
{detail: message})`:
|
|
181
|
+
|
|
182
|
+
- `{type: 'tanqory-preview-update-section', sectionId, settings, blocks?, order?}` — the check sends this
|
|
183
|
+
first, with one text setting of a home section set to a new value;
|
|
184
|
+
- `{type: 'tq:set-content', doc: [{id, type, settings}]}` — sent if the first did not apply.
|
|
185
|
+
|
|
186
|
+
A theme applies the message to the section in place and announces what it applies with
|
|
187
|
+
`window.dispatchEvent(new CustomEvent('tq:canvas-live', {detail: {types: [...]}}))` (again whenever it
|
|
188
|
+
receives `tq:canvas-bridge`). The bridge reports the announced types to the editor in `tq:ready.live`;
|
|
189
|
+
the check records them.
|
|
190
|
+
|
|
191
|
+
## 7. `x-tanqory-ai` profile (draft v0, warn only)
|
|
192
|
+
|
|
193
|
+
`manifest["x-tanqory-ai"]` names which of the theme's own sections and settings play the roles Tanqory AI
|
|
194
|
+
fills. Schema: `schema/x-tanqory-ai.v0.schema.json`. Draft; the AI-compat work owns the final shape.
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
"x-tanqory-ai": {
|
|
198
|
+
"version": 0,
|
|
199
|
+
"roles": {
|
|
200
|
+
"hero": [{ "component": "hero", "heading": "heading", "subheading": "subheading", "image": "backgroundImage", "button": "buttonLabel", "link": "buttonLink" }],
|
|
201
|
+
"headerMenu": { "component": "header", "setting": "menu" },
|
|
202
|
+
"footerMenus": [{ "component": "footer", "block": "footer-menu", "setting": "menu" }],
|
|
203
|
+
"footerBrand": { "component": "footer", "block": "footer-brand" }
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Beyond the schema, every `component`/`block` must exist in `manifest.components` and every setting it names
|
|
209
|
+
must exist with a matching type (`menu` for menus; `collection`/`product` for `productGrid.setting`; text
|
|
210
|
+
types for headings; `image` for images).
|
|
211
|
+
|
|
212
|
+
## 8. Report
|
|
213
|
+
|
|
214
|
+
`storefront-report.json`, format `tanqory-storefront-report/1`, schema
|
|
215
|
+
`schema/storefront-report.v1.schema.json`. Each check: `id`, `tier`, `group`, `title`, `status`, and when
|
|
216
|
+
relevant `message`, `route`, `locale`, `viewport`, `evidence` (`selector`, `screenshot` relative to the
|
|
217
|
+
`--evidence` directory, `url`, `details`) and `fixHint`. `measurements` hold the budgets. The security
|
|
218
|
+
tier's `conformance-report.json` (format 2) is not changed by this tier.
|
|
219
|
+
|
|
220
|
+
The default output is `./storefront-report.json` in the working directory — never inside the theme
|
|
221
|
+
directory, whose files are digested.
|