@greatstore/cli 0.1.1 → 0.1.3

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.
@@ -0,0 +1,252 @@
1
+ # `generateStructuredContent` deep dive
2
+
3
+ AI-generated, catalog-grounded JSON for UI you render yourself — the chat
4
+ panel is not involved.
5
+
6
+ ```js
7
+ const data = await window.GreatStore.generateStructuredContent(schema, prompt);
8
+ ```
9
+
10
+ - `schema` — a JSON Schema describing the output (or a Zod schema exposing
11
+ `.toJSONSchema()`).
12
+ - `prompt` — what to generate.
13
+ - Resolves to **the generated JSON object itself**, matching the schema.
14
+ Rejects with an `Error` on any failure.
15
+
16
+ Code samples here are framework-free reference implementations — re-express
17
+ them in the host repo's framework (React, Vue, Shopify Liquid, …) rather
18
+ than retrofitting them as-is.
19
+
20
+ ## The rules that make it work well
21
+
22
+ Each is unpacked in the sections below; this is the checklist.
23
+
24
+ 1. **Top level must be an object.** Want a list? Wrap it:
25
+ `{ type: "object", properties: { items: { type: "array", … } }, required: ["items"] }`.
26
+ 2. **Steer with the prompt, not schema `description`s** — free-text schema
27
+ fields are stripped before the AI sees them. Use self-explanatory
28
+ property names (`benefitHeadline`, not `text1`).
29
+ 3. **Only `require` what's guaranteed.** `required` is strictly enforced; if
30
+ the catalog can't ground a required field the whole call can fail. Require
31
+ structural fields, keep per-product details (image URLs, prices) optional,
32
+ and make rendering tolerate missing values.
33
+ 4. **Point, don't paste.** GreatStore researches the store's live catalog on
34
+ its own — name the entity (`` `…the product "${productName}" (SKU ${sku})` ``)
35
+ and let it look the facts up. Don't fetch specs/prices/descriptions
36
+ yourself and paste them into the prompt. The one thing it *can't* see is
37
+ your page, so page-only context (which page the shopper is on, what the
38
+ section is for) does belong in the prompt.
39
+ 5. **Research is bounded by the store's connectors — validate before you
40
+ build.** The AI can only look up what the store's MCP connectors actually
41
+ provide. Ask for something outside them — currency conversion, live
42
+ shipping rates, review data the store never wired up — and it has nothing
43
+ to ground on, so it will decline, omit… or hallucinate. Before writing a
44
+ prompt or schema that depends on a data capability, run `gs connectors`
45
+ (and `gs connectors health`, [store-admin.md](store-admin.md)) and confirm
46
+ a connector for it exists; if it doesn't, don't ask for it.
47
+ 6. **Responses are cached** for up to ~24h per page + prompt + schema —
48
+ shared across anonymous visitors, per-shopper for identified ones. Keep
49
+ prompts deterministic per page — no timestamps, random values, or
50
+ per-visitor data (GreatStore already knows who the shopper is; see below).
51
+ 7. **Progressive enhancement, always.** Generate after the page renders into
52
+ a hidden container, reveal on success, leave the fallback on error. Render
53
+ generated strings via `textContent`, never `innerHTML`.
54
+ 8. **One rich call beats many small ones** — there's a per-visitor rate limit
55
+ (~20 requests/minute); fetch multiple surfaces with one combined schema.
56
+
57
+ ## What actually happens
58
+
59
+ 1. The SDK posts your schema + prompt to the store's GreatStore endpoint,
60
+ along with the current **page URL and page title** (sent automatically —
61
+ you don't pass them, and you can't override them).
62
+ 2. GreatStore first **researches**: it looks up real data from the store's
63
+ live catalog (products, prices, availability, store info) using read-only
64
+ lookups. The page URL/title serve as hints about which product or category
65
+ to look up — they are *not* treated as a source of product data, and the
66
+ page's DOM is never read. Research happens on GreatStore's side — your
67
+ prompt only needs to *point* it at the right SKU, product, or collection,
68
+ not carry the material. Its reach is exactly the store's MCP connectors:
69
+ validate with `gs connectors` ([store-admin.md](store-admin.md)) that a
70
+ connector for the data you want actually exists before you build on it —
71
+ research can't exceed the wired-up connectors, and prompts that assume
72
+ otherwise invite hallucinated filler.
73
+ 3. The AI then fills your schema from the researched data, under a strict
74
+ grounding contract: it must not invent product names, prices, images, IDs,
75
+ or descriptions. Fields it can't ground are omitted or `null`. For an
76
+ identified shopper this step also sees their shopper profile — the same
77
+ identity the chat assistant has — so the output can be subtly personalized
78
+ without you passing anything about the visitor.
79
+ 4. The output is validated against your schema (with internal retries) before
80
+ being returned and cached.
81
+
82
+ GreatStore already knows who's reading: identity rides the request the same
83
+ way it does for chat, and responses for identified shoppers are cached just
84
+ for them (anonymous visitors share one entry). The practical consequence:
85
+ never put shopper data in the prompt — it's redundant, and it poisons the
86
+ cache key.
87
+
88
+ ## Schema support
89
+
90
+ Top level **must describe an object**: `type: "object"` (or a bare
91
+ `properties` / `anyOf`). To get a list, wrap it in an object property.
92
+
93
+ Supported keywords (anything else is tolerated but ignored):
94
+
95
+ - Types: `object`, `array`, `string`, `number`, `integer`, `boolean`, `null`
96
+ - Structure: `properties`, `required`, `items`, `additionalProperties`
97
+ - Choice: `enum`, `const`, `anyOf`, `nullable`
98
+ - Constraints: `minimum`, `maximum`, `minLength`, `maxLength`, `minItems`,
99
+ `maxItems`, `pattern`, `format`, `default`
100
+
101
+ Validation of the output is real: `required` is enforced, `enum`/`const`
102
+ must match, numeric and length bounds are checked, and
103
+ `additionalProperties: false` rejects extra keys. Constraints are therefore a
104
+ *tool* — `maxItems: 4` reliably caps a list, `enum` reliably restricts a
105
+ field — but every constraint is also a way for generation to fail, so apply
106
+ them only where you'd rather have no content than non-conforming content.
107
+
108
+ Zod schemas (or anything with a `.toJSONSchema()` method) are accepted and
109
+ converted automatically.
110
+
111
+ ### Free-text schema fields are stripped
112
+
113
+ `description`, `title`, and `example` are removed from the schema before the
114
+ AI sees it (they're a prompt-injection surface, so they're filtered
115
+ server-side). Consequences:
116
+
117
+ - Schema descriptions **cannot** steer generation. All steering lives in the
118
+ prompt string.
119
+ - Property *names* are the only in-schema signal of intent — make them
120
+ self-documenting: `ctaLabel`, `warmthRating`, `priceJustification`.
121
+
122
+ ## Prompting guide
123
+
124
+ The prompt is the entire instruction channel. A good prompt states, in order:
125
+
126
+ 1. **Context** — what page/situation the shopper is in, naming the entity so
127
+ research targets the right thing (the AI can't see your DOM, so identify
128
+ it explicitly):
129
+ `The shopper is viewing the product "Aurora Down Parka" (SKU AUR-021) on its product page.`
130
+ 2. **Task** — what to generate, mapped loosely onto your schema's fields:
131
+ `Write a heading and 3 reasons to love it; each reason has a short title and one supporting sentence.`
132
+ 3. **Grounding expectations** — what store data to draw on:
133
+ `Base every claim on the product's real materials, features, and price.`
134
+ 4. **Voice** — tone and constraints:
135
+ `Warm and concrete. No exclamation marks, no generic marketing filler.`
136
+
137
+ Note what's *not* in that prompt: no pasted specs, prices, or descriptions.
138
+ Pointing at the SKU is enough — GreatStore researches the rest itself, from
139
+ data that's live rather than whatever was true when you wrote the prompt.
140
+
141
+ Anti-patterns:
142
+
143
+ - **Pasting researched material into the prompt** (specs, prices,
144
+ descriptions you fetched from your platform's API). GreatStore researches
145
+ the live catalog itself — point it at the SKU/product/collection and let
146
+ it look things up. Pasted facts go stale, bloat the cache key, and compete
147
+ with the fresher data research returns.
148
+ - **Per-visitor or per-moment data in the prompt** (names, cart contents,
149
+ timestamps, `Math.random()`): destroys caching, so every visitor pays full
150
+ generation latency and the store pays for every call — and it's redundant,
151
+ because GreatStore already knows the shopper and personalizes for
152
+ identified ones server-side. If you need per-shopper *interaction*, that's
153
+ what `sendMessage` and the chat panel are for.
154
+ - **Asking for data no connector provides** ("convert the price to EUR",
155
+ "estimate delivery to the shopper's city"): research can't exceed the
156
+ store's MCP connectors, and the AI may hallucinate plausible-looking
157
+ values rather than leave the field empty. Validate first — `gs connectors`
158
+ shows what's wired up ([store-admin.md](store-admin.md)); if there's no
159
+ connector for it, don't ask for it.
160
+ - **Asking it to read the page** ("summarize the reviews shown below") — it
161
+ can't. Page-only data (review snippets, UGC, things that exist nowhere but
162
+ the DOM) is the one kind worth inlining — keep it stable per page so
163
+ caching still works.
164
+ - **Asking for minute-fresh operational data** (exact live stock counts,
165
+ delivery countdowns). Even when a connector could answer, responses cache
166
+ for up to ~24h — display operational data from your own platform APIs and
167
+ use GreatStore for *editorial intelligence over the catalog*.
168
+ - **Burying instructions in schema descriptions** — stripped, see above.
169
+
170
+ ## Caching: design for it
171
+
172
+ Responses are cached server-side for up to **24 hours**, keyed by the
173
+ combination of page URL + page title + prompt + schema. Anonymous visitors
174
+ share one entry per key; identified shoppers each get their own (their
175
+ output may be personalized, so it's only ever served back to them).
176
+ (Tracking query params like `utm_*`/`gclid` and the URL fragment are
177
+ ignored, so ad-tagged visits share the campaign-free page's cache entry.
178
+ Meaningful params like `?product=123` are part of the key.)
179
+
180
+ Practical consequences:
181
+
182
+ - **The first render pays, repeats fly.** Expect a few seconds on a cache
183
+ miss and near-instant responses after — shared across all anonymous
184
+ traffic, per-shopper for identified traffic (the research underneath is
185
+ cached briefly and shared, so even those misses are cheaper than cold).
186
+ Design loading states for the miss case.
187
+ - **Same call on different pages = different content**, automatically — the
188
+ page URL is in the key and in the AI's hints. A single site-wide snippet
189
+ with a constant prompt yields per-page content for free.
190
+ - **Content refreshes roughly daily.** Don't build experiences that assume
191
+ minute-level freshness.
192
+ - **To force different content, change the prompt or schema** (e.g. a
193
+ campaign variant string that changes weekly — deliberate, low-cardinality
194
+ variation is fine; per-visitor cardinality is not).
195
+
196
+ The catalog research underneath is also cached briefly, so several distinct
197
+ surfaces on the same page (different prompts/schemas) stay cheap even on
198
+ cold cache.
199
+
200
+ ## Errors and how to handle them
201
+
202
+ The promise rejects with `new Error(message)`. The message is
203
+ developer-facing — never render it to shoppers. Cases:
204
+
205
+ | Case | Message you'll see | Retry? |
206
+ |---|---|---|
207
+ | Bad input (empty prompt, non-object schema) | thrown immediately by the SDK | Fix the call |
208
+ | Invalid schema shape | `Invalid schema: …` | Fix the schema |
209
+ | AI declined the request | `The assistant declined to generate content for this request.` | No — permanent for that prompt/schema. Rework the prompt. |
210
+ | Output couldn't satisfy the schema | `Failed to produce valid structured content` | No — usually `required`/constraints demand data the catalog lacks. Loosen the schema. |
211
+ | Rate limit (~20/min per visitor) | rate-limit message | Later — and consolidate calls |
212
+ | Network / server | varies | Next page load |
213
+
214
+ The uniform shopper-facing strategy: render into a hidden-by-default
215
+ container, reveal on success, leave hidden (or show your static fallback) on
216
+ any rejection. One `try/catch`, no case analysis needed unless you're
217
+ logging.
218
+
219
+ ## Performance pattern
220
+
221
+ Fire generation as early as possible without blocking render — top of your
222
+ deferred script, before other work:
223
+
224
+ ```js
225
+ const highlightsPromise = window.GreatStore?.generateStructuredContent
226
+ ? window.GreatStore.generateStructuredContent(schema, prompt).catch(() => null)
227
+ : Promise.resolve(null);
228
+
229
+ // …rest of page setup…
230
+
231
+ const data = await highlightsPromise;
232
+ if (data) renderHighlights(data);
233
+ ```
234
+
235
+ The `.catch(() => null)` attached immediately avoids unhandled-rejection
236
+ noise while keeping a single render path.
237
+
238
+ For multiple surfaces on one page, prefer **one call with a combined
239
+ schema** over parallel calls — it's one research pass, one cache entry, and
240
+ no rate-limit pressure:
241
+
242
+ ```js
243
+ const schema = {
244
+ type: "object",
245
+ properties: {
246
+ highlights: { /* … */ },
247
+ faq: { /* … */ },
248
+ crossSell: { /* … */ },
249
+ },
250
+ required: ["highlights"],
251
+ };
252
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatstore/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "CLI for administering GreatStore stores and authoring custom components.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",