sbuilder-mcp 0.70.0 → 0.72.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.
- package/CHANGELOG.md +15 -0
- package/CHANGELOG.vi.md +15 -0
- package/dist/catalog/api.generated.js +60 -0
- package/dist/catalog/deadkeys.generated.js +2 -2
- package/dist/catalog/search.js +8 -1
- package/dist/catalog/shapes.generated.js +11 -1
- package/dist/catalog/source.generated.js +2 -2
- package/dist/catalog/translations.generated.js +19 -8
- package/dist/domains/site/readiness-fetch.js +2 -0
- package/dist/domains/site/readiness.js +67 -2
- package/dist/domains/site/translate.js +12 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ All notable changes to this project are documented in this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.72.0] - 2026-09-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_review` gains a `pageSeo` readiness gap: it lists published page, about, contact, policy, faq, and blog pages missing `settings.title` or `settings.description`, since the storefront falls back to the site name as `<title>` and drops the meta description entirely when either is unset, leaving every such page indistinguishable in a search result even though nothing on the canvas or in a screenshot shows it.
|
|
13
|
+
|
|
14
|
+
## [0.71.0] - 2026-09-28
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- `sb_review`'s `catalogue` readiness gap now reports "N products and none active" separately from an empty catalogue, for a store whose products were all created without `status` and stored as drafts.
|
|
18
|
+
- `sb_api_find`'s `product_traps` gains a `status` trap: a product created without `status` is stored as "draft" — the storefront never lists it and the checkout refuses it as a line — even though the write still answers 201.
|
|
19
|
+
- The translation call sheet (via `sb_api_find`) gains `review_gate`, explaining that `PUT /api/v1/translations` defaults a row's `source` to "machine", which is stored and counted but never shown on the storefront, in search, in slug routing, or in shopper mail until `POST /api/sites/{siteId}/translations/review` (or its `/bulk` form) approves it.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- The generated catalog was regenerated against the platform (`web_builder` `84f1f10bc`): flash sale campaign listing operations gain query parameters `q`, `status`, `limit`, and `offset`; the order/checkout request shapes now catalog a `locale` field that snapshots the shopper's storefront language at checkout; `slug` is now catalogued as a translatable field on products, categories, articles, blog categories, courses, and pages; and `review` is no longer a translatable entity type.
|
|
23
|
+
|
|
9
24
|
## [0.70.0] - 2026-09-26
|
|
10
25
|
|
|
11
26
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,21 @@ Mọi thay đổi đáng chú ý của dự án được ghi lại trong file n
|
|
|
6
6
|
Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
và dự án tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.72.0] - 2026-09-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `sb_review` có thêm gap readiness `pageSeo`: liệt kê các trang page, about, contact, policy, faq và blog đã publish nhưng thiếu `settings.title` hoặc `settings.description`, vì storefront sẽ dùng tên site làm `<title>` và bỏ hẳn meta description khi thiếu một trong hai — khiến các trang này không thể phân biệt trên kết quả tìm kiếm dù canvas hay screenshot đều không cho thấy điều đó.
|
|
13
|
+
|
|
14
|
+
## [0.71.0] - 2026-09-28
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Gap readiness `catalogue` của `sb_review` giờ báo riêng "có N sản phẩm, không cái nào active" thay vì gộp chung với catalogue rỗng, dành cho store có toàn bộ sản phẩm được tạo mà không kèm `status` nên bị lưu thành draft.
|
|
18
|
+
- `product_traps` của `sb_api_find` có thêm bẫy `status`: một sản phẩm tạo không kèm `status` sẽ bị lưu thành "draft" — storefront không liệt kê và checkout từ chối nó như một dòng hàng — dù request ghi vẫn trả về 201.
|
|
19
|
+
- Call sheet bản dịch (qua `sb_api_find`) có thêm `review_gate`, giải thích rằng `PUT /api/v1/translations` mặc định `source` của một dòng là "machine", dòng này được lưu và tính vào tiến độ nhưng không hiện ở storefront, trong tìm kiếm, trong slug routing hay trong mail gửi khách cho tới khi `POST /api/sites/{siteId}/translations/review` (hoặc bản `/bulk`) duyệt nó.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Catalog được sinh ra đã được làm mới dựa trên nền tảng (`web_builder` `84f1f10bc`): các operation liệt kê flash sale campaign có thêm tham số truy vấn `q`, `status`, `limit`, `offset`; request shape của order/checkout giờ catalog thêm field `locale` lưu lại ngôn ngữ storefront của khách tại thời điểm checkout; `slug` giờ được catalog là field dịch được trên product, category, article, blog category, course và page; và `review` không còn là một entity type dịch được nữa.
|
|
23
|
+
|
|
9
24
|
## [0.70.0] - 2026-09-26
|
|
10
25
|
|
|
11
26
|
### Added
|
|
@@ -8050,6 +8050,34 @@ export const API_OPERATIONS = [
|
|
|
8050
8050
|
"type": "string",
|
|
8051
8051
|
"description": "Site ID"
|
|
8052
8052
|
},
|
|
8053
|
+
{
|
|
8054
|
+
"name": "q",
|
|
8055
|
+
"in": "query",
|
|
8056
|
+
"required": false,
|
|
8057
|
+
"type": "string",
|
|
8058
|
+
"description": "Case-insensitive substring of the campaign name (GET only)"
|
|
8059
|
+
},
|
|
8060
|
+
{
|
|
8061
|
+
"name": "status",
|
|
8062
|
+
"in": "query",
|
|
8063
|
+
"required": false,
|
|
8064
|
+
"type": "string",
|
|
8065
|
+
"description": "off|scheduled|running|ended — derived status, evaluated now (GET only)"
|
|
8066
|
+
},
|
|
8067
|
+
{
|
|
8068
|
+
"name": "limit",
|
|
8069
|
+
"in": "query",
|
|
8070
|
+
"required": false,
|
|
8071
|
+
"type": "integer",
|
|
8072
|
+
"description": "Page size, default 50, max 200 (GET only)"
|
|
8073
|
+
},
|
|
8074
|
+
{
|
|
8075
|
+
"name": "offset",
|
|
8076
|
+
"in": "query",
|
|
8077
|
+
"required": false,
|
|
8078
|
+
"type": "integer",
|
|
8079
|
+
"description": "Page offset, default 0 (GET only)"
|
|
8080
|
+
},
|
|
8053
8081
|
{
|
|
8054
8082
|
"name": "flashSale",
|
|
8055
8083
|
"in": "body",
|
|
@@ -8078,6 +8106,34 @@ export const API_OPERATIONS = [
|
|
|
8078
8106
|
"type": "string",
|
|
8079
8107
|
"description": "Site ID"
|
|
8080
8108
|
},
|
|
8109
|
+
{
|
|
8110
|
+
"name": "q",
|
|
8111
|
+
"in": "query",
|
|
8112
|
+
"required": false,
|
|
8113
|
+
"type": "string",
|
|
8114
|
+
"description": "Case-insensitive substring of the campaign name (GET only)"
|
|
8115
|
+
},
|
|
8116
|
+
{
|
|
8117
|
+
"name": "status",
|
|
8118
|
+
"in": "query",
|
|
8119
|
+
"required": false,
|
|
8120
|
+
"type": "string",
|
|
8121
|
+
"description": "off|scheduled|running|ended — derived status, evaluated now (GET only)"
|
|
8122
|
+
},
|
|
8123
|
+
{
|
|
8124
|
+
"name": "limit",
|
|
8125
|
+
"in": "query",
|
|
8126
|
+
"required": false,
|
|
8127
|
+
"type": "integer",
|
|
8128
|
+
"description": "Page size, default 50, max 200 (GET only)"
|
|
8129
|
+
},
|
|
8130
|
+
{
|
|
8131
|
+
"name": "offset",
|
|
8132
|
+
"in": "query",
|
|
8133
|
+
"required": false,
|
|
8134
|
+
"type": "integer",
|
|
8135
|
+
"description": "Page offset, default 0 (GET only)"
|
|
8136
|
+
},
|
|
8081
8137
|
{
|
|
8082
8138
|
"name": "flashSale",
|
|
8083
8139
|
"in": "body",
|
|
@@ -18416,6 +18472,10 @@ export const API_DEFINITIONS = {
|
|
|
18416
18472
|
"$ref": "#/definitions/github_com_webbuilder_server_internal_orders.OrderItem"
|
|
18417
18473
|
}
|
|
18418
18474
|
},
|
|
18475
|
+
"locale": {
|
|
18476
|
+
"description": "Locale is the language the SHOPPER was reading the storefront in at\ncheckout — a SNAPSHOT, for the same reason every other checkout-time fact\non this struct is one: a merchant who later reorders or trims the site's\nlanguage list must not have an already-placed order start mailing its\nbuyer in a different language than the one they bought in.\n\n\"\" means the site's DEFAULT locale — every order placed before this field\nexisted, every order typed by staff, and every order placed through a\nsurface that has not been taught to send one yet. It is never trusted\noutright: Checkout.Create normalises it against the site's published\nlocales (SiteLocales port) before it is stored, so a stale or tampered\nclaim can only ever fall back to \"\", never choose an unpublished\nlanguage. See Checkout.normalizeLocale.",
|
|
18477
|
+
"type": "string"
|
|
18478
|
+
},
|
|
18419
18479
|
"note": {
|
|
18420
18480
|
"type": "string"
|
|
18421
18481
|
},
|
package/dist/catalog/search.js
CHANGED
|
@@ -352,12 +352,19 @@ export function describeOperation(op) {
|
|
|
352
352
|
'/apps/blocks again for what it contributed.',
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
|
-
// FILLING A CATALOGUE WAS REACHABLE AND
|
|
355
|
+
// FILLING A CATALOGUE WAS REACHABLE AND SIX FACTS ABOUT IT WERE NOT, each
|
|
356
356
|
// failing silently. Attached to the call sheet for the same reason the
|
|
357
357
|
// translation table is: this is where the agent is when it decides what to
|
|
358
358
|
// send, on both credential surfaces a product can be created or replaced on.
|
|
359
359
|
if (PRODUCT_WRITE_IDS.has(op.id)) {
|
|
360
360
|
out.product_traps = {
|
|
361
|
+
// THE STATUS NOBODY SENDS. An empty Status becomes ProductStatusDraft
|
|
362
|
+
// (products/product.go), the storefront lists only "active", and since
|
|
363
|
+
// web_builder b4d60a862 the checkout REFUSES any other status as a line.
|
|
364
|
+
// The write answers 201 either way.
|
|
365
|
+
status: 'Send status: "active". A product created without one is stored as "draft" — the ' +
|
|
366
|
+
'storefront never lists it and the checkout refuses it — and the write still ' +
|
|
367
|
+
'answers 201. PATCH is 405: to activate later, PUT the whole object back.',
|
|
361
368
|
price: 'Price lives on the VARIANT, not the product — variants[].priceCents. A product ' +
|
|
362
369
|
'posted with no variant renders a catalogue entry nobody can buy.',
|
|
363
370
|
// MINOR UNITS, SAID WHERE THE NUMBER IS BEING WRITTEN.
|
|
@@ -4,7 +4,7 @@ export const SHAPE_SOURCE = {
|
|
|
4
4
|
"fromHandlers": 197,
|
|
5
5
|
"fromSwaggerOnly": 0,
|
|
6
6
|
"withReadOnly": 28,
|
|
7
|
-
"structsRead":
|
|
7
|
+
"structsRead": 1977
|
|
8
8
|
};
|
|
9
9
|
export const REQUEST_SHAPES = {
|
|
10
10
|
"post:/api/sites/{id}/ai-credits/purchase": {
|
|
@@ -4736,6 +4736,11 @@ export const REQUEST_SHAPES = {
|
|
|
4736
4736
|
"name": "currency",
|
|
4737
4737
|
"type": "string"
|
|
4738
4738
|
},
|
|
4739
|
+
{
|
|
4740
|
+
"name": "locale",
|
|
4741
|
+
"type": "string",
|
|
4742
|
+
"note": "Locale is the language the SHOPPER was reading the storefront in at checkout — a SNAPSHOT, for the same reason every other checkout-time fact on this struct is one: a merchant who later reorders or trims the site's language list must not…"
|
|
4743
|
+
},
|
|
4739
4744
|
{
|
|
4740
4745
|
"name": "discountCode",
|
|
4741
4746
|
"type": "string",
|
|
@@ -5047,6 +5052,11 @@ export const REQUEST_SHAPES = {
|
|
|
5047
5052
|
"name": "currency",
|
|
5048
5053
|
"type": "string"
|
|
5049
5054
|
},
|
|
5055
|
+
{
|
|
5056
|
+
"name": "locale",
|
|
5057
|
+
"type": "string",
|
|
5058
|
+
"note": "Locale is the language the SHOPPER was reading the storefront in at checkout — a SNAPSHOT, for the same reason every other checkout-time fact on this struct is one: a merchant who later reorders or trims the site's language list must not…"
|
|
5059
|
+
},
|
|
5050
5060
|
{
|
|
5051
5061
|
"name": "discountCode",
|
|
5052
5062
|
"type": "string",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// GENERATED by scripts/gen-catalog.ts — do not edit by hand.
|
|
2
2
|
// The platform commit every other generated file here describes.
|
|
3
3
|
export const PLATFORM_SOURCE = {
|
|
4
|
-
"commit": "
|
|
5
|
-
"committedAt": "2026-09-
|
|
4
|
+
"commit": "84f1f10bcbef0748d0b18e19283ccb0458599c1b",
|
|
5
|
+
"committedAt": "2026-09-26T21:25:06+07:00",
|
|
6
6
|
"dirty": false
|
|
7
7
|
};
|
|
@@ -4,7 +4,7 @@ export const TRANSLATION_SOURCE = {
|
|
|
4
4
|
"elements": 66,
|
|
5
5
|
"pairs": 180,
|
|
6
6
|
"neverKeys": 178,
|
|
7
|
-
"entityTypes":
|
|
7
|
+
"entityTypes": 11
|
|
8
8
|
};
|
|
9
9
|
/** Every entity type a translation record can name, including "node". */
|
|
10
10
|
export const TRANSLATION_ENTITY_TYPES = [
|
|
@@ -13,7 +13,6 @@ export const TRANSLATION_ENTITY_TYPES = [
|
|
|
13
13
|
"article",
|
|
14
14
|
"blogCategory",
|
|
15
15
|
"course",
|
|
16
|
-
"review",
|
|
17
16
|
"courseSection",
|
|
18
17
|
"courseLesson",
|
|
19
18
|
"node",
|
|
@@ -546,6 +545,9 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
546
545
|
"key": "attributes",
|
|
547
546
|
"list": "attributes"
|
|
548
547
|
},
|
|
548
|
+
{
|
|
549
|
+
"key": "slug"
|
|
550
|
+
},
|
|
549
551
|
{
|
|
550
552
|
"key": "seoTitle"
|
|
551
553
|
},
|
|
@@ -570,6 +572,9 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
570
572
|
"key": "description",
|
|
571
573
|
"html": true
|
|
572
574
|
},
|
|
575
|
+
{
|
|
576
|
+
"key": "slug"
|
|
577
|
+
},
|
|
573
578
|
{
|
|
574
579
|
"key": "seoTitle"
|
|
575
580
|
},
|
|
@@ -601,6 +606,9 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
601
606
|
{
|
|
602
607
|
"key": "tags"
|
|
603
608
|
},
|
|
609
|
+
{
|
|
610
|
+
"key": "slug"
|
|
611
|
+
},
|
|
604
612
|
{
|
|
605
613
|
"key": "seoTitle"
|
|
606
614
|
},
|
|
@@ -624,6 +632,9 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
624
632
|
{
|
|
625
633
|
"key": "description"
|
|
626
634
|
},
|
|
635
|
+
{
|
|
636
|
+
"key": "slug"
|
|
637
|
+
},
|
|
627
638
|
{
|
|
628
639
|
"key": "seoTitle"
|
|
629
640
|
},
|
|
@@ -666,6 +677,9 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
666
677
|
{
|
|
667
678
|
"key": "tags"
|
|
668
679
|
},
|
|
680
|
+
{
|
|
681
|
+
"key": "slug"
|
|
682
|
+
},
|
|
669
683
|
{
|
|
670
684
|
"key": "seoTitle"
|
|
671
685
|
},
|
|
@@ -682,12 +696,6 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
682
696
|
"key": "ogDesc"
|
|
683
697
|
}
|
|
684
698
|
],
|
|
685
|
-
"review": [
|
|
686
|
-
{
|
|
687
|
-
"key": "reply",
|
|
688
|
-
"html": true
|
|
689
|
-
}
|
|
690
|
-
],
|
|
691
699
|
"courseSection": [
|
|
692
700
|
{
|
|
693
701
|
"key": "title"
|
|
@@ -713,6 +721,9 @@ export const TRANSLATABLE_ENTITY_FIELDS = {
|
|
|
713
721
|
},
|
|
714
722
|
{
|
|
715
723
|
"key": "ogDesc"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
"key": "slug"
|
|
716
727
|
}
|
|
717
728
|
],
|
|
718
729
|
"uiString": [
|
|
@@ -104,6 +104,8 @@ export async function gatherReadiness(ctx, siteId, pageNodes, openPageId) {
|
|
|
104
104
|
? {
|
|
105
105
|
active: rows.filter((p) => (p.status ?? 'active') === 'active').length,
|
|
106
106
|
purchasable: rows.filter((p) => (p.status ?? 'active') === 'active' && (p.priceCents ?? 0) > 0).length,
|
|
107
|
+
// Drafts and archived: what a catalogue built without status:"active" is.
|
|
108
|
+
inactive: rows.filter((p) => (p.status ?? 'active') !== 'active').length,
|
|
107
109
|
}
|
|
108
110
|
: null;
|
|
109
111
|
// A page an entity links to is that entity's own; only the shared template
|
|
@@ -31,6 +31,15 @@ const COMMERCE_TYPES = [
|
|
|
31
31
|
'dataset-block',
|
|
32
32
|
'list-dataset',
|
|
33
33
|
];
|
|
34
|
+
/**
|
|
35
|
+
* The page types a search engine INDEXES under the page's own settings. Entity
|
|
36
|
+
* templates (product, category, post, course) get their <title> and
|
|
37
|
+
* description merged from the entity (storefront/entityroute.go
|
|
38
|
+
* mergeEntitySettings); error and maintain have no address; the fixed-path and
|
|
39
|
+
* auth types are chrome a crawler is not sent to. Mirrors page.SlugTypes minus
|
|
40
|
+
* the auth four, plus the blog listing.
|
|
41
|
+
*/
|
|
42
|
+
const INDEXED_PAGE_TYPES = new Set(['page', 'about', 'contact', 'policy', 'faq', 'blog']);
|
|
34
43
|
/** Does this node carry a purchase action (add to cart, buy now)? */
|
|
35
44
|
function boundProductAction(n) {
|
|
36
45
|
return (n.bindings ?? []).some((b) => b.id === PRODUCT_ACTION_BINDING_ID && !!b.target?.action);
|
|
@@ -163,6 +172,45 @@ export function readinessGaps(input) {
|
|
|
163
172
|
'footer and a link back to the home page.',
|
|
164
173
|
});
|
|
165
174
|
}
|
|
175
|
+
// EVERY PAGE ANSWERS WITH THE SAME <title>, AND NO DESCRIPTION.
|
|
176
|
+
//
|
|
177
|
+
// storefront.go pageSettings.head: a page whose settings carry no `title`
|
|
178
|
+
// serves the SITE NAME as its <title>, and no `description` means no meta
|
|
179
|
+
// description at all — so a site built through these tools, where nothing
|
|
180
|
+
// ever asks for either, publishes N pages that a search result cannot tell
|
|
181
|
+
// apart. Invisible on the canvas, invisible in a screenshot, and the one
|
|
182
|
+
// thing a search engine reads first.
|
|
183
|
+
//
|
|
184
|
+
// Only the types whose head IS the page's own settings (INDEXED_PAGE_TYPES),
|
|
185
|
+
// only published pages, and only when the list carried `settings` at all —
|
|
186
|
+
// an absent blob is "not read", not "empty".
|
|
187
|
+
if (pages) {
|
|
188
|
+
const bare = pages.filter((p) => {
|
|
189
|
+
if (p.status !== 'published' || !INDEXED_PAGE_TYPES.has(p.type))
|
|
190
|
+
return false;
|
|
191
|
+
const st = p.settings;
|
|
192
|
+
if (!st || typeof st !== 'object')
|
|
193
|
+
return false;
|
|
194
|
+
const has = (k) => typeof st[k] === 'string' && st[k].trim() !== '';
|
|
195
|
+
return !(has('title') && has('description'));
|
|
196
|
+
});
|
|
197
|
+
if (bare.length > 0) {
|
|
198
|
+
const named = bare.map((p) => p.name).filter((n) => !!n).slice(0, 3);
|
|
199
|
+
gaps.push({
|
|
200
|
+
id: 'pageSeo',
|
|
201
|
+
draft: false,
|
|
202
|
+
problem: `${bare.length} published page${bare.length === 1 ? '' : 's'}` +
|
|
203
|
+
(named.length ? ` (${named.join(', ')}${bare.length > named.length ? ', …' : ''})` : '') +
|
|
204
|
+
' carry no settings.title or settings.description. Each serves the SITE NAME as its ' +
|
|
205
|
+
'<title> and no meta description — a search result cannot tell them apart, and ' +
|
|
206
|
+
'nothing on the canvas or in a screenshot shows it.',
|
|
207
|
+
fix: 'PATCH /api/v1/pages/{id} with {settings: {...existing, title, description}} — ' +
|
|
208
|
+
'`settings` REPLACES the blob, so read the page and send it back whole. Title is ' +
|
|
209
|
+
'what the tab and the result show (under ~60 chars); description is the ' +
|
|
210
|
+
'two-line summary under it. ogImage takes a share picture.',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
166
214
|
// ONE PAGE QUIETLY BECAME THE WHOLE ACCOUNT AREA.
|
|
167
215
|
//
|
|
168
216
|
// This server used to tell agents, in as many words, to put the login and the
|
|
@@ -363,14 +411,31 @@ export function readinessGaps(input) {
|
|
|
363
411
|
// NOTHING TO SELL. Checked before the delivery option, because a shipping
|
|
364
412
|
// method for an empty catalogue is furniture.
|
|
365
413
|
if (input.products) {
|
|
366
|
-
if (input.products.active === 0) {
|
|
414
|
+
if (input.products.active === 0 && (input.products.inactive ?? 0) > 0) {
|
|
415
|
+
// PRODUCTS EXIST AND NONE IS ACTIVE. A product created without a status is
|
|
416
|
+
// stored as "draft" (products.go: an empty Status becomes ProductStatusDraft),
|
|
417
|
+
// the storefront lists only "active", and since web_builder b4d60a862 the
|
|
418
|
+
// checkout refuses any other status outright — so a catalogue built through
|
|
419
|
+
// the API without saying status:"active" is a store with nothing to buy.
|
|
420
|
+
gaps.push({
|
|
421
|
+
id: 'catalogue',
|
|
422
|
+
draft: false,
|
|
423
|
+
problem: `The store has ${input.products.inactive} products and none is active — a product ` +
|
|
424
|
+
'created without status is stored as "draft", which the storefront never lists and ' +
|
|
425
|
+
'the checkout refuses. Every product list renders its empty state.',
|
|
426
|
+
fix: 'PUT /api/v1/products/{id} with the whole object read back and status: "active" ' +
|
|
427
|
+
'(PATCH is 405). Send status: "active" on every POST /api/v1/products from now on.',
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
else if (input.products.active === 0) {
|
|
367
431
|
gaps.push({
|
|
368
432
|
id: 'catalogue',
|
|
369
433
|
draft: false,
|
|
370
434
|
problem: 'The store has no active products. Every product list on the site renders its empty ' +
|
|
371
435
|
'state, the product template has nothing to bind to, and there is nothing to add to a ' +
|
|
372
436
|
'cart — on a site that otherwise reports ready.',
|
|
373
|
-
fix: 'Create products (sb_api_find "create product")
|
|
437
|
+
fix: 'Create products (sb_api_find "create product") with status: "active" — one created ' +
|
|
438
|
+
'without a status is a draft. priceCents is minor units — VND × 100.',
|
|
374
439
|
});
|
|
375
440
|
}
|
|
376
441
|
else if (input.products.purchasable === 0) {
|
|
@@ -68,5 +68,17 @@ export function translationCallSheet() {
|
|
|
68
68
|
'that element allows. Translating any OTHER special BREAKS the render rather than ' +
|
|
69
69
|
'degrading it: `name` is a lucide icon id, `src` a URL, `filterSource` a registry id the ' +
|
|
70
70
|
'renderer switches on. Never walk a document translating every string you find.',
|
|
71
|
+
// THE REVIEW GATE (web_builder c6184c085). The storefront, search, slug routing
|
|
72
|
+
// and shopper mail read only rows whose source is "human". The key surface
|
|
73
|
+
// defaults source to "machine" — deliberately, so an app cannot mislabel an
|
|
74
|
+
// unread value as reviewed — so a translation written there is stored,
|
|
75
|
+
// counted by /progress, and never SHOWN until a review call approves it.
|
|
76
|
+
review_gate: 'Only rows with source "human" are served — on the storefront, in search, in slug ' +
|
|
77
|
+
'routing and in shopper mail. PUT /api/v1/translations defaults source to "machine": ' +
|
|
78
|
+
'the row is stored and counted by GET /api/v1/translations/progress, and shown to ' +
|
|
79
|
+
'nobody until POST /api/sites/{siteId}/translations/review {locale, entityType, ' +
|
|
80
|
+
'entityId, field} or …/review/bulk approves it (session credential). Send ' +
|
|
81
|
+
'"source": "human" only when a person read the text; the site-scoped PUT ' +
|
|
82
|
+
'/api/sites/{siteId}/translations already defaults to "human".',
|
|
71
83
|
};
|
|
72
84
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"description": "MCP server that designs and operates a Store Builder site — pages, data, theme and publish — through the platform's own API and live-edit protocol.",
|
|
5
5
|
"mcpName": "io.github.vuluu2k/sbuilder-mcp",
|
|
6
6
|
"type": "module",
|