@bacano/sdk 1.0.1 → 1.1.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.
Files changed (68) hide show
  1. package/README.md +342 -7
  2. package/STOREFRONT_CLARITY.md +140 -0
  3. package/STOREFRONT_CONSTRUCTION.md +92 -0
  4. package/STOREFRONT_LEADS.md +130 -0
  5. package/STOREFRONT_LEGAL.md +66 -0
  6. package/STOREFRONT_MARKETING.md +286 -0
  7. package/STOREFRONT_PERFORMANCE.md +265 -0
  8. package/STOREFRONT_PREVIEW.md +474 -0
  9. package/client.d.ts +7 -1
  10. package/errors.d.ts +9 -1
  11. package/graphql.cjs +63 -0
  12. package/graphql.d.ts +2 -2
  13. package/graphql.js +63 -0
  14. package/index.cjs +1542 -192
  15. package/index.d.ts +8 -6
  16. package/index.js +1542 -192
  17. package/marketing/attribution.d.ts +51 -0
  18. package/marketing/clarity.d.ts +3 -0
  19. package/marketing/domain.d.ts +1 -0
  20. package/marketing/ga4.d.ts +2 -0
  21. package/marketing/index.d.ts +5 -0
  22. package/marketing/runtime.d.ts +2 -0
  23. package/marketing/sanitize.d.ts +2 -0
  24. package/marketing/types.d.ts +109 -0
  25. package/media.d.ts +45 -2
  26. package/modules/auth.d.ts +3 -2
  27. package/modules/availability.d.ts +2 -2
  28. package/modules/cart-session.d.ts +2 -2
  29. package/modules/cart.d.ts +3 -3
  30. package/modules/catalog.d.ts +15 -3
  31. package/modules/content.d.ts +2 -2
  32. package/modules/marketing.d.ts +5 -0
  33. package/modules/orders.d.ts +2 -2
  34. package/modules/profile.d.ts +1 -1
  35. package/modules/static-catalog.d.ts +13 -0
  36. package/modules/website.d.ts +7 -1
  37. package/package.json +14 -3
  38. package/react/context-registry.d.ts +9 -0
  39. package/react/context.d.ts +1 -1
  40. package/react/hooks.d.ts +23 -2
  41. package/react/marketing-context.d.ts +3 -0
  42. package/react/marketing-provider.d.ts +10 -0
  43. package/react/preview/cache.d.ts +23 -0
  44. package/react/preview/context.d.ts +5 -0
  45. package/react/preview/navigation.d.ts +9 -0
  46. package/react/preview/provider.d.ts +3 -0
  47. package/react/preview/shell.d.ts +3 -0
  48. package/react/preview/toolbar.d.ts +3 -0
  49. package/react/preview/types.d.ts +60 -0
  50. package/react/preview.cjs +742 -0
  51. package/react/preview.d.ts +14 -0
  52. package/react/preview.js +730 -0
  53. package/react/website-gate.d.ts +22 -0
  54. package/react.cjs +3432 -1780
  55. package/react.d.ts +8 -4
  56. package/react.js +3428 -1772
  57. package/skills/bacano-web/SKILL.md +109 -0
  58. package/skills/bacano-web/agents/openai.yaml +4 -0
  59. package/skills/bacano-web/references/common.md +91 -0
  60. package/skills/bacano-web/references/informational.md +39 -0
  61. package/skills/bacano-web/references/report.md +52 -0
  62. package/skills/bacano-web/references/storefront.md +116 -0
  63. package/skills/bacano-web/scripts/audit-http.mjs +247 -0
  64. package/skills/bacano-web/scripts/cli.mjs +222 -0
  65. package/skills/bacano-web/scripts/postinstall.mjs +8 -0
  66. package/transport/documents.d.ts +8 -6
  67. package/transport/graphql.d.ts +13 -1
  68. package/types.d.ts +202 -0
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from "node:crypto";
4
+ import {
5
+ cpSync,
6
+ existsSync,
7
+ lstatSync,
8
+ mkdirSync,
9
+ readFileSync,
10
+ readlinkSync,
11
+ rmSync,
12
+ symlinkSync,
13
+ } from "node:fs";
14
+ import { dirname, relative, resolve } from "node:path";
15
+ import { createInterface } from "node:readline/promises";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const scriptDirectory = dirname(fileURLToPath(import.meta.url));
19
+ const sourceSkill = resolve(scriptDirectory, "..");
20
+ const sourceSkillFile = resolve(sourceSkill, "SKILL.md");
21
+ const projectRoot = process.cwd();
22
+ const canonicalSkill = resolve(projectRoot, ".agents", "skills", "bacano-web");
23
+
24
+ const adapterPaths = {
25
+ antigravity: canonicalSkill,
26
+ claude: resolve(projectRoot, ".claude", "skills", "bacano-web"),
27
+ codex: resolve(projectRoot, ".codex", "skills", "bacano-web"),
28
+ };
29
+
30
+ function usage() {
31
+ console.log(`Bacano Web readiness skill
32
+
33
+ Usage:
34
+ bacano-web install [--agent antigravity|claude|codex|all] [--force]
35
+ bacano-web check
36
+
37
+ The canonical project copy lives at .agents/skills/bacano-web.
38
+ Claude and Codex receive links to that same copy.`);
39
+ }
40
+
41
+ function option(name) {
42
+ const index = process.argv.indexOf(name);
43
+ return index >= 0 ? process.argv[index + 1] : null;
44
+ }
45
+
46
+ function hash(file) {
47
+ return createHash("sha256").update(readFileSync(file)).digest("hex");
48
+ }
49
+
50
+ function skillFile(directory) {
51
+ return resolve(directory, "SKILL.md");
52
+ }
53
+
54
+ function relativeDisplay(path) {
55
+ const value = relative(projectRoot, path);
56
+ return value || ".";
57
+ }
58
+
59
+ function removeExisting(path, force) {
60
+ if (!existsSync(path)) return;
61
+ if (!force) {
62
+ throw new Error(
63
+ `${relativeDisplay(path)} ya existe. Usa --force para reemplazarlo.`,
64
+ );
65
+ }
66
+ rmSync(path, { recursive: true, force: true });
67
+ }
68
+
69
+ function installCanonical(force) {
70
+ if (resolve(sourceSkill) === canonicalSkill) return;
71
+
72
+ if (existsSync(skillFile(canonicalSkill))) {
73
+ if (hash(skillFile(canonicalSkill)) === hash(sourceSkillFile)) return;
74
+ removeExisting(canonicalSkill, force);
75
+ } else if (existsSync(canonicalSkill)) {
76
+ removeExisting(canonicalSkill, force);
77
+ }
78
+
79
+ mkdirSync(dirname(canonicalSkill), { recursive: true });
80
+ cpSync(sourceSkill, canonicalSkill, { recursive: true });
81
+ }
82
+
83
+ function installAdapter(agent, force) {
84
+ const target = adapterPaths[agent];
85
+ if (!target || target === canonicalSkill) return;
86
+
87
+ if (existsSync(target)) {
88
+ const stat = lstatSync(target);
89
+ if (stat.isSymbolicLink()) {
90
+ const resolvedLink = resolve(dirname(target), readlinkSync(target));
91
+ if (resolvedLink === canonicalSkill) return;
92
+ }
93
+ removeExisting(target, force);
94
+ }
95
+
96
+ mkdirSync(dirname(target), { recursive: true });
97
+ const targetReference =
98
+ process.platform === "win32"
99
+ ? canonicalSkill
100
+ : relative(dirname(target), canonicalSkill);
101
+ symlinkSync(
102
+ targetReference,
103
+ target,
104
+ process.platform === "win32" ? "junction" : "dir",
105
+ );
106
+ }
107
+
108
+ async function chooseAgent() {
109
+ const explicit = option("--agent");
110
+ if (explicit) return explicit;
111
+
112
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
113
+ throw new Error(
114
+ "Indica --agent antigravity, --agent claude, --agent codex o --agent all.",
115
+ );
116
+ }
117
+
118
+ const prompt = createInterface({
119
+ input: process.stdin,
120
+ output: process.stdout,
121
+ });
122
+ const answer = await prompt.question(
123
+ "¿Qué agente usará la skill? [1] Antigravity [2] Claude Code [3] Codex [4] Todos: ",
124
+ );
125
+ prompt.close();
126
+
127
+ return (
128
+ { 1: "antigravity", 2: "claude", 3: "codex", 4: "all" }[answer.trim()] ??
129
+ answer.trim().toLowerCase()
130
+ );
131
+ }
132
+
133
+ function validateAgent(agent) {
134
+ if (!["antigravity", "claude", "codex", "all"].includes(agent)) {
135
+ throw new Error(`Agente no reconocido: ${agent || "(vacío)"}.`);
136
+ }
137
+ }
138
+
139
+ async function install() {
140
+ const agent = await chooseAgent();
141
+ validateAgent(agent);
142
+ const force = process.argv.includes("--force");
143
+
144
+ installCanonical(force);
145
+ if (agent === "all") {
146
+ installAdapter("claude", force);
147
+ installAdapter("codex", force);
148
+ } else {
149
+ installAdapter(agent, force);
150
+ }
151
+
152
+ console.log("Skill Bacano Web instalada.");
153
+ console.log(`Fuente canónica: ${relativeDisplay(canonicalSkill)}`);
154
+ if (agent === "claude" || agent === "all") {
155
+ console.log(`Claude Code: ${relativeDisplay(adapterPaths.claude)}`);
156
+ }
157
+ if (agent === "codex" || agent === "all") {
158
+ console.log(`Codex: ${relativeDisplay(adapterPaths.codex)}`);
159
+ }
160
+ console.log("Ejecuta bacano-web check para verificarla.");
161
+ }
162
+
163
+ function checkPath(name, path) {
164
+ if (!existsSync(path))
165
+ return { name, path: relativeDisplay(path), status: "missing" };
166
+ const file = skillFile(path);
167
+ if (!existsSync(file)) {
168
+ return { name, path: relativeDisplay(path), status: "invalid" };
169
+ }
170
+ return {
171
+ name,
172
+ path: relativeDisplay(path),
173
+ status: hash(file) === hash(sourceSkillFile) ? "current" : "outdated",
174
+ link: lstatSync(path).isSymbolicLink(),
175
+ };
176
+ }
177
+
178
+ function check() {
179
+ const results = [
180
+ checkPath("canonical", canonicalSkill),
181
+ checkPath("antigravity", adapterPaths.antigravity),
182
+ checkPath("claude", adapterPaths.claude),
183
+ checkPath("codex", adapterPaths.codex),
184
+ ];
185
+ const unique = results.filter(
186
+ (result, index) =>
187
+ results.findIndex((candidate) => candidate.path === result.path) ===
188
+ index,
189
+ );
190
+
191
+ console.table(unique);
192
+ const canonical = results[0];
193
+ if (canonical.status !== "current") {
194
+ console.error(
195
+ "Bacano Web no está instalada o está desactualizada. Ejecuta bacano-web install.",
196
+ );
197
+ process.exitCode = 1;
198
+ return;
199
+ }
200
+
201
+ const adapters = results
202
+ .slice(2)
203
+ .filter((result) => result.status !== "missing");
204
+ if (adapters.some((result) => !result.link)) {
205
+ console.warn(
206
+ "Se detectaron copias físicas en adaptadores. Reinstala con --force para evitar divergencias.",
207
+ );
208
+ }
209
+ if (unique.some((result) => result.status === "outdated"))
210
+ process.exitCode = 1;
211
+ }
212
+
213
+ const command = process.argv[2] ?? "help";
214
+
215
+ try {
216
+ if (command === "install") await install();
217
+ else if (command === "check") check();
218
+ else usage();
219
+ } catch (error) {
220
+ console.error(error instanceof Error ? error.message : String(error));
221
+ process.exitCode = 1;
222
+ }
@@ -0,0 +1,8 @@
1
+ if (!process.env.CI && process.env.BACANO_SDK_HIDE_SKILL_NOTICE !== "1") {
2
+ console.log(`
3
+ @bacano/sdk incluye la skill Bacano Web para evaluar sitios antes de producción.
4
+ Instálala una vez: pnpm exec bacano-web install --agent all
5
+ Verifica su estado: pnpm exec bacano-web check
6
+ Luego invócala desde tu agente para iniciar la auditoría.
7
+ `);
8
+ }
@@ -12,13 +12,14 @@ export declare const WEBSITE_GET_PRODUCT_RECOMMENDATIONS = "\n mutation Website
12
12
  export declare const GET_BRANDS = "\n query GetBrands($companyId: uuid!, $chainId: uuid!) {\n brands(\n where: {\n company_id: { _eq: $companyId }\n products: {\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n order_by: { name: asc }\n ) {\n id\n name\n }\n }\n";
