@tokenoftrust/cli 1.3.4-rc.3 → 1.3.4-rc.5

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 (41) hide show
  1. package/bin/tot.cjs +44 -16
  2. package/bin/tot.mjs +8 -0
  3. package/package.json +2 -1
  4. package/src/app-scaffold.mjs +84 -0
  5. package/src/banner.mjs +46 -0
  6. package/src/commands/app/dev.mjs +268 -0
  7. package/src/commands/app/index.mjs +35 -0
  8. package/src/commands/app/scaffold.mjs +57 -0
  9. package/src/commands/checkout.mjs +5 -0
  10. package/src/commands/dev.mjs +61 -14
  11. package/src/commands/start.mjs +76 -17
  12. package/src/commands/validate.mjs +4 -0
  13. package/src/dev-logs.mjs +34 -7
  14. package/src/obstacle-beacon.cjs +111 -0
  15. package/src/obstacle.mjs +35 -0
  16. package/src/validate.mjs +77 -8
  17. package/src/vendor/private-apps-devkit.mjs +490 -0
  18. package/template/private-app/.env.example +10 -0
  19. package/template/private-app/Dockerfile +12 -0
  20. package/template/private-app/README.md +45 -0
  21. package/template/private-app/fixtures/order.created.cloudevent.json +36 -0
  22. package/template/private-app/server.js +81 -0
  23. package/template/private-app/tot-app.json +29 -0
  24. package/template/sample-store/content/chrome.html +140 -0
  25. package/template/sample-store/content/chrome.json +86 -0
  26. package/template/sample-store/content/home.html +121 -0
  27. package/template/sample-store/content/home.json +50 -0
  28. package/template/sample-store/content/pages/about.json +10 -0
  29. package/template/sample-store/content/pages/privacy.json +10 -0
  30. package/template/sample-store/content/pages/shipping-returns.json +10 -0
  31. package/template/sample-store/content/pages-html/blogs/news.html +26 -0
  32. package/template/sample-store/content/pages-html/pages/about-us.html +44 -0
  33. package/template/sample-store/content/pages-html/pages/contact-us.html +48 -0
  34. package/template/sample-store/content/pages-html/pages/privacy-policy.html +27 -0
  35. package/template/sample-store/content/pages-html/pages/shipping-returns.html +25 -0
  36. package/template/sample-store/public/logo.svg +6 -0
  37. package/template/sample-store/public/pages/home.css +120 -0
  38. package/template/sample-store/public/pages/mkt.css +185 -0
  39. package/template/sample-store/public/pages/page.css +155 -0
  40. package/template/sample-store/public/themes/sample.css +76 -0
  41. package/template/sample-store/theme.json +38 -0
