create-magic-storefront 0.1.2 → 0.2.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.
@@ -57,6 +57,22 @@ const en = {
57
57
  `${count} ${plural('en', count, { one: 'review', other: 'reviews' })}`,
58
58
  recommendUs: (percent: number) => `${percent}% recommend us`,
59
59
  leaveReview: 'Leave a review',
60
+ // Blog
61
+ blog: 'Blog',
62
+ allArticles: 'All articles',
63
+ featured: "Editor's picks",
64
+ readingTime: (minutes: number) => `${minutes} min read`,
65
+ relatedArticles: 'Read also',
66
+ productsInArticle: 'Products in this article',
67
+ wasHelpful: 'Was this article helpful?',
68
+ helpful: 'Yes',
69
+ notHelpful: 'No',
70
+ helpfulCount: (count: number) => `Helpful for ${count}`,
71
+ thanksForVote: 'Thank you for your feedback',
72
+ searchArticles: 'Search articles',
73
+ sortNewest: 'Newest',
74
+ sortPopular: 'Popular',
75
+ tag: 'Tag',
60
76
  // Product
61
77
  addToCart: 'Add to cart',
62
78
  addedToCart: 'Added to cart.',
@@ -156,6 +172,21 @@ const ru: Messages = {
156
172
  `${count} ${plural('ru', count, { one: 'отзыв', few: 'отзыва', other: 'отзывов' })}`,
157
173
  recommendUs: (percent) => `${percent}% рекомендуют нас`,
158
174
  leaveReview: 'Оставить отзыв',
175
+ blog: 'Блог',
176
+ allArticles: 'Все статьи',
177
+ featured: 'Выбор редакции',
178
+ readingTime: (minutes) => `${minutes} мин чтения`,
179
+ relatedArticles: 'Читайте также',
180
+ productsInArticle: 'Товары из статьи',
181
+ wasHelpful: 'Статья была полезной?',
182
+ helpful: 'Да',
183
+ notHelpful: 'Нет',
184
+ helpfulCount: (count) => `Полезно: ${count}`,
185
+ thanksForVote: 'Спасибо за отзыв',
186
+ searchArticles: 'Поиск по статьям',
187
+ sortNewest: 'Новые',
188
+ sortPopular: 'Популярные',
189
+ tag: 'Тег',
159
190
  addToCart: 'В корзину',
160
191
  addedToCart: 'Добавлено в корзину.',
161
192
  couldNotAdd: 'Не удалось добавить в корзину.',
@@ -248,6 +279,21 @@ const uz: Messages = {
248
279
  reviewsCount: (count) => `${count} ta sharh`,
249
280
  recommendUs: (percent) => `${percent}% bizni tavsiya qiladi`,
250
281
  leaveReview: 'Sharh qoldirish',
282
+ blog: 'Blog',
283
+ allArticles: 'Barcha maqolalar',
284
+ featured: 'Muharrir tanlovi',
285
+ readingTime: (minutes) => `${minutes} daqiqa o'qish`,
286
+ relatedArticles: "Shuningdek o'qing",
287
+ productsInArticle: 'Maqoladagi mahsulotlar',
288
+ wasHelpful: "Maqola foydali bo'ldimi?",
289
+ helpful: 'Ha',
290
+ notHelpful: "Yo'q",
291
+ helpfulCount: (count) => `Foydali: ${count}`,
292
+ thanksForVote: 'Fikringiz uchun rahmat',
293
+ searchArticles: 'Maqolalardan qidirish',
294
+ sortNewest: 'Yangilari',
295
+ sortPopular: 'Ommaboplari',
296
+ tag: 'Teg',
251
297
  addToCart: "Savatga qo'shish",
252
298
  addedToCart: "Savatga qo'shildi.",
253
299
  couldNotAdd: "Savatga qo'shib bo'lmadi.",
@@ -0,0 +1,12 @@
1
+ import type { Schema } from '@magicstoreai/storefront-client';
2
+
3
+ /**
4
+ * The storefront's public origin for absolute URLs (sitemap, canonical links): `SITE_URL`, else
5
+ * the shop's primary domain.
6
+ */
7
+ export function siteOrigin(shop: Pick<Schema<'Shop'>, 'primaryDomain'>): string {
8
+ return (
9
+ process.env.SITE_URL?.replace(/\/+$/, '') ??
10
+ `https://${shop.primaryDomain ?? process.env.MAGICSTORE_SHOP_DOMAIN}`
11
+ );
12
+ }
package/template/llms.txt CHANGED
@@ -11,12 +11,15 @@ Every storefront — hand-written or generated — imports the SDK **only** thro
11
11
  | ------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------- |
12
12
  | `@magicstoreai/storefront-client` | `createStorefrontClient`: one typed method per API `operationId`, `MagicStoreError`, `paginate`, `Schema<>` | anywhere, no React |
13
13
  | `@magicstoreai/hydrogen` | React: `MagicStoreProvider`, `useCart`, `useCustomer`, `useWishlist`, `<Money>`, `<Image>`, … | client (`"use client"`) |
14
- | `@magicstoreai/hydrogen/core` | The same state without React: `CartController`, `CustomerSessionController`, `formatMoney`, variants | anywhere |
14
+ | `@magicstoreai/hydrogen/core` | The same state without React: `CartController`, `CustomerSessionController`, `formatMoney`, variants, blog | anywhere |
15
15
  | `@magicstoreai/hydrogen/server` | `nextCacheFetch` (cache tags), `createWebhookHandler` (signed webhooks → revalidation) | server, edge |
16
- | `@magicstoreai/hydrogen/seo` | `pageMeta`, `productJsonLd`, `breadcrumbJsonLd`, `jsonLdScript` | anywhere |
16
+ | `@magicstoreai/hydrogen/seo` | `pageMeta`, `productJsonLd`, `articleMeta`, `articleJsonLd`, `breadcrumbJsonLd`, `jsonLdScript` | anywhere |
17
17
 
18
18
  Never import from `src/` or `dist/` of a package, and never call `/api/v2/storefront/…` by URL —
19
- call the client method. `npx create-magic-storefront check` fails on either.
19
+ call the client method. A server component takes only components (`<Image>`, `<Money>`, …) from
20
+ `@magicstoreai/hydrogen`, a client module; functions and classes (`paginationState`,
21
+ `MagicStoreError`, `formatMoney`) come from `/core`, `/server`, `/seo` or
22
+ `@magicstoreai/storefront-client`. `npx create-magic-storefront check` fails on any of these.
20
23
 
21
24
  ## Start
22
25
 
@@ -24,15 +27,15 @@ call the client method. `npx create-magic-storefront check` fails on either.
24
27
  npm create magic-storefront@latest my-shop -- --shop shop.example.uz
25
28
  ```
26
29
 
27
- scaffolds a Next.js App Router storefront (home from the merchant's sections, catalog, product,
28
- search, cart, checkout, sign-in, orders, SEO, webhook revalidation). Or install the packages:
30
+ scaffolds a Next.js App Router storefront (home from the merchant's sections or your own, catalog,
31
+ product, search, blog, cart, checkout, sign-in, orders, SEO, webhook revalidation). Or install the packages:
29
32
  `npm i @magicstoreai/storefront-client @magicstoreai/hydrogen`.
30
33
 
31
34
  ## Docs
32
35
 
33
36
  - `PAGES.md` (in a scaffolded storefront; `examples/starter/PAGES.md` in the SDK repository): how to
34
- build each page type — home from the `/home` sections, collection, product, search, cart,
35
- checkout, account — with its data calls, cache tags, SEO and required states.
37
+ build each page type — home (from the `/home` sections or custom), collection, product, search,
38
+ blog, cart, checkout, account — with its data calls, cache tags, SEO and required states.
36
39
  - [Hydrogen catalogue](node_modules/@magicstoreai/hydrogen/CATALOGUE.md): every export with its props
37
40
  or signature, an example, and the API operations it calls. In the SDK repository:
38
41
  `packages/hydrogen/CATALOGUE.md`.
@@ -40,7 +43,8 @@ search, cart, checkout, sign-in, orders, SEO, webhook revalidation). Or install
40
43
  idempotency, retries, errors, pagination.
41
44
  - [Hydrogen README](node_modules/@magicstoreai/hydrogen/README.md): provider, hooks, server helpers.
42
45
  - The API itself (normative): the backend repository's `docs/api/v2/` — `standards.md` (wire format,
43
- every error code), `authentication.md`, `cart.md`, `checkout.md`, `customer.md`, `webhooks.md`.
46
+ every error code), `authentication.md`, `cart.md`, `checkout.md`, `customer.md`, `blog.md`,
47
+ `webhooks.md`.
44
48
  Operation shapes: `Schema<'Name'>` and the client's method types, generated from the OpenAPI spec.
45
49
 
46
50
  ## Two credentials
@@ -65,8 +69,30 @@ Public catalog reads need no customer. Render them on the server with a client w
65
69
  - Lists paginate with `page` + `perPage` (default 24, max 100); read `meta.pagination`, or
66
70
  `<Pagination>`. Sorts order (`sort=-createdAt`), filters narrow (`filter[onSale]=true`).
67
71
  - A missing value is `null`, never `0` or `""`: show a fallback.
68
- - The home page is `GET /home`: an ordered list of sections, one `type` each
69
- (`GET /theme/section-schema` has every type's JSON Schema). Render an unknown type as nothing.
72
+ - The home page is yours to design. `GET /home` (an ordered list of sections, one `type` each;
73
+ `GET /theme/section-schema` has every type's JSON Schema) is optional: render it to let the
74
+ merchant arrange the page from the admin, or compose a custom home from catalog and content
75
+ data. When you render it, an unknown type renders as nothing.
76
+
77
+ ## Blog
78
+
79
+ - Only when `shop.features.blog` is on: at least one article is readable in the response language.
80
+ `shop.blog` is the blog home's `{ title, description }` or `null`.
81
+ - `blogArticlesIndex` (12 a page; `sort`, `q`, `filter[category|author|tag|featured]`),
82
+ `blogArticlesShow`, `blogArticlesRelated`, `blogCategoriesIndex`, `blogAuthorsIndex`. An unknown
83
+ `sort` or filter is `422 VALIDATION_FAILED`, never ignored.
84
+ - Articles are strict about language: one not written in the response locale is a `404`, with no
85
+ fallback. Build `hreflang` only from `availableLocales` (`articleMeta` does).
86
+ - `bodyHtml` is sanitized by the server. Split it with `articleBodyBlocks()` (`/core`, server-safe):
87
+ `<product-embed handle>` → a product card, `<oembed url>` → media, `<aside data-callout>` → a
88
+ callout. Fetch the embedded products in one `productsIndex({ query: { 'filter[handles]' } })` per
89
+ `embeddedProductFilters(article.products)` value, put them back in order with
90
+ `orderEmbeddedProducts`, and drop a marker whose product did not come back.
91
+ - «Was this helpful?»: `useArticleVote(handle, helpfulCount)`. A guest votes as the browser
92
+ (`X-Anonymous-Id`, `anonymousId()`), a customer as themselves. Report reads with
93
+ `useAnalytics().articleView(article.id)` (`ARTICLE_VIEW`).
94
+ - Blog reads are cached under `magicstore:pages` (`PAGES_UPDATED`); view and vote counters may be
95
+ up to 10 minutes old.
70
96
 
71
97
  ## Credentials in the browser
72
98