13
13
  export declare const WEBSITE_GET_CONTENT = "\n mutation WebsiteGetContent(\n $websiteId: uuid!\n $key: String\n $categoryKey: String\n $tag: String\n $locale: String\n ) {\n result: websiteGetContent(\n website_id: $websiteId\n key: $key\n category_key: $categoryKey\n tag: $tag\n locale: $locale\n ) {\n success\n errors\n entries {\n \n id\n key\n category_key\n name\n locale\n status\n tags\n content_json\n resolved_fields\n published_at\n updated_at\n content_type {\n id\n key\n name\n kind\n }\n\n }\n }\n }\n";
14
14
  export declare const WEBSITE_GET_PRODUCT_CONTENT = "\n mutation WebsiteGetProductContent(\n $websiteId: uuid!\n $productId: uuid!\n $locale: String\n ) {\n result: websiteGetProductContent(\n website_id: $websiteId\n product_id: $productId\n locale: $locale\n ) {\n success\n errors\n blocks {\n id\n product_id\n locale\n content_json\n resolved_fields\n sort_order\n updated_at\n content_type {\n id\n key\n name\n kind\n }\n }\n }\n }\n";
15
- export declare const CHECK_AVAILABILITY = "\n query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n ) {\n product_variant_id\n in_stock\n label\n }\n }\n";
15
+ export declare const CHECK_AVAILABILITY = "\n query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n ) {\n product_variant_id\n in_stock\n label\n max_purchasable_quantity\n }\n }\n";
16
+ export declare const GET_PRODUCT_LIVE_STATE = "\n query GetProductLiveState(\n $variantIds: [uuid!]!\n $companyId: uuid!\n $chainId: uuid!\n $websiteId: uuid!\n ) {\n product_variants(\n where: {\n id: { _in: $variantIds }\n is_disabled: { _eq: false }\n product: {\n company_id: { _eq: $companyId }\n product_website_publications: {\n website_id: { _eq: $websiteId }\n is_visible: { _eq: true }\n }\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n ) {\n id\n product_variant_prices(\n where: {\n price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }\n _or: [\n { chain_id: { _eq: $chainId } }\n { chain_id: { _is_null: true } }\n ]\n }\n ) {\n id\n price\n price_type\n chain_id\n }\n }\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n ) {\n product_variant_id\n in_stock\n label\n max_purchasable_quantity\n }\n }\n";
16
17
  export declare const CREATE_CART = "\n mutation CreateCart(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput\n $fulfillmentFamily: String\n ) {\n websiteCreateCart(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
