@blamejs/blamejs-shop 0.0.49 → 0.0.50

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/CHANGELOG.md CHANGED
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.0.x
10
10
 
11
+ - v0.0.50 (2026-05-22) — **OpenGraph + Twitter Card meta tags — shared links unfurl with proper previews.** Pages rendered without `og:*` or `twitter:*` tags. Sharing the live URL in Slack / iMessage / Twitter / Discord landed a bare URL with no preview, no image, no description. This release wires OpenGraph + Twitter Card meta tags into the LAYOUT head with sensible site-level defaults (brand logo + shop-level lede). The PDP overrides them with product-specific values — `og:title` is the product title, `og:description` is the catalog `description`, `og:image` is the first attached media URL — so a product share renders with the actual hero image + product copy. **Added:** *OpenGraph + Twitter Card tags in `LAYOUT`* — `<meta property="og:type">`, `og:site_name`, `og:title`, `og:description`, `og:image`, `og:url` + the Twitter Card analogues (`twitter:card=summary_large_image`, `twitter:title`, `twitter:description`, `twitter:image`). All templated through `{{og_*}}` placeholders so the `_render` HTML-escape protects against operator-supplied content breaking out of the attribute boundary. `<meta name="description">` also lands so non-OG crawlers see the same description. · *Default OG values on `_wrap(opts)`* — Every page renders with sensible defaults: `og:type=website`, `og:title=<page title> — <shop name>`, `og:description=Open-source ecommerce framework built on blamejs. Server-rendered HTML, post-quantum crypto, zero npm runtime dependencies.`, `og:image=/assets/brand/logo.png`. Per-renderer overrides via `opts.og_*` work surgically — only the field passed in overrides; the rest stay on defaults. · *Product-specific OG on the PDP* — `renderProduct` overrides four OG fields: `og_type=product`, `og_title=<product.title> — <shop_name>`, `og_description=<product.description>` (or a generated fallback when the description is empty), `og_image=<assetPrefix><heroMedia.r2_key>` (the first attached media row). A PDP share now renders the SVG hero + the product copy, not the brand logo + a generic shop description.
12
+
11
13
  - v0.0.49 (2026-05-22) — **README refresh — scoped install command, live-demo URL, refreshed primitive table + migration list.** The README hadn't been touched since v0.0.43; the install snippet still pointed at the unscoped `blamejs-shop` name and the primitives table listed only the original commerce surface. Operators landing on the GitHub repo now see the correct `npm install @blamejs/blamejs-shop`, the live demo URL, badges for npm + license + SLSA L3, and a primitives table that includes `customers`, `subscriptions`, `newsletter`, and `theme` alongside the original commerce stack. **Changed:** *README — install snippet uses `@blamejs/blamejs-shop`* — Replaces the unscoped clone-and-smoke recipe with `npm install @blamejs/blamejs-shop` + the `require()` shape that resolves the framework + adapters. The local clone path stays as the secondary recipe for operators who want to develop against the repo. Three badges land in the header — npm version, Apache-2.0 license, SLSA Level 3. · *README — primitive table extended* — Adds rows for `customers` (passkey-only, email hash-only), `subscriptions` (Stripe-backed recurring), `newsletter` (signup primitive shipped in v0.0.41), and `theme` (file-backed templates). The `storefront` row gains the full surface description — utility bar, sticky header, dark hero with code-preview card, marquee, featured-product callout, collections grid, framework feature band, designed catalog grid, newsletter band, and the four-column footer. `admin` row mentions the subscription-plan routes. · *README — migration list updated* — Lists migrations 0001-0010 (catalog, cart, order, shop_config, webhooks, customers, inventory_thresholds, subscriptions, newsletter_signups) so operators don't have to grep the directory to know what schema lands. Adds the demo-seed recipe (`scripts/seed-sample-products.sql` + `scripts/seed-sample-product-media.sql`) for one-shot population of the live demo's four products + their SVG hero images. · *Live-demo URL pinned* — `https://blamejs-shop.coocoo.workers.dev/` lands in the README header so operators evaluating the framework can see a real deployed instance rendering the storefront design system end-to-end before committing to wiring their own Cloudflare account.
12
14
 
13
15
  - v0.0.48 (2026-05-22) — **Home page — featured-product callout between the primitives marquee and the collections grid.** The home page rhythm went marquee → 6-tile collections grid → framework band, so a single product never got more than the four-tile catalog card's surface area to introduce itself. This release inserts a split callout (square hero image on the left, copy column with `Featured` eyebrow + title + description + accent-coloured price + `View product →` CTA on the right) between the marquee and the collections grid. The render picks the first product with attached media; operators wrapping `renderHome` can override the selection via `opts.featured`. **Added:** *`.featured-product` section* — Sits between the primitives marquee and the collections grid. Two-column grid at desktop (square hero + copy column), stacks to image-above-copy at <64rem with a 16/10 image aspect ratio. Title is a `clamp(1.75rem, 3vw, 2.5rem)` headline; price renders in the display font at `--text-2xl` with the accent colour + tabular-nums. Image is wrapped in an anchor to the PDP and has a hover-scale of `1.03` driven through the existing `--duration-slow` ease curve. The card itself has a hairline border on `var(--paper)` so it reads as a content tile, not a hero band — keeps the dark hero above and the framework band below as the page's main rhythm changes. · *`storefront.renderHome({ ..., featured? })` selection override* — When `opts.featured` is supplied the renderer uses it verbatim (shape: `{ title, description, price, slug, image_url, image_alt }`). When absent, the renderer scans the active-products list for the first row with `image_url` and uses that as the featured pick. Products without media are skipped; when no product has media the callout renders as an empty string and the page rhythm falls back to marquee → collections directly. **Changed:** *Product data shape — `description` carried through* — The renderHome `products.map(...)` projection now includes `description` (from the catalog products row) so the featured callout's lede can pull product-specific copy. Existing callers that didn't pass a description continue to render — the callout falls back to a stock 'Server-rendered, PQC-secured, shipped from origin' line.