@@ -0,0 +1,45 @@
1
+ # my-app
2
+
3
+ A Storefront Private App, scaffolded by `tot app scaffold`. See
4
+ [the Private Apps devbook](https://github.com/tokenoftrust/storefront/blob/main/docs/private-apps/devbook/private-apps-devbook.md)
5
+ for the full contract this manifest and receiver implement.
6
+
7
+ ## What's here
8
+
9
+ - **`tot-app.json`** — your app's manifest: identity, scopes, webhook
10
+ subscriptions, widgets. Validated against
11
+ [`tot-app.schema.json`](https://github.com/tokenoftrust/storefront/blob/main/docs/private-apps/contract/tot-app.schema.json).
12
+ Everything here is a PLACEHOLDER — edit `id`, `owner`, `webhooks.endpoint`,
13
+ and `scopes` for your real app before you install it anywhere.
14
+ - **`fixtures/`** — sample CloudEvents (from `docs/private-apps/contract/examples/`)
15
+ you can sign and deliver to your own receiver locally, before you have a
16
+ real install. Add one fixture per topic you subscribe to.
17
+ - **`server.js`** — a minimal Node HTTP receiver for `webhooks.endpoint`. It
18
+ verifies the RFC 9421 HTTP Message Signature + RFC 9530 Content-Digest on
19
+ every inbound request before trusting the body — replace the handler logic,
20
+ keep the verification.
21
+ - **`Dockerfile`** — only relevant if you set `installMode: "hosted"`; ignored
22
+ for `"external"` apps (the default here).
23
+ - **`.env.example`** — copy to `.env`; no real values are included.
24
+
25
+ ## Local loop (no network, no real ToT account needed)
26
+
27
+ ```
28
+ tot app dev validate # lint tot-app.json against the contract
29
+ node server.js & # start the stub receiver
30
+ tot app dev emit order.created # sign a fixture CloudEvent and POST it in
31
+ tot app dev mint # mint a dev widget-launch JWT against an ephemeral key
32
+ ```
33
+
34
+ `tot app dev` generates its own throwaway RS256 keypair per app directory
35
+ (`.tot/dev-keys.json`, gitignored) so signing and verifying works fully
36
+ offline — nothing here is a real ToT-issued credential. Run `tot app dev --help`
37
+ for the full command surface.
38
+
39
+ ## Going live
40
+
41
+ 1. Point `webhooks.endpoint` at your real, publicly reachable HTTPS receiver.
42
+ 2. Get your manifest installed for a real `(tenant, env)` — this issues your
43
+ real client credentials; nothing above touches them.
44
+ 3. Swap the dev-only signature verification for the real gateway's published
45
+ JWKS/signing-key metadata (see the devbook's Signature Verification section).
@@ -0,0 +1,36 @@
1
+ {
2
+ "specversion": "1.0",
3
+ "id": "evt_01J9Z3Q7M2K8V6C4B0N5X7Y2A1",
4
+ "source": "/tenants/acme/storefront",
5
+ "type": "com.tokenoftrust.storefront.order.created",
6
+ "time": "2026-07-21T18:42:07.512Z",
7
+ "datacontenttype": "application/json",
8
+ "dataschemaversion": "1",
9
+ "tenantid": "acme",
10
+ "appinstallid": "inst_01J9Z2R4H5T3W1P8D6F0K9M2C7",
11
+ "retrycount": 0,
12
+ "totrequestid": "req_7f3c9a1e2b4d",
13
+ "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
14
+ "data": {
15
+ "id": "ord_10482",
16
+ "status": "created",
17
+ "currency": "USD",
18
+ "customerHash": "cus_h_9a7f2c4e8b1d6033",
19
+ "subtotal": 84.0,
20
+ "total": 91.35,
21
+ "lineItems": [
22
+ {
23
+ "sku": "VAPE-KIT-STARTER",
24
+ "handle": "starter-kit",
25
+ "quantity": 1,
26
+ "lineTotal": 49.0
27
+ },
28
+ {
29
+ "sku": "PODS-MENTHOL-4PK",
30
+ "handle": "menthol-pods-4pk",
31
+ "quantity": 1,
32
+ "lineTotal": 35.0
33
+ }
34
+ ]
35
+ }
36
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Minimal webhook receiver stub for a Storefront Private App. Verifies the
4
+ * RFC 9421 HTTP Message Signature + RFC 9530 Content-Digest on every inbound
5
+ * request BEFORE trusting the body — replace the "handle the event" bit
6
+ * below, keep the verification.
7
+ *
8
+ * Trusts the public key `tot app dev` wrote to .tot/dev-keys.json (re-read on
9
+ * every request, so it always sees the harness's current key). That file is
10
+ * dev-only scaffolding: your real receiver checks against the platform's
11
+ * published JWKS/signing-key metadata instead — see the devbook's Signature
12
+ * Verification section.
13
+ */
14
+ import { createServer } from "node:http";
15
+ import { existsSync, readFileSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { verifyWebhookSignature } from "./lib/private-apps-devkit.mjs";
18
+
19
+ const PORT = Number(process.env.PORT || 8787);
20
+ const DEV_KEYS_PATH = join(process.cwd(), ".tot", "dev-keys.json");
21
+
22
+ function loadDevPublicKey() {
23
+ if (!existsSync(DEV_KEYS_PATH)) return null;
24
+ const { publicKeyJwk } = JSON.parse(readFileSync(DEV_KEYS_PATH, "utf8"));
25
+ return crypto.subtle.importKey("jwk", publicKeyJwk, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["verify"]);
26
+ }
27
+
28
+ function readBody(req) {
29
+ return new Promise((resolvePromise, reject) => {
30
+ const chunks = [];
31
+ req.on("data", (c) => chunks.push(c));
32
+ req.on("end", () => resolvePromise(Buffer.concat(chunks).toString("utf8")));
33
+ req.on("error", reject);
34
+ });
35
+ }
36
+
37
+ const server = createServer(async (req, res) => {
38
+ const requestId = req.headers["tot-request-id"] || "(none)";
39
+ if (req.method !== "POST") {
40
+ res.writeHead(405).end();
41
+ return;
42
+ }
43
+
44
+ const body = await readBody(req);
45
+ const publicKey = await loadDevPublicKey();
46
+ if (!publicKey) {
47
+ console.error(`[${requestId}] no .tot/dev-keys.json — run \`tot app dev emit\` first`);
48
+ res.writeHead(401, { "content-type": "application/json" }).end(JSON.stringify({ error: "no trusted key on file" }));
49
+ return;
50
+ }
51
+
52
+ const result = await verifyWebhookSignature({
53
+ method: req.method,
54
+ url: `http://${req.headers.host}${req.url}`,
55
+ body,
56
+ headers: {
57
+ "content-digest": req.headers["content-digest"],
58
+ "signature-input": req.headers["signature-input"],
59
+ signature: req.headers["signature"],
60
+ },
61
+ publicKey,
62
+ });
63
+
64
+ if (!result.ok) {
65
+ console.error(`[${requestId}] signature verification FAILED: ${result.code} — ${result.reason}`);
66
+ res.writeHead(401, { "content-type": "application/json" }).end(JSON.stringify(result));
67
+ return;
68
+ }
69
+
70
+ console.log(`[${requestId}] verified delivery (keyid=${result.keyid}, created=${result.created})`);
71
+ // ── Handle the event ────────────────────────────────────────────────────
72
+ const event = JSON.parse(body);
73
+ console.log(`[${requestId}] ${event.type} for tenant ${event.tenantid}`);
74
+ // ─────────────────────────────────────────────────────────────────────────
75
+
76
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
77
+ });
78
+
79
+ server.listen(PORT, () => {
80
+ console.log(`webhook receiver listening on http://localhost:${PORT} (POST here — see tot-app.json webhooks.endpoint)`);
81
+ });
@@ -0,0 +1,29 @@
1
+ {
2
+ "contractVersion": "1",
3
+ "id": "com.example.my-app",
4
+ "name": "My Example App",
5
+ "version": "0.1.0",
6
+ "owner": {
7
+ "name": "Your Company",
8
+ "email": "you@example.com",
9
+ "url": "https://example.com"
10
+ },
11
+ "description": "Describe what your app does in one or two sentences.",
12
+ "installMode": "external",
13
+ "scopes": [
14
+ "orders:read:minimal",
15
+ "orders:webhook"
16
+ ],
17
+ "webhooks": {
18
+ "endpoint": "https://example.com/webhooks/tot",
19
+ "topics": [
20
+ "order.created"
21
+ ],
22
+ "signatureKeyId": "dev-key-1"
23
+ },
24
+ "telemetryMode": "sampled",
25
+ "retention": {
26
+ "eventLogDays": 30,
27
+ "piiRedaction": "default"
28
+ }
29
+ }
@@ -0,0 +1,140 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <link rel="icon" type="image/svg+xml" href="/tenants/sample-store.example/logo.svg">
7
+ <link rel="stylesheet" href="/tenants/sample-store.example/themes/sample.css">
8
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/mkt.css">
9
+ <!--PAGE_HEAD-->
10
+ </head>
11
+ <body>
12
+ <a class="skip-link" href="#main">Skip to main content</a>
13
+
14
+ <!-- Top age/nicotine warning bar -->
15
+ <div class="topbar-warning"><p>WARNING: This product contains nicotine. Nicotine is an addictive chemical.</p></div>
16
+
17
+ <!-- Announcement: scrolling marquee -->
18
+ <div class="announce-marquee" aria-label="Free US shipping on orders over $100">
19
+ <div class="announce-marquee__track" aria-hidden="true">
20
+ <span>FREE U.S. Shipping on Orders Over $100!</span><span>FREE U.S. Shipping on Orders Over $100!</span><span>FREE U.S. Shipping on Orders Over $100!</span><span>FREE U.S. Shipping on Orders Over $100!</span>
21
+ <span>FREE U.S. Shipping on Orders Over $100!</span><span>FREE U.S. Shipping on Orders Over $100!</span><span>FREE U.S. Shipping on Orders Over $100!</span><span>FREE U.S. Shipping on Orders Over $100!</span>
22
+ </div>
23
+ </div>
24
+
25
+ <!-- Utility nav -->
26
+ <div class="utility-nav">
27
+ <div class="container">
28
+ <ul>
29
+ <li><a href="/blogs/news">News</a></li>
30
+ <li><a href="/pages/about-us">About</a></li>
31
+ <li><a href="/pages/contact-us">Contact</a></li>
32
+ </ul>
33
+ <div class="utility-nav__right">
34
+ <span class="sel">English</span>
35
+ <span class="sel">United States (USD $)</span>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <!-- Header -->
41
+ <header class="site-header">
42
+ <div class="container header-row">
43
+ <a class="brand" href="/" aria-label="Sample Vapor Co. home"><img src="/tenants/sample-store.example/logo.svg" alt="Sample Vapor Co." width="240" height="48"></a>
44
+ <form class="header-search" action="/search" method="get" role="search">
45
+ <label class="sr-only" for="hdr-q">Search for anything</label>
46
+ <input id="hdr-q" type="search" name="q" placeholder="Search e-liquids, disposables, hardware" autocomplete="off">
47
+ <button type="submit" aria-label="Search"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg></button>
48
+ </form>
49
+ <div class="header-actions">
50
+ <a href="/account" aria-label="Account"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-7 8-7s8 2.6 8 7"/></svg></a>
51
+ <a class="cart" href="/cart" aria-label="Cart"><svg viewBox="0 0 24 24"><path d="M6 6h15l-1.5 9h-12z"/><circle cx="9" cy="20" r="1.4"/><circle cx="18" cy="20" r="1.4"/><path d="M6 6L5 3H2"/></svg><span class="count">0</span></a>
52
+ <button class="hamburger" id="hamburger" aria-label="Toggle navigation menu" aria-expanded="false" aria-controls="primaryNav"><span></span><span></span><span></span></button>
53
+ </div>
54
+ </div>
55
+ <nav class="primary-nav-bar" aria-label="Primary">
56
+ <div class="container">
57
+ <nav class="primary-nav" id="primaryNav">
58
+ <ul>
59
+ <li><a href="/collections/e-liquid">E-Liquid<span class="caret">▾</span></a></li>
60
+ <li><a href="/collections/salt-nic">Salt Nic</a></li>
61
+ <li><a href="/collections/disposables">Disposables<span class="caret">▾</span></a></li>
62
+ <li><a href="/collections/hardware">Hardware<span class="caret">▾</span></a></li>
63
+ <li><a href="/collections/new-arrivals">New Arrivals</a></li>
64
+ <li><a class="is-flag" href="/collections/clearance">Clearance<span class="new-badge">SALE</span></a></li>
65
+ </ul>
66
+ </nav>
67
+ </div>
68
+ </nav>
69
+ </header>
70
+
71
+ <!--PAGE_BODY-->
72
+
73
+ <!-- Compliance strip -->
74
+ <div class="compliance-strip">
75
+ <div class="container"><p>WARNING: This product contains nicotine. Nicotine is an addictive chemical.</p></div>
76
+ </div>
77
+
78
+ <!-- Footer -->
79
+ <footer class="site-footer">
80
+ <div class="container">
81
+ <div class="footer-grid">
82
+ <div class="footer-brand">
83
+ <img src="/tenants/sample-store.example/logo.svg" alt="Sample Vapor Co." width="240" height="48">
84
+ <p>A fictional demo store for the Token of Trust local dev loop.<br>Not a real merchant — sample content only.</p>
85
+ </div>
86
+ <div class="footer-col">
87
+ <h4>Shop</h4>
88
+ <ul>
89
+ <li><a href="/collections/e-liquid">E-Liquid</a></li>
90
+ <li><a href="/collections/salt-nic">Salt Nic</a></li>
91
+ <li><a href="/collections/disposables">Disposables</a></li>
92
+ <li><a href="/collections/hardware">Hardware</a></li>
93
+ <li><a href="/collections/clearance">Clearance</a></li>
94
+ </ul>
95
+ </div>
96
+ <div class="footer-col">
97
+ <h4>Support</h4>
98
+ <ul>
99
+ <li><a href="/search">Search</a></li>
100
+ <li><a href="/pages/about-us">About Us</a></li>
101
+ <li><a href="/pages/contact-us">Contact Us</a></li>
102
+ <li><a href="/blogs/news">Blog</a></li>
103
+ </ul>
104
+ </div>
105
+ <div class="footer-col">
106
+ <h4>Company</h4>
107
+ <ul>
108
+ <li><a href="/pages/shipping-returns">Shipping &amp; Returns</a></li>
109
+ <li><a href="/pages/privacy-policy">Privacy Policy</a></li>
110
+ </ul>
111
+ <form class="footer-news" action="/api/newsletter" method="post">
112
+ <input type="hidden" name="source" value="footer">
113
+ <label class="sr-only" for="foot-email">Email</label>
114
+ <input id="foot-email" type="email" name="email" placeholder="Email" required>
115
+ <button type="submit" aria-label="Subscribe">→</button>
116
+ </form>
117
+ <div class="pay-badges" aria-label="Accepted payments">
118
+ <span>AMEX</span><span>Discover</span><span>Mastercard</span><span>Visa</span><span>PayPal</span>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ <div class="footer-warning">
123
+ <p><strong>WARNING:</strong> This product contains nicotine. Nicotine is an addictive chemical. Products intended for adults of legal smoking age (21+) only.</p>
124
+ </div>
125
+ <div class="footer-bottom">
126
+ <p>© Sample Vapor Co. — fictional demo store. All rights reserved.</p>
127
+ <p>Must be 21 or older to purchase. Not for sale to minors.</p>
128
+ </div>
129
+ </div>
130
+ </footer>
131
+
132
+ <script>
133
+ (function () {
134
+ var burger = document.getElementById('hamburger');
135
+ var nav = document.getElementById('primaryNav');
136
+ if (burger && nav) burger.addEventListener('click', function () { var o = nav.classList.toggle('open'); burger.setAttribute('aria-expanded', o ? 'true' : 'false'); });
137
+ })();
138
+ </script>
139
+ </body>
140
+ </html>
@@ -0,0 +1,86 @@
1
+ {
2
+ "announcement": "FREE U.S. Shipping on Orders Over $100!",
3
+ "nav": [
4
+ {
5
+ "label": "E-Liquid",
6
+ "href": "/collections/e-liquid",
7
+ "children": [
8
+ {
9
+ "label": "E-Liquid",
10
+ "href": "/collections/e-liquid"
11
+ },
12
+ {
13
+ "label": "Salt Nic",
14
+ "href": "/collections/salt-nic"
15
+ }
16
+ ]
17
+ },
18
+ {
19
+ "label": "Salt Nic",
20
+ "href": "/collections/salt-nic"
21
+ },
22
+ {
23
+ "label": "Disposables",
24
+ "href": "/collections/disposables"
25
+ },
26
+ {
27
+ "label": "About",
28
+ "href": "/about"
29
+ }
30
+ ],
31
+ "footer": {
32
+ "tagline": "A fictional demo store for the Token of Trust local dev loop.",
33
+ "columns": [
34
+ {
35
+ "title": "Shop",
36
+ "links": [
37
+ {
38
+ "label": "E-Liquid",
39
+ "href": "/collections/e-liquid"
40
+ },
41
+ {
42
+ "label": "Salt Nic",
43
+ "href": "/collections/salt-nic"
44
+ },
45
+ {
46
+ "label": "Disposables",
47
+ "href": "/collections/disposables"
48
+ },
49
+ {
50
+ "label": "Search",
51
+ "href": "/search"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "title": "Company",
57
+ "links": [
58
+ {
59
+ "label": "About",
60
+ "href": "/about"
61
+ },
62
+ {
63
+ "label": "Shipping & Returns",
64
+ "href": "/shipping-returns"
65
+ },
66
+ {
67
+ "label": "Privacy",
68
+ "href": "/privacy"
69
+ }
70
+ ]
71
+ }
72
+ ],
73
+ "social": [
74
+ {
75
+ "label": "Newsletter",
76
+ "href": "#newsletter"
77
+ }
78
+ ]
79
+ },
80
+ "disclaimer": "<p><b>WARNING:</b> This product contains nicotine. Nicotine is an addictive chemical. Products intended for adults of legal smoking age (21+) only.</p>\n<p>This is sample demo content for the Token of Trust local dev loop, not a real merchant. Sample Vapor Co. makes no product claims.</p>",
81
+ "disclaimers": {
82
+ "Vape Juice": "<p><b>WARNING:</b> This product contains nicotine. Nicotine is an addictive chemical. Products intended for adults of legal smoking age (21+) only. Sample demo content — not a real product.</p>",
83
+ "Nicotine Salt": "<p><b>WARNING:</b> This product contains nicotine. Nicotine is an addictive chemical. Nicotine salt e-liquid carries a higher nicotine concentration; use only with low-output devices. Products intended for adults 21+ only. Sample demo content — not a real product.</p>",
84
+ "Vape Disposable": "<p><b>WARNING:</b> This product contains nicotine. Nicotine is an addictive chemical. Products intended for adults of legal smoking age (21+) only. Sample demo content — not a real product.</p>"
85
+ }
86
+ }
@@ -0,0 +1,121 @@
1
+ <head>
2
+ <title>Sample Vapor Co. | Local Dev Demo Store</title>
3
+ <meta name="description" content="Sample Vapor Co. is a fictional demo store bundled with the Token of Trust local dev loop. It shows the 21+ age gate and nicotine-warning compliance affordances on non-merchant sample content.">
4
+ <link rel="canonical" href="https://sample-store.example/">
5
+ <meta name="robots" content="noindex,nofollow">
6
+ <meta property="og:type" content="website">
7
+ <meta property="og:site_name" content="Sample Vapor Co.">
8
+ <meta property="og:title" content="Sample Vapor Co. | Local Dev Demo Store">
9
+ <meta property="og:description" content="A fictional demo store showing the 21+ compliance gate and nicotine warnings. Sample content only.">
10
+ <meta property="og:url" content="https://sample-store.example/">
11
+ <link rel="stylesheet" href="/tenants/sample-store.example/pages/home.css">
12
+ </head>
13
+
14
+ <main id="main">
15
+ <!-- 1. HERO — two-up -->
16
+ <section class="hero2" aria-label="Featured">
17
+ <div class="container">
18
+ <a class="promo-tile promo-tile--hero" href="/collections/e-liquid" aria-label="Shop the sample e-liquid collection">
19
+ <div class="promo-tile__copy">
20
+ <span class="eyebrow">Sample content · 21+ only</span>
21
+ <h2>Big flavor, no nonsense.</h2>
22
+ <p>A fictional demo store for the local dev loop.</p>
23
+ <span class="btn btn-primary">Shop E-Liquid</span>
24
+ </div>
25
+ </a>
26
+ <a class="promo-tile" href="/collections/new-arrivals" aria-label="New arrivals just landed"></a>
27
+ </div>
28
+ </section>
29
+
30
+ <!-- 2. Promo cards -->
31
+ <section class="promos" aria-label="Promotions">
32
+ <div class="container">
33
+ <a class="promo-card promo-card--blue" href="/collections/new-arrivals"><h3>New Arrivals</h3><p>Fresh juice &amp; hardware just in</p><span class="link">Shop New Arrivals</span></a>
34
+ <a class="promo-card promo-card--red" href="/collections/clearance"><h3>Clearance</h3><p>Deals while they last</p><span class="link">Shop Clearance</span></a>
35
+ <a class="promo-card promo-card--blue" href="/collections/disposables" style="background:linear-gradient(160deg,#1b3b64,#2f5a8f)"><h3>Disposables</h3><p>Big puff counts, big flavor</p><span class="link">Shop Disposables</span></a>
36
+ </div>
37
+ </section>
38
+
39
+ <!-- 3. Categories (icon cards) -->
40
+ <section class="cats" aria-labelledby="cats-h">
41
+ <div class="container">
42
+ <h2 id="cats-h">Categories</h2>
43
+ <div class="grid">
44
+ <a class="cat-icon-card" href="/collections/e-liquid"><span class="ico"><svg viewBox="0 0 48 48"><path d="M19 8h10v6l4 6v18a4 4 0 0 1-4 4H19a4 4 0 0 1-4-4V20l4-6z"/><path d="M22 30h4"/></svg></span><span>E-Liquid</span></a>
45
+ <a class="cat-icon-card" href="/collections/salt-nic"><span class="ico"><svg viewBox="0 0 48 48"><path d="M20 8h8v5l3 5v17a4 4 0 0 1-4 4h-6a4 4 0 0 1-4-4V18l3-5z"/><path d="M23 28h2"/></svg></span><span>Salt Nic</span></a>
46
+ <a class="cat-icon-card" href="/collections/disposables"><span class="ico"><svg viewBox="0 0 48 48"><rect x="18" y="6" width="12" height="30" rx="3"/><path d="M20 36v4a4 4 0 0 0 8 0v-4"/><path d="M21 12h6"/></svg></span><span>Disposables</span></a>
47
+ <a class="cat-icon-card" href="/collections/hardware"><span class="ico"><svg viewBox="0 0 48 48"><rect x="16" y="6" width="16" height="36" rx="4"/><rect x="21" y="12" width="6" height="8" rx="1"/><path d="M22 28h4"/></svg></span><span>Hardware</span></a>
48
+ <a class="cat-icon-card" href="/collections/clearance"><span class="ico"><svg viewBox="0 0 48 48"><path d="M8 24 24 8l16 16-16 16z"/><circle cx="19" cy="19" r="2.5"/></svg></span><span>Clearance</span></a>
49
+ </div>
50
+ </div>
51
+ </section>
52
+
53
+ <!-- SHELF: Sample E-Liquid -->
54
+ <section class="section shelf" aria-labelledby="shelf-eliquid">
55
+ <div class="container">
56
+ <div class="sec-head sec-head--between">
57
+ <div><span class="eyebrow">Shop</span><h2 id="shelf-eliquid">Sample E-Liquid</h2></div>
58
+ <a class="link-more" href="/collections/e-liquid">View all →</a>
59
+ </div>
60
+ <div class="pcarousel">
61
+ <article class="pcard"><a href="/products/sample-house-blend"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">House Blend — 120mL</h3><p class="pcard__price">$14.99 <span class="pcard__was">$19.99</span></p></a></article>
62
+ <article class="pcard"><a href="/products/sample-citrus"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Citrus Sample — 120mL</h3><p class="pcard__price">$14.99 <span class="pcard__was">$19.99</span></p></a></article>
63
+ <article class="pcard"><a href="/products/sample-berry"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Berry Sample — 120mL</h3><p class="pcard__price">$14.99 <span class="pcard__was">$19.99</span></p></a></article>
64
+ <article class="pcard"><a href="/products/sample-menthol"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Cool Menthol — 120mL</h3><p class="pcard__price">$14.99 <span class="pcard__was">$19.99</span></p></a></article>
65
+ </div>
66
+ </div>
67
+ </section>
68
+
69
+ <!-- SHELF: Sample Salt Nic -->
70
+ <section class="section shelf" aria-labelledby="shelf-salt">
71
+ <div class="container">
72
+ <div class="sec-head sec-head--between">
73
+ <div><span class="eyebrow">Shop</span><h2 id="shelf-salt">Sample Salt Nic</h2></div>
74
+ <a class="link-more" href="/collections/salt-nic">View all →</a>
75
+ </div>
76
+ <div class="pcarousel">
77
+ <article class="pcard"><a href="/products/sample-salt-mango"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Mango Salt — 30mL</h3><p class="pcard__price">$12.99 <span class="pcard__was">$16.99</span></p></a></article>
78
+ <article class="pcard"><a href="/products/sample-salt-grape"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Grape Salt — 30mL</h3><p class="pcard__price">$12.99 <span class="pcard__was">$16.99</span></p></a></article>
79
+ <article class="pcard"><a href="/products/sample-salt-mint"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Mint Salt — 30mL</h3><p class="pcard__price">$12.99 <span class="pcard__was">$16.99</span></p></a></article>
80
+ <article class="pcard"><a href="/products/sample-salt-tobacco"><div class="pcard__media"></div><p class="pcard__vendor">Sample Vapor Co.</p><h3 class="pcard__title">Classic Tobacco Salt — 30mL</h3><p class="pcard__price">$12.99 <span class="pcard__was">$16.99</span></p></a></article>
81
+ </div>
82
+ </div>
83
+ </section>
84
+
85
+ <!-- Featured Hardware -->
86
+ <section class="section section--surface" aria-labelledby="feat-hw-h">
87
+ <div class="container">
88
+ <div class="sec-head"><span class="eyebrow">Featured Hardware</span><h2 id="feat-hw-h">Sample Pod Systems</h2></div>
89
+ <a class="spotlight" href="/collections/hardware">
90
+ <div class="spotlight__media"></div>
91
+ <div class="spotlight__body">
92
+ <p class="pcard__vendor">Sample Vapor Co.</p>
93
+ <h3>Innovation in every draw</h3>
94
+ <p>This is placeholder demo copy for a fictional hardware line. Swap it out in <code>content/home.html</code> to see your edits hot-reload in the local dev loop.</p>
95
+ <span class="btn btn-primary">Shop Hardware</span>
96
+ </div>
97
+ </a>
98
+ </div>
99
+ </section>
100
+
101
+ <!-- Ecigs and Vapes Online — story / SEO -->
102
+ <section class="section section--surface intro" aria-labelledby="story-h">
103
+ <div class="container">
104
+ <span class="eyebrow">Sample Vapor Co.</span>
105
+ <h2 id="story-h">A fictional demo store</h2>
106
+ <p>Sample Vapor Co. is not a real merchant. It ships with the Token of Trust local dev loop as neutral sample content so you can see the renderer, the shared chrome, and the 21+ age-gate / nicotine-warning compliance affordances without any real store's data. Edit these files under <code>content/</code> and save to watch the page reload.</p>
107
+ </div>
108
+ </section>
109
+
110
+ <!-- Blog -->
111
+ <section class="section" aria-labelledby="blog-h">
112
+ <div class="container">
113
+ <div class="sec-head sec-head--between"><div><span class="eyebrow">News</span><h2 id="blog-h">From the Blog</h2></div><a class="link-more" href="/blogs/news">Read the blog →</a></div>
114
+ <div class="bloggrid">
115
+ <a class="blogcard" href="/blogs/news"><div class="blogcard__media"></div><h3>Sample blog post one</h3><span class="link-more">Read more →</span></a>
116
+ <a class="blogcard" href="/blogs/news"><div class="blogcard__media"></div><h3>Sample blog post two</h3><span class="link-more">Read more →</span></a>
117
+ <a class="blogcard" href="/blogs/news"><div class="blogcard__media"></div><h3>Sample blog post three</h3><span class="link-more">Read more →</span></a>
118
+ </div>
119
+ </div>
120
+ </section>
121
+ </main>
@@ -0,0 +1,50 @@
1
+ {
2
+ "seoTitle": "Sample Vapor Co. — Local Dev Demo Store",
3
+ "seoDescription": "A fictional demo store bundled with the Token of Trust local dev loop. Shows the 21+ age gate and nicotine warnings on neutral sample content. 21+ only.",
4
+ "blocks": [
5
+ {
6
+ "component": "hero",
7
+ "eyebrow": "Sample content · 21+ only",
8
+ "headline": "Big flavor,",
9
+ "headlineAccent": "no nonsense.",
10
+ "subhead": "A fictional demo store for the local dev loop. Edit content/ and save to see this page reload. 21+ only.",
11
+ "ctaLabel": "Shop e-liquids",
12
+ "ctaHref": "/collections/e-liquid",
13
+ "featureCollection": "e-liquid"
14
+ },
15
+ {
16
+ "component": "featured_collections",
17
+ "title": "Start here",
18
+ "handles": ["e-liquid", "salt-nic", "disposables"]
19
+ },
20
+ {
21
+ "component": "featured_products",
22
+ "title": "Crowd favorites",
23
+ "source": "featured",
24
+ "limit": 4
25
+ },
26
+ {
27
+ "component": "editorial",
28
+ "eyebrow": "About this demo",
29
+ "title": "Sample content only",
30
+ "body_html": "<p>Sample Vapor Co. is not a real merchant. It ships with the local dev loop as neutral placeholder content so you can see the renderer and the 21+ compliance affordances.</p><p>Edit these files and save — the page hot-reloads.</p>",
31
+ "imageSeed": "sample-store-make",
32
+ "align": "right",
33
+ "ctaLabel": "About us",
34
+ "ctaHref": "/about"
35
+ },
36
+ {
37
+ "component": "featured_products",
38
+ "title": "Just landed",
39
+ "source": "newest",
40
+ "limit": 4
41
+ },
42
+ {
43
+ "component": "newsletter",
44
+ "title": "New drops & restock alerts",
45
+ "body": "Sample newsletter block — wire it to your own list when you build a real store.",
46
+ "placeholder": "you@example.com",
47
+ "ctaLabel": "Keep me posted"
48
+ }
49
+ ]
50
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "slug": "about",
3
+ "title": "About Sample Vapor Co.",
4
+ "seoTitle": "About | Sample Vapor Co.",
5
+ "seoDescription": "Sample Vapor Co. is a fictional demo store bundled with the Token of Trust local dev loop.",
6
+ "eyebrow": "Sample content",
7
+ "intro": "Sample Vapor Co. is a fictional demo store — not a real merchant.",
8
+ "isPolicy": false,
9
+ "body_html": "<p>Sample Vapor Co. is a fictional store that ships with the Token of Trust local dev loop. It exists so you can see the renderer, the shared chrome, and the 21+ age-gate and nicotine-warning compliance affordances on neutral sample content — without any real merchant's data.</p><h2>Edit and reload</h2><p>Every file under <code>content/</code> is yours to change. Edit this page's copy, save, and watch the local dev server hot-reload it.</p><h2>Build a real store</h2><p>When you're ready, check out your own tenant with the Token of Trust CLI and this sample is replaced by your real content.</p>"
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "slug": "privacy",
3
+ "title": "Privacy Policy",
4
+ "seoTitle": "Privacy Policy | Sample Vapor Co.",
5
+ "seoDescription": "Sample privacy policy for the fictional Sample Vapor Co. demo store.",
6
+ "eyebrow": "Sample content",
7
+ "intro": "This is placeholder policy copy for a fictional demo store.",
8
+ "isPolicy": true,
9
+ "body_html": "<p><em>Sample Vapor Co. is a fictional demo store bundled with the Token of Trust local dev loop. The text below is neutral placeholder copy — it is not a real privacy policy and does not describe any real data practices.</em></p><h2>Information we collect</h2><p>A real store's privacy policy would describe the information it collects, such as contact and order details. Replace this copy with your own.</p><h2>How we use information</h2><p>Describe here how your store uses the information it collects, and any third parties involved.</p><h2>Your choices</h2><p>Describe here the choices customers have about their information. Edit this file under <code>content/pages/</code> and save to see it reload.</p>"
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "slug": "shipping-returns",
3
+ "title": "Shipping & Returns",
4
+ "seoTitle": "Shipping & Returns | Sample Vapor Co.",
5
+ "seoDescription": "Sample shipping and returns copy for the fictional Sample Vapor Co. demo store.",
6
+ "eyebrow": "Sample content",
7
+ "intro": "Placeholder shipping & returns copy for a fictional demo store.",
8
+ "isPolicy": true,
9
+ "body_html": "<p><em>Sample Vapor Co. is a fictional demo store. The text below is neutral placeholder copy, not a real shipping policy.</em></p><h2>Shipping</h2><p>Age-restricted products require an adult signature (21+) on delivery. Describe your real shipping methods and timelines here.</p><h2>Returns</h2><p>Describe your return policy here. Consumable nicotine products are commonly non-returnable — set your own terms.</p>"
10
+ }