17
18
  export declare const GET_CART = "\n mutation GetCart($orderId: uuid!, $cartToken: String!) {\n websiteGetCart(order_id: $orderId, cart_token: $cartToken) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
18
- export declare const START_GUEST_CHECKOUT = "\n mutation StartGuestCheckout(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput!\n $items: [WebsiteCheckoutItemInput!]!\n $fulfillmentFamily: String\n ) {\n websiteStartGuestCheckout(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n items: $items\n fulfillment_family: $fulfillmentFamily\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
19
+ export declare const START_GUEST_CHECKOUT = "\n mutation StartGuestCheckout(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput!\n $items: [WebsiteCheckoutItemInput!]!\n $fulfillmentFamily: String\n $marketingAttribution: WebsiteMarketingAttributionInput\n ) {\n websiteStartGuestCheckout(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n items: $items\n fulfillment_family: $fulfillmentFamily\n marketing_attribution: $marketingAttribution\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
19
20
  export declare const SET_CART_ITEM = "\n mutation SetCartItem(\n $orderId: uuid!\n $cartToken: String!\n $variantId: uuid!\n $quantity: numeric!\n ) {\n websiteSetCartItem(\n order_id: $orderId\n cart_token: $cartToken\n product_variant_id: $variantId\n quantity: $quantity\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
20
21
  export declare const REMOVE_CART_ITEM = "\n mutation RemoveCartItem(\n $orderId: uuid!\n $cartToken: String!\n $itemId: uuid!\n ) {\n websiteRemoveCartItem(\n order_id: $orderId\n cart_token: $cartToken\n item_id: $itemId\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
21
- export declare const CHECKOUT = "\n mutation Checkout(\n $orderId: uuid!\n $cartToken: String!\n $contact: WebsiteGuestContactInput!\n $fulfillmentFamily: String\n $address: WebsiteAddressInput\n $shippingQuoteId: uuid\n $paymentMode: String\n $paymentMethodId: uuid\n $buyerAccountOptIn: Boolean\n $initialPayment: WebsiteInitialPaymentInput\n ) {\n websiteCheckout(\n order_id: $orderId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n address: $address\n shipping_quote_id: $shippingQuoteId\n payment_mode: $paymentMode\n payment_method_id: $paymentMethodId\n buyer_account_opt_in: $buyerAccountOptIn\n initial_payment: $initialPayment\n ) {\n success\n order_id\n order_number\n payment_method_id\n public_order_token\n requires_payment_session\n buyer_account_invitation_requested\n errors\n }\n }\n";
22
+ export declare const CHECKOUT = "\n mutation Checkout(\n $orderId: uuid!\n $cartToken: String!\n $contact: WebsiteGuestContactInput!\n $fulfillmentFamily: String\n $address: WebsiteAddressInput\n $shippingQuoteId: uuid\n $paymentMode: String\n $paymentMethodId: uuid\n $buyerAccountOptIn: Boolean\n $initialPayment: WebsiteInitialPaymentInput\n $marketingAttribution: WebsiteMarketingAttributionInput\n $advertisingConsent: WebsiteAdvertisingConsentInput\n ) {\n websiteCheckout(\n order_id: $orderId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n address: $address\n shipping_quote_id: $shippingQuoteId\n payment_mode: $paymentMode\n payment_method_id: $paymentMethodId\n buyer_account_opt_in: $buyerAccountOptIn\n initial_payment: $initialPayment\n marketing_attribution: $marketingAttribution\n advertising_consent: $advertisingConsent\n ) {\n success\n order_id\n order_number\n payment_method_id\n public_order_token\n requires_payment_session\n buyer_account_invitation_requested\n errors\n }\n }\n";
22
23
  export declare const GET_LOCATION_OPTIONS = "\n mutation GetLocationOptions($websiteId: uuid!) {\n websiteGetLocationOptions(website_id: $websiteId) {\n success\n country_code\n departments {\n department_id\n department_name\n department_code\n municipalities {\n municipality_id\n municipality_name\n municipality_code\n department_id\n department_code\n }\n }\n errors\n }\n }\n";
23
24
  export declare const GET_DELIVERY_OPTIONS = "\n mutation GetDeliveryOptions($websiteId: uuid!) {\n websiteGetDeliveryOptions(website_id: $websiteId) {\n success\n delivery_options {\n id\n fulfillment_family\n label\n description\n is_enabled\n pickup_info {\n label\n address\n schedule\n notes\n }\n }\n errors\n }\n }\n";
24
25
  export declare const GET_SHIPPING_QUOTES = "\n mutation GetShippingQuotes(\n $websiteId: uuid!\n $orderId: uuid\n $cartToken: String\n $items: [WebsiteCheckoutItemInput!]\n $address: WebsiteAddressInput!\n ) {\n websiteGetShippingQuotes(\n website_id: $websiteId\n order_id: $orderId\n cart_token: $cartToken\n items: $items\n address: $address\n ) {\n success\n quotes {\n quote_id\n shipping_service_id\n label\n service_name\n base_amount\n amount\n currency\n collection_mode\n total_policy\n estimated_delivery\n free_shipping_eligible\n free_shipping_applied\n free_shipping_threshold\n free_shipping_remaining\n free_shipping_eligible_subtotal\n }\n errors\n }\n }\n";
@@ -53,17 +54,18 @@ export declare const documents: {
53
54
  readonly WEBSITE_GET_CATALOG_PAGE: "\n mutation WebsiteGetCatalogPage(\n $websiteId: uuid!\n $categoryListKey: String!\n $attributeListKey: String!\n $filters: WebsiteCatalogPageFiltersInput\n $include: WebsiteCatalogPageIncludeInput\n ) {\n result: websiteGetCatalogPage(\n website_id: $websiteId\n category_list_key: $categoryListKey\n attribute_list_key: $attributeListKey\n filters: $filters\n include: $include\n ) {\n success\n products\n category_list\n attribute_list\n filters\n brands\n pagination\n applied_filters\n debug\n errors\n }\n }\n";
54
55
  readonly WEBSITE_GET_PRODUCT_RECOMMENDATIONS: "\n mutation WebsiteGetProductRecommendations(\n $websiteId: uuid!\n $productId: uuid!\n $limit: Int\n ) {\n result: websiteGetProductRecommendations(\n website_id: $websiteId\n product_id: $productId\n limit: $limit\n ) {\n success\n products\n strategy\n errors\n }\n }\n";
55
56
  readonly GET_BRANDS: "\n query GetBrands($companyId: uuid!, $chainId: uuid!) {\n brands(\n where: {\n company_id: { _eq: $companyId }\n products: {\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n order_by: { name: asc }\n ) {\n id\n name\n }\n }\n";
56
- readonly CHECK_AVAILABILITY: "\n query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n ) {\n product_variant_id\n in_stock\n label\n }\n }\n";
57
+ readonly CHECK_AVAILABILITY: "\n query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n ) {\n product_variant_id\n in_stock\n label\n max_purchasable_quantity\n }\n }\n";
58
+ readonly GET_PRODUCT_LIVE_STATE: "\n query GetProductLiveState(\n $variantIds: [uuid!]!\n $companyId: uuid!\n $chainId: uuid!\n $websiteId: uuid!\n ) {\n product_variants(\n where: {\n id: { _in: $variantIds }\n is_disabled: { _eq: false }\n product: {\n company_id: { _eq: $companyId }\n product_website_publications: {\n website_id: { _eq: $websiteId }\n is_visible: { _eq: true }\n }\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n ) {\n id\n product_variant_prices(\n where: {\n price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }\n _or: [\n { chain_id: { _eq: $chainId } }\n { chain_id: { _is_null: true } }\n ]\n }\n ) {\n id\n price\n price_type\n chain_id\n }\n }\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n ) {\n product_variant_id\n in_stock\n label\n max_purchasable_quantity\n }\n }\n";
57
59
  readonly GET_PRODUCT_FILTERS: "\n query GetProductFilters(\n $companyId: uuid!\n $chainId: uuid!\n $where: products_bool_exp = {}\n $attributeListKey: String!\n ) {\n products(\n where: {\n _and: [\n { company_id: { _eq: $companyId } }\n {\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n $where\n ]\n }\n ) {\n id\n category\n subcategory\n color\n brand {\n id\n name\n }\n product_attributes {\n id\n value\n attribute_value {\n id\n code\n sort_order\n metadata\n }\n attribute {\n id\n name\n }\n }\n product_variants(where: { is_disabled: { _eq: false } }) {\n id\n product_variant_prices(\n where: {\n price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }\n _or: [\n { chain_id: { _eq: $chainId } }\n { chain_id: { _is_null: true } }\n ]\n }\n ) {\n id\n price\n price_type\n chain_id\n }\n product_variant_attributes(order_by: { order: asc }) {\n id\n value\n order\n attribute_value {\n id\n code\n sort_order\n metadata\n }\n attribute {\n id\n name\n }\n }\n }\n }\n catalog_attribute_lists(\n where: {\n company_id: { _eq: $companyId }\n key: { _eq: $attributeListKey }\n status: { _eq: published }\n }\n limit: 1\n ) {\n id\n key\n name\n description\n status\n items(\n where: { is_visible: { _eq: true } }\n order_by: [{ sort_order: asc }, { public_label: asc }]\n ) {\n id\n attribute_id\n public_label\n public_slug\n sort_order\n attribute {\n id\n name\n type\n }\n values(\n where: { is_visible: { _eq: true } }\n order_by: [{ sort_order: asc }, { public_label: asc }]\n ) {\n id\n attribute_value_id\n public_label\n public_slug\n sort_order\n attribute_value {\n id\n code\n metadata\n }\n }\n }\n }\n }\n";
58
60
  readonly GET_SEARCH_SUGGESTIONS: "\n query GetSearchSuggestions(\n $companyId: uuid!\n $chainId: uuid!\n $term: String!\n $productLimit: Int = 5\n $brandLimit: Int = 3\n ) {\n products(\n where: {\n company_id: { _eq: $companyId }\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n name: { _ilike: $term }\n }\n limit: $productLimit\n order_by: { name: asc }\n ) {\n id\n name\n }\n brands(\n where: {\n company_id: { _eq: $companyId }\n name: { _ilike: $term }\n products: {\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n limit: $brandLimit\n order_by: { name: asc }\n ) {\n id\n name\n }\n }\n";
59
61
  readonly WEBSITE_GET_CONTENT: "\n mutation WebsiteGetContent(\n $websiteId: uuid!\n $key: String\n $categoryKey: String\n $tag: String\n $locale: String\n ) {\n result: websiteGetContent(\n website_id: $websiteId\n key: $key\n category_key: $categoryKey\n tag: $tag\n locale: $locale\n ) {\n success\n errors\n entries {\n \n id\n key\n category_key\n name\n locale\n status\n tags\n content_json\n resolved_fields\n published_at\n updated_at\n content_type {\n id\n key\n name\n kind\n }\n\n }\n }\n }\n";
60
62
  readonly WEBSITE_GET_PRODUCT_CONTENT: "\n mutation WebsiteGetProductContent(\n $websiteId: uuid!\n $productId: uuid!\n $locale: String\n ) {\n result: websiteGetProductContent(\n website_id: $websiteId\n product_id: $productId\n locale: $locale\n ) {\n success\n errors\n blocks {\n id\n product_id\n locale\n content_json\n resolved_fields\n sort_order\n updated_at\n content_type {\n id\n key\n name\n kind\n }\n }\n }\n }\n";
61
63
  readonly CREATE_CART: "\n mutation CreateCart(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput\n $fulfillmentFamily: String\n ) {\n websiteCreateCart(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
62
64
  readonly GET_CART: "\n mutation GetCart($orderId: uuid!, $cartToken: String!) {\n websiteGetCart(order_id: $orderId, cart_token: $cartToken) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
63
- readonly START_GUEST_CHECKOUT: "\n mutation StartGuestCheckout(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput!\n $items: [WebsiteCheckoutItemInput!]!\n $fulfillmentFamily: String\n ) {\n websiteStartGuestCheckout(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n items: $items\n fulfillment_family: $fulfillmentFamily\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
65
+ readonly START_GUEST_CHECKOUT: "\n mutation StartGuestCheckout(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput!\n $items: [WebsiteCheckoutItemInput!]!\n $fulfillmentFamily: String\n $marketingAttribution: WebsiteMarketingAttributionInput\n ) {\n websiteStartGuestCheckout(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n items: $items\n fulfillment_family: $fulfillmentFamily\n marketing_attribution: $marketingAttribution\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
64
66
  readonly SET_CART_ITEM: "\n mutation SetCartItem(\n $orderId: uuid!\n $cartToken: String!\n $variantId: uuid!\n $quantity: numeric!\n ) {\n websiteSetCartItem(\n order_id: $orderId\n cart_token: $cartToken\n product_variant_id: $variantId\n quantity: $quantity\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
65
67
  readonly REMOVE_CART_ITEM: "\n mutation RemoveCartItem(\n $orderId: uuid!\n $cartToken: String!\n $itemId: uuid!\n ) {\n websiteRemoveCartItem(\n order_id: $orderId\n cart_token: $cartToken\n item_id: $itemId\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n items {\n id\n product_variant_id\n quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
66
- readonly CHECKOUT: "\n mutation Checkout(\n $orderId: uuid!\n $cartToken: String!\n $contact: WebsiteGuestContactInput!\n $fulfillmentFamily: String\n $address: WebsiteAddressInput\n $shippingQuoteId: uuid\n $paymentMode: String\n $paymentMethodId: uuid\n $buyerAccountOptIn: Boolean\n $initialPayment: WebsiteInitialPaymentInput\n ) {\n websiteCheckout(\n order_id: $orderId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n address: $address\n shipping_quote_id: $shippingQuoteId\n payment_mode: $paymentMode\n payment_method_id: $paymentMethodId\n buyer_account_opt_in: $buyerAccountOptIn\n initial_payment: $initialPayment\n ) {\n success\n order_id\n order_number\n payment_method_id\n public_order_token\n requires_payment_session\n buyer_account_invitation_requested\n errors\n }\n }\n";
68
+ readonly CHECKOUT: "\n mutation Checkout(\n $orderId: uuid!\n $cartToken: String!\n $contact: WebsiteGuestContactInput!\n $fulfillmentFamily: String\n $address: WebsiteAddressInput\n $shippingQuoteId: uuid\n $paymentMode: String\n $paymentMethodId: uuid\n $buyerAccountOptIn: Boolean\n $initialPayment: WebsiteInitialPaymentInput\n $marketingAttribution: WebsiteMarketingAttributionInput\n $advertisingConsent: WebsiteAdvertisingConsentInput\n ) {\n websiteCheckout(\n order_id: $orderId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n address: $address\n shipping_quote_id: $shippingQuoteId\n payment_mode: $paymentMode\n payment_method_id: $paymentMethodId\n buyer_account_opt_in: $buyerAccountOptIn\n initial_payment: $initialPayment\n marketing_attribution: $marketingAttribution\n advertising_consent: $advertisingConsent\n ) {\n success\n order_id\n order_number\n payment_method_id\n public_order_token\n requires_payment_session\n buyer_account_invitation_requested\n errors\n }\n }\n";
67
69
  readonly GET_LOCATION_OPTIONS: "\n mutation GetLocationOptions($websiteId: uuid!) {\n websiteGetLocationOptions(website_id: $websiteId) {\n success\n country_code\n departments {\n department_id\n department_name\n department_code\n municipalities {\n municipality_id\n municipality_name\n municipality_code\n department_id\n department_code\n }\n }\n errors\n }\n }\n";
68
70
  readonly GET_DELIVERY_OPTIONS: "\n mutation GetDeliveryOptions($websiteId: uuid!) {\n websiteGetDeliveryOptions(website_id: $websiteId) {\n success\n delivery_options {\n id\n fulfillment_family\n label\n description\n is_enabled\n pickup_info {\n label\n address\n schedule\n notes\n }\n }\n errors\n }\n }\n";
69
71
  readonly GET_SHIPPING_POLICY: "\n mutation GetShippingPolicy($websiteId: uuid!) {\n websiteGetShippingPolicy(website_id: $websiteId) {\n success\n policy {\n free_shipping_enabled\n free_shipping_threshold\n currency\n }\n errors\n }\n }\n";
@@ -1,7 +1,19 @@
1
1
  export type RequestHeaders = Record<string, string>;
2
2
  export type RequestHeadersProvider = () => RequestHeaders | Promise<RequestHeaders>;
3
+ export interface GraphQLRetryOptions {
4
+ /** Total attempts, including the initial request. */
5
+ maxAttempts?: number;
6
+ /** Maximum cumulative wait accepted from the server. */
7
+ maxTotalWaitMs?: number;
8
+ /** Adds delay without ever retrying before Retry-After. */
9
+ jitterRatio?: number;
10
+ }
11
+ export interface GraphQLRequestOptions {
12
+ /** Disabled by default. Enable only for idempotent reads. */
13
+ retry?: GraphQLRetryOptions;
14
+ }
3
15
  /**
4
16
  * Minimal fetch-based GraphQL executor.
5
17
  * Zero dependencies — uses the native `fetch` API.
6
18
  */
7
- export declare function executeGraphQL<T = Record<string, unknown>>(url: string, query: string, variables?: Record<string, unknown>, headers?: RequestHeaders | RequestHeadersProvider): Promise<T>;
19
+ export declare function executeGraphQL<T = Record<string, unknown>>(url: string, query: string, variables?: Record<string, unknown>, headers?: RequestHeaders | RequestHeadersProvider, options?: GraphQLRequestOptions): Promise<T>;
package/types.d.ts CHANGED
@@ -1,8 +1,14 @@
1
+ import type { WebsiteAdvertisingConsent, WebsiteMarketingAttribution } from "./marketing/types";
1
2
  export interface BacanoClientConfig {
2
3
  /** Bacano public API URL (e.g., https://api.bacanoerp.com) */
3
4
  apiUrl: string;
4
5
  /** Website slug — required tenant context for every storefront request. */
5
6
  websiteSlug: string;
7
+ /**
8
+ * Browser-safe website context already returned by `public-config`.
9
+ * Passing it avoids resolving the same website again through GraphQL.
10
+ */
11
+ initialContext?: BacanoClientContext;
6
12
  /** Cart session storage strategy (default: 'localStorage') */
7
13
  tokenStorage?: "localStorage" | "cookie" | "memory";
8
14
  /**
@@ -18,12 +24,134 @@ export type AccessTokenProvider = () => string | null | Promise<string | null>;
18
24
  export interface WebsitePublicConfig {
19
25
  websiteId: string;
20
26
  companyId: string;
27
+ /** Versioned marker for the website context embedded in public-config. */
28
+ contextSchemaVersion?: 1 | null;
29
+ chainId?: string | null;
21
30
  slug: string;
22
31
  name: string;
23
32
  domain: string | null;
33
+ config?: Record<string, unknown> | null;
24
34
  buyerAccountsEnabled: boolean;
25
35
  clerkPublishableKey: string | null;
26
36
  customerIdentification: WebsiteCustomerIdentificationPolicy;
37
+ marketing: WebsiteMarketingPublicConfig;
38
+ /**
39
+ * Optional on caller-constructed configs for compatibility with SDK 1.0.10.
40
+ * `getWebsitePublicConfig()` always returns this block.
41
+ */
42
+ legal?: WebsiteLegalPublicConfig;
43
+ /**
44
+ * Optional on caller-constructed configs. `getWebsitePublicConfig()` always
45
+ * resolves this block, including a safe disabled fallback.
46
+ */
47
+ construction?: WebsiteConstructionPublicConfig;
48
+ }
49
+ export interface WebsiteConstructionMedia {
50
+ fileId: string;
51
+ name: string | null;
52
+ altText: string | null;
53
+ width: number | null;
54
+ height: number | null;
55
+ originalUrl: string;
56
+ heroUrl: string;
57
+ responsiveSources: Array<{
58
+ width: number;
59
+ url: string;
60
+ }>;
61
+ }
62
+ export interface WebsiteConstructionPublicConfig {
63
+ schemaVersion: 1;
64
+ enabled: boolean;
65
+ /** True only on the canonical production domain without a valid preview. */
66
+ enforced: boolean;
67
+ previewAccessGranted: boolean;
68
+ title: string | null;
69
+ message: string | null;
70
+ desktopImage: WebsiteConstructionMedia | null;
71
+ mobileImage: WebsiteConstructionMedia | null;
72
+ }
73
+ export interface WebsiteLegalPublicConfig {
74
+ schemaVersion: 1;
75
+ version: number;
76
+ contentHash: string | null;
77
+ documents: {
78
+ terms: Record<string, unknown> | null;
79
+ /** Present in public-config from SDK 1.0.12; optional for older fixtures. */
80
+ shipping?: Record<string, unknown> | null;
81
+ /** Present in public-config from SDK 1.0.12; optional for older fixtures. */
82
+ returns?: Record<string, unknown> | null;
83
+ privacy: Record<string, unknown> | null;
84
+ cookies: Record<string, unknown> | null;
85
+ };
86
+ inquiriesChannel: string | null;
87
+ directMarketingConsent: {
88
+ version: number;
89
+ statement: string;
90
+ statementHash: string;
91
+ } | null;
92
+ cookieNotice: {
93
+ enabled: boolean;
94
+ message: string;
95
+ buttonLabel: string;
96
+ };
97
+ }
98
+ export type WebsiteCampaignCaptureSource = "WEBSITE" | "QR";
99
+ export interface WebsiteCampaignCaptureContact {
100
+ partyKind?: "PERSON" | "COMPANY";
101
+ firstName?: string | null;
102
+ lastName?: string | null;
103
+ legalName?: string | null;
104
+ commercialName?: string | null;
105
+ email?: string | null;
106
+ phone?: string | null;
107
+ }
108
+ export interface WebsiteCampaignCaptureInput {
109
+ /** Stable public key configured for the campaign in Bacano. */
110
+ campaignKey: string;
111
+ /** Stable per-submission key. Reusing it is safe and does not duplicate data. */
112
+ idempotencyKey: string;
113
+ /** Use QR only when the current form was reached through a QR entry point. */
114
+ source?: WebsiteCampaignCaptureSource;
115
+ /** Must reflect an explicit acceptance of the website's current statement. */
116
+ commercialConsent: true;
117
+ contact: WebsiteCampaignCaptureContact;
118
+ /** Storefront-defined answers. Bacano stores these as an immutable snapshot. */
119
+ answers?: Record<string, unknown>;
120
+ }
121
+ export interface WebsiteCampaignCaptureResult {
122
+ accepted: boolean;
123
+ message: string;
124
+ }
125
+ export type ResolvedWebsitePublicConfig = WebsitePublicConfig & {
126
+ legal: WebsiteLegalPublicConfig;
127
+ construction: WebsiteConstructionPublicConfig;
128
+ };
129
+ export interface WebsiteMarketingPublicConfig {
130
+ schemaVersion: 1;
131
+ enabled: boolean;
132
+ currencyCode: string;
133
+ timeZone: string;
134
+ attributionWindowDays: number;
135
+ consentPolicy: {
136
+ id: string;
137
+ version: number;
138
+ statement: string;
139
+ statementHash: string;
140
+ } | null;
141
+ destinations: {
142
+ ga4: {
143
+ measurementId: string;
144
+ diagnosticMode: boolean;
145
+ } | null;
146
+ meta: {
147
+ pixelId: string;
148
+ diagnosticMode: boolean;
149
+ } | null;
150
+ /** Browser-only Microsoft Clarity destination; absent in SDK <= 1.0.14. */
151
+ clarity?: {
152
+ projectId: string;
153
+ } | null;
154
+ };
27
155
  }
28
156
  export type WebsiteCustomerIdentifierType = "AUTO" | "EMAIL" | "PHONE" | "DOCUMENT";
29
157
  export interface WebsiteCustomerDocumentType {
@@ -49,6 +177,11 @@ export interface GetWebsitePublicConfigOptions {
49
177
  * header automatically and should omit this option.
50
178
  */
51
179
  origin?: string;
180
+ /**
181
+ * Short-lived signed token generated by Bacano. Storefronts should normally
182
+ * let `BacanoWebsiteGate` discover and manage it automatically.
183
+ */
184
+ previewToken?: string;
52
185
  }
53
186
  export interface BacanoClientContext {
54
187
  websiteId: string;
@@ -79,6 +212,11 @@ export interface Product {
79
212
  createdAt: string;
80
213
  updatedAt: string;
81
214
  variants: ProductVariant[];
215
+ /**
216
+ * Variants visible on a product detail page. Unlike `variants`, this list
217
+ * also preserves enabled public-price variants that are currently sold out.
218
+ */
219
+ detailVariants?: ProductVariant[];
82
220
  media: ProductMedia[];
83
221
  attributes: ProductAttribute[];
84
222
  relatedProducts: ProductRelatedProduct[];
@@ -138,6 +276,11 @@ export interface ProductMedia {
138
276
  thumbnailUrl: string;
139
277
  cardUrl: string;
140
278
  detailUrl: string;
279
+ /** Width-labelled WebP candidates for responsive storefront images. */
280
+ responsiveSources?: Array<{
281
+ width: number;
282
+ url: string;
283
+ }>;
141
284
  mediaKind: ProductMediaKind;
142
285
  mimeType: string | null;
143
286
  fileName: string | null;
@@ -300,6 +443,16 @@ export interface ProductAvailability {
300
443
  productVariantId: string;
301
444
  inStock: boolean;
302
445
  label: string;
446
+ /** Current public purchase limit, net of active stock reservations. */
447
+ maxPurchasableQuantity: number;
448
+ }
449
+ export interface ProductVariantLiveState {
450
+ productVariantId: string;
451
+ inStock: boolean;
452
+ availabilityLabel: string;
453
+ /** Current public purchase limit, net of active stock reservations. */
454
+ maxPurchasableQuantity: number;
455
+ pricing: ProductVariantPricing;
303
456
  }
304
457
  export type ProductSortBy = "name_asc" | "name_desc" | "price_asc" | "price_desc" | "newest";
305
458
  export interface GetProductsOptions {
@@ -370,6 +523,8 @@ export interface GetProductFiltersOptions {
370
523
  }
371
524
  export interface CatalogPageIncludeOptions {
372
525
  products?: boolean;
526
+ /** Include sold-out public variants for statically generated detail pages. */
527
+ detailVariants?: boolean;
373
528
  categories?: boolean;
374
529
  attributes?: boolean;
375
530
  filters?: boolean;
@@ -443,6 +598,38 @@ export interface CatalogPageResult {
443
598
  appliedFilters: Record<string, unknown>;
444
599
  debug: CatalogPageDebug | null;
445
600
  }
601
+ export interface GetStaticCatalogSnapshotOptions {
602
+ categoryListKey: string;
603
+ attributeListKey: string;
604
+ /** Products fetched per gateway request. Defaults to the server maximum. */
605
+ pageSize?: number;
606
+ /** Safety ceiling for a single build. Defaults to 10,000 products. */
607
+ maxProducts?: number;
608
+ }
609
+ export interface GetStaticCatalogBootstrapOptions extends GetStaticCatalogSnapshotOptions {
610
+ /** Products exposed in the initial static catalog page. Defaults to 24. */
611
+ initialPageSize?: number;
612
+ /** Stable order used by both the snapshot and its initial page. */
613
+ sortBy?: ProductSortBy;
614
+ }
615
+ export interface StaticCatalogSnapshot {
616
+ schemaVersion: 1;
617
+ snapshotId: string;
618
+ generatedAt: string;
619
+ websiteId: string;
620
+ products: Product[];
621
+ total: number;
622
+ pageSize: number;
623
+ pageCount: number;
624
+ }
625
+ /**
626
+ * Complete build snapshot plus the first renderable catalog page and facets.
627
+ * This avoids a second catalog request while statically generating a store.
628
+ */
629
+ export interface StaticCatalogBootstrap extends StaticCatalogSnapshot {
630
+ bootstrapSchemaVersion: 1;
631
+ initialPage: CatalogPageResult;
632
+ }
446
633
  export type ProductRecommendationSource = "manual" | "subcategory" | "category" | "attributes" | "fallback";
447
634
  export interface ProductRecommendationPricing {
448
635
  currentPriceMin: number;
@@ -482,6 +669,18 @@ export interface ContentMedia {
482
669
  thumbnailUrl?: string | null;
483
670
  cardUrl?: string | null;
484
671
  detailUrl?: string | null;
672
+ /** Optimized full-width editorial source, capped at the original width. */
673
+ heroUrl?: string | null;
674
+ /** Width-labelled WebP candidates for responsive storefront images. */
675
+ responsiveSources?: Array<{
676
+ width: number;
677
+ url: string;
678
+ }>;
679
+ /** Intrinsic dimensions of the original image when available. */
680
+ originalWidth?: number | null;
681
+ originalHeight?: number | null;
682
+ /** @deprecated Use originalUrl/cardUrl/detailUrl/heroUrl for the rendering context. */
683
+ src?: string | null;
485
684
  /** @deprecated Use originalUrl/cardUrl/detailUrl based on the rendering context. */
486
685
  url?: string | null;
487
686
  }
@@ -628,6 +827,7 @@ export interface StartGuestCheckoutInput {
628
827
  contact: WebsiteGuestContact;
629
828
  items: WebsiteCheckoutItem[];
630
829
  fulfillmentFamily?: WebsiteFulfillmentFamily;
830
+ marketingAttribution?: WebsiteMarketingAttribution;
631
831
  }
632
832
  export interface CheckoutSubmission {
633
833
  contact: WebsiteGuestContact;
@@ -636,6 +836,8 @@ export interface CheckoutSubmission {
636
836
  shippingQuoteId?: string;
637
837
  paymentMode?: WebsitePaymentMode;
638
838
  paymentMethodId?: string;
839
+ marketingAttribution?: WebsiteMarketingAttribution;
840
+ advertisingConsent?: WebsiteAdvertisingConsent;
639
841
  /**
640
842
  * Request an account activation email after Bacano creates a valid guest order.
641
843
  * Delivery does not depend on the order being paid.