package/lib/storefront.js CHANGED
@@ -60,8 +60,19 @@ var LAYOUT =
60
60
  " <meta charset=\"utf-8\">\n" +
61
61
  " <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n" +
62
62
  " <title>{{title}} — {{shop_name}}</title>\n" +
63
+ " <meta name=\"description\" content=\"{{og_description}}\">\n" +
63
64
  " <link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/brand/favicon.svg\">\n" +
64
65
  " <link rel=\"stylesheet\" href=\"{{theme_css}}\">\n" +
66
+ " <meta property=\"og:type\" content=\"{{og_type}}\">\n" +
67
+ " <meta property=\"og:site_name\" content=\"{{shop_name}}\">\n" +
68
+ " <meta property=\"og:title\" content=\"{{og_title}}\">\n" +
69
+ " <meta property=\"og:description\" content=\"{{og_description}}\">\n" +
70
+ " <meta property=\"og:image\" content=\"{{og_image}}\">\n" +
71
+ " <meta property=\"og:url\" content=\"{{og_url}}\">\n" +
72
+ " <meta name=\"twitter:card\" content=\"summary_large_image\">\n" +
73
+ " <meta name=\"twitter:title\" content=\"{{og_title}}\">\n" +
74
+ " <meta name=\"twitter:description\" content=\"{{og_description}}\">\n" +
75
+ " <meta name=\"twitter:image\" content=\"{{og_image}}\">\n" +
65
76
  "</head>\n" +
66
77
  "<body>\n" +
67
78
  " <a class=\"skip-link\" href=\"#main\">Skip to content</a>\n" +
@@ -179,14 +190,28 @@ function _wrap(opts) {
179
190
  var themeCss = (opts && typeof opts.theme_css === "string" && opts.theme_css.length)
180
191
  ? opts.theme_css
181
192
  : DEFAULT_THEME_CSS_URL;
193
+ // OpenGraph / Twitter Card defaults — every page sets reasonable
194
+ // fallbacks; per-page renderers (PDP, etc.) can override via
195
+ // `opts.og_*` for a product-specific share preview.
196
+ var shopName = opts.shop_name || "blamejs.shop";
197
+ var ogType = opts.og_type || "website";
198
+ var ogTitle = opts.og_title || (opts.title ? opts.title + " — " + shopName : shopName);
199
+ var ogDescription = opts.og_description || "Open-source ecommerce framework built on blamejs. Server-rendered HTML, post-quantum crypto, zero npm runtime dependencies.";
200
+ var ogImage = opts.og_image || "/assets/brand/logo.png";
201
+ var ogUrl = opts.og_url || "";
182
202
  return _render(LAYOUT, {
183
- title: opts.title,
184
- shop_name: opts.shop_name,
185
- cart_count: opts.cart_count == null ? 0 : opts.cart_count,
186
- year: String(new Date().getUTCFullYear()),
187
- search_q: opts.search_q == null ? "" : opts.search_q,
188
- theme_css: themeCss,
189
- body: "RAW_BODY_PLACEHOLDER",
203
+ title: opts.title,
204
+ shop_name: shopName,
205
+ cart_count: opts.cart_count == null ? 0 : opts.cart_count,
206
+ year: String(new Date().getUTCFullYear()),
207
+ search_q: opts.search_q == null ? "" : opts.search_q,
208
+ theme_css: themeCss,
209
+ og_type: ogType,
210
+ og_title: ogTitle,
211
+ og_description: ogDescription,
212
+ og_image: ogImage,
213
+ og_url: ogUrl,
214
+ body: "RAW_BODY_PLACEHOLDER",
190
215
  }).replace("RAW_BODY_PLACEHOLDER", opts.body);
191
216
  // The body is RAW HTML (already rendered + escaped at the
192
217
  // per-fragment level). The placeholder swap is post-render so the
@@ -740,12 +765,21 @@ function renderProduct(opts) {
740
765
  })
741
766
  .replace("RAW_GALLERY_PLACEHOLDER", galleryHtml)
742
767
  .replace("RAW_ROWS_PLACEHOLDER", rows);
768
+ // Product-specific OpenGraph + Twitter Card values so shares
769
+ // unfurl as "Operator Tee — blamejs.shop" with the SVG hero, not
770
+ // the default shop-level description + brand logo.
771
+ var heroMedia = (opts.media && opts.media[0]) || null;
772
+ var ogImage = heroMedia ? ((opts.asset_prefix || "/assets/") + heroMedia.r2_key) : "/assets/brand/logo.png";
743
773
  return _wrap({
744
- title: opts.product.title,
745
- shop_name: shopName,
746
- cart_count: cartCount,
747
- theme_css: opts.theme_css,
748
- body: body,
774
+ title: opts.product.title,
775
+ shop_name: shopName,
776
+ cart_count: cartCount,
777
+ theme_css: opts.theme_css,
778
+ og_type: "product",
779
+ og_title: opts.product.title + " — " + shopName,
780
+ og_description: description || ("Browse " + opts.product.title + " on " + shopName + "."),
781
+ og_image: ogImage,
782
+ body: body,
749
783
  });
750
784
  }
751
785
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.0.49",
3
+ "version": "0.0.50",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {