@commercelayer/agent-cli 0.2.2 → 0.2.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.
- package/README.md +35 -19
- package/dist/api/client.js +39 -1
- package/dist/api/stub/management.js +169 -29
- package/dist/api/stub/shopping.js +5 -6
- package/dist/api/stub/theme.js +32 -3
- package/dist/api/stub/ucp.js +4 -1
- package/dist/bin.js +31 -1
- package/dist/commands.js +160 -27
- package/dist/files.js +21 -0
- package/dist/links.js +118 -0
- package/dist/promotions.js +59 -30
- package/dist/store.js +3 -29
- package/dist/storefront.js +6 -3
- package/package.json +1 -1
package/dist/store.js
CHANGED
|
@@ -8,7 +8,6 @@ import YAML from 'yaml';
|
|
|
8
8
|
import { fail } from './output.js';
|
|
9
9
|
import { paths } from './files.js';
|
|
10
10
|
import { productFiles, readProductFile } from './products.js';
|
|
11
|
-
import { checkPromotions } from './promotions.js';
|
|
12
11
|
/** The keys a product may have. Stock is not one: inventory lives in Commerce Layer. */
|
|
13
12
|
export const PRODUCT_KEYS = ['sku', 'name', 'description', 'image_url', 'category', 'prices'];
|
|
14
13
|
export const THEME_KEYS = ['color', 'logo', 'hero', 'tagline'];
|
|
@@ -62,18 +61,6 @@ theme:
|
|
|
62
61
|
# hero: img/hero.jpg # the home page's wide image; without it a band in the accent
|
|
63
62
|
# tagline: Small-batch coffee, roasted in Milan # at most 120 characters
|
|
64
63
|
|
|
65
|
-
promotions:
|
|
66
|
-
# Discount codes and automatic discounts: rules (when) and actions (what).
|
|
67
|
-
# The id is unique and stable. Amounts are in the market currency, like prices.
|
|
68
|
-
# - id: welcome10
|
|
69
|
-
# name: Welcome, 10% off
|
|
70
|
-
# rules: [{ coupon: WELCOME10 }] # codes have 8 to 40 characters
|
|
71
|
-
# actions: [{ percentage: 10 }]
|
|
72
|
-
# - id: free-shipping-50
|
|
73
|
-
# market: eu
|
|
74
|
-
# rules: [{ order_amount: { min: 50 } }]
|
|
75
|
-
# actions: [free_shipping]
|
|
76
|
-
|
|
77
64
|
storefront:
|
|
78
65
|
# Only for a custom storefront (your own web app in this repo, published with
|
|
79
66
|
# c11r storefront publish): it replaces the theme. Its folder, from the repo root.
|
|
@@ -149,6 +136,8 @@ export function loadStore() {
|
|
|
149
136
|
}
|
|
150
137
|
}
|
|
151
138
|
}
|
|
139
|
+
if (doc.promotions !== undefined)
|
|
140
|
+
fail('invalid_store', 'store.yml has promotions:, which is not part of the store files.', 'Promotions are live settings in Commerce Layer: create each one with `c11r promotions create -f <file>` (the same entry as YAML or JSON), then remove promotions: from store.yml.');
|
|
152
141
|
const policies = (doc.policies ?? {});
|
|
153
142
|
for (const [key, url] of Object.entries(policies)) {
|
|
154
143
|
if (url && !/^https?:\/\//.test(String(url)))
|
|
@@ -157,8 +146,7 @@ export function loadStore() {
|
|
|
157
146
|
const theme = checkTheme(doc.theme);
|
|
158
147
|
const currencies = [...new Set(markets.map((m) => m.currency))];
|
|
159
148
|
const products = checkProducts(sourced, currencies);
|
|
160
|
-
|
|
161
|
-
return { name: doc.name, markets, products, storefront: doc.storefront, policies, theme, promotions };
|
|
149
|
+
return { name: doc.name, markets, products, storefront: doc.storefront, policies, theme };
|
|
162
150
|
}
|
|
163
151
|
/** The product rules, shared by loadStore and catalog pull: errors name each product's source. */
|
|
164
152
|
export function checkProducts(sourced, currencies) {
|
|
@@ -270,19 +258,5 @@ export function diff(state, desired) {
|
|
|
270
258
|
if (!(id in after[kind]))
|
|
271
259
|
changes.push({ action: 'delete', type, id });
|
|
272
260
|
}
|
|
273
|
-
// Promotions, by id. An API that does not know them (undefined) is skipped: apply refuses instead.
|
|
274
|
-
if (!applied || state.promotions !== undefined) {
|
|
275
|
-
const before = (applied ? state.promotions : undefined) ?? {};
|
|
276
|
-
const want = Object.fromEntries((desired.promotions ?? []).map((p) => [p.id, p]));
|
|
277
|
-
for (const id of Object.keys(want)) {
|
|
278
|
-
if (!(id in before))
|
|
279
|
-
changes.push({ action: 'create', type: 'promotion', id });
|
|
280
|
-
else if (canonical(before[id]) !== canonical(want[id]))
|
|
281
|
-
changes.push({ action: 'update', type: 'promotion', id });
|
|
282
|
-
}
|
|
283
|
-
for (const id of Object.keys(before))
|
|
284
|
-
if (!(id in want))
|
|
285
|
-
changes.push({ action: 'delete', type: 'promotion', id });
|
|
286
|
-
}
|
|
287
261
|
return changes;
|
|
288
262
|
}
|
package/dist/storefront.js
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
// top-level category), styles.css (theme tokens first, then the drop-in.css
|
|
4
4
|
// overrides, then the page) and app.js (market switch, category links, and a
|
|
5
5
|
// product page at #/p/<sku>). Product images are the store's, at /img/<file> (from
|
|
6
|
-
// .commercelayer/img/, uploaded by apply)
|
|
6
|
+
// .commercelayer/img/, uploaded by apply); a product without one shows the tile the
|
|
7
|
+
// store host draws at /tiles/<sku>.svg. The agent then makes it the human's.
|
|
7
8
|
import { isLocalImage } from './store.js';
|
|
8
9
|
/** img/<file> is the store's image at /img/<file> on the store host, whatever page links it. */
|
|
9
10
|
const imageSrc = (url) => (isLocalImage(url) ? `/${url}` : url);
|
|
11
|
+
/** A product without an image gets the tile the store host draws for it, at /tiles/<sku>.svg. */
|
|
12
|
+
const productImage = (p) => (p.image_url ? imageSrc(p.image_url) : `/tiles/${encodeURIComponent(p.sku)}.svg`);
|
|
10
13
|
const esc = (t) => String(t ?? '').replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]);
|
|
11
14
|
/** The scaffold's files, by path relative to the storefront folder. */
|
|
12
15
|
export function storefrontFiles(store, clientId) {
|
|
@@ -17,7 +20,7 @@ function indexHtml(store, clientId) {
|
|
|
17
20
|
const card = (p, h) => {
|
|
18
21
|
const href = `#/p/${encodeURIComponent(p.sku)}`;
|
|
19
22
|
return ` <article class="product" id="p-${esc(p.sku)}">
|
|
20
|
-
<a class="media" href="${esc(href)}" tabindex="-1"
|
|
23
|
+
<a class="media" href="${esc(href)}" tabindex="-1"><img src="${esc(productImage(p))}" alt="${esc(p.name)}"${p.image_url ? '' : ' loading="lazy"'}></a>
|
|
21
24
|
<${h}><a href="${esc(href)}">${esc(p.name)}</a></${h}>
|
|
22
25
|
${p.description ? ` <p>${esc(p.description)}</p>\n` : ''} <cl-price code="${esc(p.sku)}"><cl-price-amount type="compare-at"></cl-price-amount> <cl-price-amount type="price"></cl-price-amount></cl-price>
|
|
23
26
|
<cl-availability code="${esc(p.sku)}"><cl-availability-status type="available">In stock</cl-availability-status><cl-availability-status type="unavailable">Sold out</cl-availability-status></cl-availability>
|
|
@@ -53,7 +56,7 @@ ${p.description ? ` <p>${esc(p.description)}</p>\n` : ''} <cl-pric
|
|
|
53
56
|
}
|
|
54
57
|
const options = store.markets.map((m) => ` <option value="${esc(m.code)}">${esc(m.name)} (${esc(m.currency)})</option>`).join('\n');
|
|
55
58
|
// What the product page shows, by sku. Prices and availability come live from drop-in.js.
|
|
56
|
-
const data = Object.fromEntries(store.products.map((p) => [p.sku, { name: p.name, description: p.description, image_url:
|
|
59
|
+
const data = Object.fromEntries(store.products.map((p) => [p.sku, { name: p.name, description: p.description, image_url: productImage(p), category: p.category }]));
|
|
57
60
|
const json = JSON.stringify(data, null, 2).replace(/</g, '\\u003c');
|
|
58
61
|
return `<!doctype html>
|
|
59
62
|
<html lang="en">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercelayer/agent-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "c11r: the Commerce Layer CLI for coding agents. Builds a sandbox store that people and shopping agents can buy from.",
|
|
5
5
|
"homepage": "https://commercelayer.ai",
|
|
6
6
|
"keywords": [
|