@wix/agent-skills 1.14.0 → 1.15.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 (247) hide show
  1. package/README.md +4 -1
  2. package/package.json +1 -1
  3. package/skills/README.md +23 -0
  4. package/skills/wix-app/SKILL.md +10 -2
  5. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
  6. package/skills/wix-app/references/CODE_QUALITY.md +1 -0
  7. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
  9. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +23 -9
  10. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  11. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  12. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  13. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  31. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  32. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  33. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  34. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +17 -105
  35. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  36. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  37. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +15 -15
  38. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  39. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  40. package/skills/wix-auth/SKILL.md +12 -0
  41. package/skills/wix-auth/references/device-flow.md +140 -0
  42. package/skills/wix-docs/SKILL.md +188 -0
  43. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  44. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  45. package/skills/wix-headless/SKILL.md +84 -130
  46. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  47. package/skills/wix-headless/entry/skill.md +78 -0
  48. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  49. package/skills/wix-headless/references/CAPABILITIES.md +84 -0
  50. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  51. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  52. package/skills/wix-headless/references/FEEDBACK.md +118 -0
  53. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  54. package/skills/wix-headless/references/SDK_HANDOFF.md +97 -0
  55. package/skills/wix-headless/references/SEED.md +44 -188
  56. package/skills/wix-headless/references/SETUP.md +32 -103
  57. package/skills/wix-headless/references/astro.md +98 -0
  58. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  59. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  60. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  61. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  62. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  63. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  64. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  65. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  66. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  67. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +173 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  72. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  73. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +228 -0
  74. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  75. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  76. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  77. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +176 -0
  78. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +153 -0
  79. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  80. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +161 -0
  81. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +117 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  83. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  84. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  85. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  86. package/skills/wix-headless/references/managed/DEPLOYMENT.md +57 -0
  87. package/skills/wix-headless/references/non-astro.md +59 -0
  88. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  89. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +7 -0
  90. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  91. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +63 -0
  92. package/skills/wix-manage/SKILL.md +24 -1
  93. package/skills/wix-manage/references/analytics/query-site-analytics.md +241 -0
  94. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  95. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +265 -0
  96. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +436 -0
  97. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +209 -0
  98. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  99. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +51 -0
  100. package/skills/wix-vibe-headless/SKILL.md +136 -0
  101. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  102. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  103. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +153 -0
  104. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  105. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +215 -0
  106. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  107. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  108. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  109. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +135 -0
  110. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  111. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  112. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  113. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  114. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +194 -0
  115. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  116. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  117. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  118. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  119. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  120. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  121. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  122. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +155 -0
  123. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  124. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +142 -0
  125. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  126. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  127. package/skills/wix-headless/references/BUILD.md +0 -105
  128. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  129. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  130. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  131. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  132. package/skills/wix-headless/references/PLAN-create.md +0 -17
  133. package/skills/wix-headless/references/PLAN.md +0 -92
  134. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  135. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  136. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  137. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  138. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  139. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  140. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  141. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  142. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  143. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  144. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  145. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  146. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  147. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  148. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  149. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  150. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  151. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  152. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  153. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  154. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  155. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  156. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  157. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  158. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  159. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  160. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  161. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  162. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  163. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  164. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  165. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  166. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  167. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  168. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  169. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  170. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  171. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  172. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  173. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  174. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  175. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  176. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  177. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  178. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  179. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  180. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  181. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  182. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  183. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  184. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  185. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  186. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  187. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  188. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  189. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  190. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  191. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  192. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  193. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  194. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  195. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  196. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  197. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  198. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  199. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  200. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  201. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  202. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  203. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  204. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  205. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  206. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  207. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  208. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  209. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  210. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  211. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  212. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  213. package/skills/wix-headless/references/seed-recipes.md +0 -49
  214. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  215. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  216. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  217. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  218. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  219. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  220. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  221. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  222. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  223. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  224. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  225. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  226. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  227. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  228. package/skills/wix-headless/references/verticals/blog.md +0 -31
  229. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  230. package/skills/wix-headless/references/verticals/cms.md +0 -30
  231. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  232. package/skills/wix-headless/references/verticals/forms.md +0 -26
  233. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  234. package/skills/wix-headless/references/verticals/stores.md +0 -33
  235. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  236. package/skills/wix-headless/scripts/compose.mjs +0 -520
  237. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  238. package/skills/wix-headless/scripts/flatten.sh +0 -61
  239. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  240. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  241. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  242. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  243. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  244. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  245. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  246. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  247. package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
@@ -0,0 +1,186 @@
1
+ /**
2
+ * REST version of the Wix headless integration — the direct analog of
3
+ * `storefrontApiRequest` in src/lib/shopify.ts. Plain fetch, JSON in / JSON out.
4
+ * No SDK, no query builder, no Velo.
5
+ *
6
+ * WIX_CLIENT_ID is the one thing to set. The client exchanges it for a visitor
7
+ * access token via the OAuth2 token endpoint, then sends that token on every API call.
8
+ */
9
+
10
+ /**
11
+ * The public headless OAuth client id. It is a buyer-facing credential (it only mints
12
+ * anonymous visitor tokens), NOT a secret — so it is safe to hardcode here. Paste the
13
+ * value from the Wix Business Manager prompt in place of the placeholder.
14
+ */
15
+ export const WIX_CLIENT_ID = "<YOUR-CLIENT-ID>";
16
+
17
+ export const WIX_API_BASE = "https://www.wixapis.com";
18
+ const OAUTH_TOKEN_URL = `${WIX_API_BASE}/oauth2/token`;
19
+
20
+ // Scope the storage key by client id so two headless sites served from the same
21
+ // origin (e.g. localhost:4321 across projects) don't share one visitor token —
22
+ // which would load site A's token for site B and mix up carts/identity.
23
+ const TOKEN_STORAGE_KEY = `wix-visitor-token-${WIX_CLIENT_ID}`;
24
+ let tokenCache = null;
25
+
26
+ function loadToken() {
27
+ if (tokenCache) return tokenCache;
28
+ if (typeof window === "undefined") return null;
29
+ try {
30
+ const raw = window.localStorage.getItem(TOKEN_STORAGE_KEY);
31
+ if (raw) tokenCache = JSON.parse(raw);
32
+ } catch {
33
+ /* ignore disabled/full storage */
34
+ }
35
+ return tokenCache;
36
+ }
37
+
38
+ function saveToken(t) {
39
+ tokenCache = t;
40
+ if (typeof window === "undefined") return;
41
+ try {
42
+ window.localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify(t));
43
+ } catch {
44
+ /* ignore */
45
+ }
46
+ }
47
+
48
+ async function mintToken(body) {
49
+ const res = await fetch(OAUTH_TOKEN_URL, {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify(body),
53
+ });
54
+ if (!res.ok) throw new Error(`Wix OAuth failed: ${res.status}`);
55
+ const data = await res.json();
56
+ return {
57
+ accessToken: data.access_token,
58
+ refreshToken: data.refresh_token,
59
+ expiresAt: Date.now() + data.expires_in * 1000,
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Log a member in on THIS client by swapping the persisted token set for the
65
+ * member's tokens (see references/members/). Member tokens are the SAME shape as
66
+ * visitor tokens and refresh via the same `refresh_token` grant — logging in just
67
+ * replaces the set the client already carries, so EVERY subsequent `wixApiRequest`
68
+ * (cart, orders, bookings, "my …" reads) now runs as the member. Called by the
69
+ * members helper after a successful credential / social / SSO login.
70
+ *
71
+ * @param {{ accessToken: string, refreshToken: string, expiresIn: number }} tokens
72
+ */
73
+ export function setSessionTokens({ accessToken, refreshToken, expiresIn }) {
74
+ saveToken({
75
+ accessToken,
76
+ refreshToken,
77
+ expiresAt: Date.now() + expiresIn * 1000,
78
+ role: "member",
79
+ });
80
+ }
81
+
82
+ /**
83
+ * Drop the persisted session so the next call mints a FRESH anonymous visitor.
84
+ * Call on logout — otherwise the dead member token lingers and reads fail.
85
+ */
86
+ export function clearSession() {
87
+ tokenCache = null;
88
+ if (typeof window === "undefined") return;
89
+ try {
90
+ window.localStorage.removeItem(TOKEN_STORAGE_KEY);
91
+ } catch {
92
+ /* ignore */
93
+ }
94
+ }
95
+
96
+ /**
97
+ * True once a member is logged in on this client (vs. an anonymous visitor).
98
+ * Gate account UI on this, or on the members helper's `isLoggedIn()`.
99
+ * @returns {boolean}
100
+ */
101
+ export function isMember() {
102
+ return loadToken()?.role === "member";
103
+ }
104
+
105
+ /**
106
+ * Get a visitor access token from the client id.
107
+ *
108
+ * The visitor token IS the identity of the Wix "current cart", so we persist the
109
+ * refresh token to localStorage and REFRESH it on expiry — re-minting a fresh
110
+ * `anonymous` token would create a NEW visitor and silently empty the cart on
111
+ * every reload / after the 4h token lifetime. Anonymous mint happens only once
112
+ * (or when no refresh token is stored).
113
+ */
114
+ async function getAccessToken() {
115
+ const cached = loadToken();
116
+ if (cached && cached.expiresAt > Date.now() + 60_000) return cached.accessToken;
117
+
118
+ if (cached?.refreshToken) {
119
+ try {
120
+ const refreshed = await mintToken({ clientId: WIX_CLIENT_ID, grantType: "refresh_token", refreshToken: cached.refreshToken });
121
+ // Refreshing preserves identity: a member refresh token yields member tokens.
122
+ refreshed.role = cached.role || "visitor";
123
+ saveToken(refreshed);
124
+ return refreshed.accessToken;
125
+ } catch {
126
+ /* refresh failed — fall through to a fresh anonymous visitor */
127
+ }
128
+ }
129
+ const fresh = await mintToken({ clientId: WIX_CLIENT_ID, grantType: "anonymous" });
130
+ fresh.role = "visitor";
131
+ saveToken(fresh);
132
+ return fresh.accessToken;
133
+ }
134
+
135
+ /**
136
+ * Core transport — mirrors storefrontApiRequest. Adds the Authorization header,
137
+ * resolves the path against the Wix API base, parses JSON, surfaces errors.
138
+ *
139
+ * @param {string} path
140
+ * @param {{ method?: "GET"|"POST"|"PUT"|"DELETE", body?: unknown, query?: Record<string, string | undefined> }} [options]
141
+ */
142
+ export async function wixApiRequest(path, options = {}) {
143
+ const { method = "POST", body, query } = options;
144
+ const token = await getAccessToken();
145
+
146
+ const url = new URL(path.startsWith("http") ? path : `${WIX_API_BASE}${path}`);
147
+ if (query) {
148
+ for (const [k, v] of Object.entries(query)) {
149
+ if (v === undefined) continue;
150
+ if (Array.isArray(v)) {
151
+ for (const item of v) url.searchParams.append(k, item);
152
+ } else {
153
+ url.searchParams.set(k, v);
154
+ }
155
+ }
156
+ }
157
+
158
+ const res = await fetch(url.toString(), {
159
+ method,
160
+ headers: {
161
+ "Content-Type": "application/json",
162
+ Authorization: token, // Wix expects the raw access token (no "Bearer " prefix)
163
+ },
164
+ body: body !== undefined ? JSON.stringify(body) : undefined,
165
+ });
166
+
167
+ if (res.status === 402) {
168
+ // This API requires an active plan / premium feature on the site.
169
+ console.warn("Wix: Payment required (402) — this API needs an active plan/premium feature.");
170
+ return;
171
+ }
172
+ if (!res.ok) {
173
+ const text = await res.text().catch(() => "");
174
+ let parsed;
175
+ try { parsed = JSON.parse(text); } catch { parsed = text; }
176
+ // Attach status + parsed body so callers (e.g. the members helper) can map a
177
+ // meaningful auth outcome (404 = bad credentials, 409 = email exists) to a
178
+ // friendly message instead of parsing the message string.
179
+ const err = new Error(`Wix API error ${res.status}: ${text}`);
180
+ err.status = res.status;
181
+ err.body = parsed;
182
+ throw err;
183
+ }
184
+ if (res.status === 204) return undefined;
185
+ return await res.json();
186
+ }
@@ -0,0 +1,155 @@
1
+
2
+ # Wix Storefront Skill
3
+
4
+ > **Source files (in this skill):** the shared transport `references/shared/wix-client.js` and both storefront helpers from `references/storefront/`. All helpers import from `"./wix-client.js"`, so copy them into the same folder (e.g. `src/rest/`). Copy **both** for a full storefront:
5
+ >
6
+ > | File | What it covers |
7
+ > |---|---|
8
+ > | `wix-store-catalog.js` | Products, categories, product detail, search |
9
+ > | `wix-store-cart.js` | Add to cart, cart management, checkout |
10
+
11
+ Builds a real, client-only Wix storefront. The browser talks to Wix directly over a
12
+ public `WIX_CLIENT_ID`. Never mock products; never hand-build `/checkout` URLs — always
13
+ go through the eCom cart + redirect-session.
14
+
15
+ ## When to use
16
+ - User wants a Wix eCommerce store or asks to "connect Wix".
17
+ - Replacing placeholder/mock products with live Wix data.
18
+ - Adding cart, checkout, categories, or product detail pages over an existing Wix Stores catalog.
19
+
20
+ ## Prerequisites
21
+ 1. A Wix site with **Wix Stores installed and products already added** (this skill does
22
+ NOT provision — it's read-only over the catalog).
23
+ 2. The site's public headless **`WIX_CLIENT_ID`**, provided in the handoff prompt (the
24
+ Wix Business Manager surfaces a copyable prompt with the id filled in — see
25
+ the router `SKILL.md`). Paste it into `src/rest/wix-client.js` in place of the placeholder. It is a
26
+ buyer-facing credential (it only mints anonymous visitor tokens), **not** a secret, so
27
+ hardcoding/committing it is fine.
28
+ 3. The deployed app domain must be allow-listed on the OAuth client for Wix-hosted
29
+ checkout to return. This is a **separate Wix setup flow the user completes later** —
30
+ out of this skill's scope. If checkout return fails before that setup is done, that's
31
+ expected; flag it and continue.
32
+
33
+ ## The API (copy as-is; do not re-derive it)
34
+ This skill ships only the REST layer — no UI components. Build the storefront's UI
35
+ however the project wants; wire it to these two snippets. Copy them into the app (e.g.
36
+ `src/api/`) and only adjust import paths:
37
+ - `src/rest/wix-client.js` — visitor-token mint/refresh + transport. Set `WIX_CLIENT_ID` to
38
+ the id from the prompt (replace the `<YOUR-CLIENT-ID>` placeholder). The visitor refresh
39
+ token IS the cart identity; it is persisted to localStorage. Do not re-mint anonymously
40
+ per load or the cart silently empties.
41
+ - `src/rest/wix-store-catalog.js` — **Catalog:**
42
+ `queryProducts`, `queryProductsByCategory`, `getProductBySlug`, `countProducts`,
43
+ `queryCategories`, `getCategoryBySlug`
44
+ - `src/rest/wix-store-cart.js` — **Cart & checkout:**
45
+ `addToCart`, `getCurrentCart`, `updateCartItemQuantity`, `removeFromCart`, `checkout`
46
+
47
+ The Product and Cart shapes are documented as JSDoc comments at the top of each helper file.
48
+ Read them before building the UI — they describe the key fields and link to the full API
49
+ reference for anything not shown.
50
+
51
+ ## How to wire it (UI is the project's choice)
52
+ - **Product grid** — `queryProducts()` for the listing (visible products only); pass
53
+ `nextCursor` back as `cursor` to load the next page. Render fields directly from the Wix
54
+ product object (see the `Product` typedef in `wix-store.js` for key fields). For price, use
55
+ `actualPriceRange.minValue.formattedAmount` (already includes the currency symbol) — no
56
+ manual formatting needed.
57
+ - **PDP** — `getProductBySlug(slug)` keyed off the URL slug; returns null on miss — show
58
+ a not-found state, never invent a product. **Drive the whole PDP from the returned product
59
+ object at runtime — build it generically, not around the one product you happened to inspect.**
60
+ A catalog is heterogeneous: some products have `options`, some have `modifiers` (mandatory or
61
+ optional), some track inventory, some none of these. Render a selector for **every** entry the
62
+ product actually carries: one control per `product.options` (variant choices) **and** one per
63
+ `product.modifiers` (TEXT_CHOICES → choice buttons/select; FREE_TEXT → a text input); render
64
+ neither when the arrays are empty. Skipping modifiers is a common miss — a product with a
65
+ **mandatory** modifier (e.g. "gift wrap?") whose control isn't rendered can never be added: the
66
+ buyer can't satisfy the requirement, so `add-to-cart` returns 200 with an **empty** `lineItems`
67
+ and the add silently no-ops.
68
+ - **Gate the Add-to-cart button** — disable it only until the requirements the product *actually
69
+ has* are met, computed from the product object (never assume every product has options or
70
+ modifiers): if `product.options` is non-empty, a variant must resolve from the selections; every
71
+ `modifier.mandatory === true` must have a value. A product with no options and no mandatory
72
+ modifiers is immediately addable — don't leave the button stuck. Optional modifiers never block.
73
+ Then pass the selections to `addToCart` (see Cart below); never call it with a required selection missing.
74
+ - **Reflect stock in the UI** — the product object already carries availability at three levels; surface
75
+ it rather than letting the buyer discover it only on click. Read it from the data at runtime (never
76
+ hardcode):
77
+ - **Grid / card:** `product.inventory.availabilityStatus` (`IN_STOCK` / `OUT_OF_STOCK` /
78
+ `PARTIALLY_OUT_OF_STOCK`) — badge an out-of-stock product as sold out.
79
+ - **Option choice:** `product.options[].choicesSettings.choices[].inStock` — disable/strike a choice
80
+ (e.g. size L) that has no in-stock variant, before a full variant is even resolved.
81
+ - **Variant:** `variantsInfo.variants[].inventoryStatus.inStock` — once selections resolve to a
82
+ variant, disable Add-to-cart (label "Out of stock") when that variant is `inStock: false`.
83
+ A product/variant with inventory tracking **off** reports `availabilityStatus: IN_STOCK` /
84
+ `inStock: true` and stays freely addable — tracking-off is not "no data", it's "always available".
85
+ `addToCart` still throws on a sold-out line as a backstop, but the UI should prevent reaching it.
86
+ - **Categories** — `queryCategories()` for a category menu; `getCategoryBySlug(slug)` for
87
+ a category landing page. Pass `category.id` to `queryProductsByCategory(categoryId, { limit?, cursor? })`
88
+ to list only the products in that category; paginate exactly like `queryProducts`.
89
+ - **Cart** — `addToCart(catalogItemId, variantId?, qty?, { modifierChoices?, customTextFields? }?)`,
90
+ `updateCartItemQuantity(lineItemId, qty)`, `removeFromCart(lineItemId)`.
91
+ - `variantId` (`variantsInfo.variants[].id` from `getProductBySlug`) — required for products with
92
+ options; resolve it by matching the buyer's selections to `variant.choices[].optionChoiceIds`.
93
+ - `modifierChoices` — `{ [modifier.key]: choiceKey }` for `TEXT_CHOICES` modifiers.
94
+ - `customTextFields` — `{ [modifier.freeTextSettings.key]: userInput }` for `FREE_TEXT` modifiers.
95
+ Mandatory modifiers must be included. See the eCommerce integration guide:
96
+ https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/e-commerce-integration.md
97
+ - Use `cart.lineItems[].id` as `lineItemId` (not `catalogItemId`) for mutations.
98
+ - Read the cart back with `getCurrentCart()` rather than mirroring it locally.
99
+ - **Checkout** — `window.location.href = await checkout()`. After the buyer returns from
100
+ hosted checkout the order is placed and the cart is empty — re-fetch with
101
+ `getCurrentCart()` on return (e.g. on mount + `visibilitychange`) to clear the UI.
102
+ - **Empty state** — if `countProducts()` is 0, show an empty state telling the user to
103
+ add products in their Wix dashboard. Never invent products.
104
+
105
+ ## Hard rules (do not violate)
106
+ - ✅ Checkout ONLY via `checkout()` (`create-checkout` → `/headless/v1/redirect-session`
107
+ `fullUrl`), then redirect.
108
+ - ❌ Never hand-build `/checkout`, cart-add, or product permalinks for purchase.
109
+ - ❌ Never mock products — render live Wix data or the empty state.
110
+ - ❌ Never generate fake reviews, ratings, or testimonials. Empty review UI only.
111
+ - ✅ Set `WIX_CLIENT_ID` from the prompt's value (public client id — safe to hardcode).
112
+ - ✅ `lineItemId` for cart mutations is `cart.lineItems[].id`, not `catalogItemId`.
113
+ - ✅ On the PDP, render a control for **every** `product.options` entry **and** every `product.modifiers`
114
+ entry — never only variants. Keep Add-to-cart disabled until a variant resolves and every
115
+ `modifier.mandatory === true` has a value; a mandatory modifier with no rendered control makes the
116
+ product unbuyable (add-to-cart returns 200 with empty `lineItems`).
117
+ - ✅ Pass `addToCart`'s `variantId` (`variantsInfo.variants[].id`) for products with variants; omit for products without.
118
+ - ✅ Pass `modifierChoices` (`{ [modifier.key]: choiceKey }`) for TEXT_CHOICES modifiers; pass `customTextFields`
119
+ (`{ [modifier.freeTextSettings.key]: userInput }`) for FREE_TEXT modifiers. Include mandatory modifiers.
120
+ - The engine fails loudly on purpose: `addToCart`/`checkout` throw on out-of-stock or
121
+ empty carts. A green path means it is really buyable — don't swallow these.
122
+
123
+ ## Beyond the snippets
124
+ The snippets cover the common storefront paths. If you hit a use case they don't cover
125
+ (e.g. coupons, members/auth, a product field not shown in the typedef), make the call
126
+ yourself with `wixApiRequest` — but look up the exact endpoint, HTTP method, and request
127
+ body in the **official Wix API reference** first; never guess:
128
+ - Official Wix API reference: https://dev.wix.com/docs/api-reference.md
129
+ - eCommerce integration guide (modifiers, custom text, variants): https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/e-commerce-integration.md
130
+ - Member login + a "my orders" account view → the **members** vertical (`references/members/INSTRUCTIONS.md`): custom login on your own UI so buyers can sign in and see their account.
131
+
132
+ Keep the snippets as the default for everything they already do; reach for the API
133
+ reference only for the gap.
134
+
135
+ ## Point the user to their dashboard
136
+ In some cases, users need to access the Wix dashboard in order to edit the store content for their site. To facilitate this, provide the user with deep links directly to the relevant dashboard pages. For store data those pages are:
137
+ - **Products** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-stores/products` (`Dashboard → Store → Products`; add/edit products, variants, inventory)
138
+ - **Categories** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-stores/categories/list` (`Dashboard → Store → Categories`; organize products into the category menu)
139
+
140
+ Substitute the site's `metaSiteId` to complete the links (you have it from the handoff / `ListWixSites`). Include the in-dashboard navigation as a fallback.
141
+
142
+ ## Verification checklist (before declaring done)
143
+ - [ ] `WIX_CLIENT_ID` set to the prompt's value (not the `<YOUR-CLIENT-ID>` placeholder)
144
+ - [ ] Visitor token persists across reload (cart survives reload, same visitor)
145
+ - [ ] Every product choice renders on the PDP — variant options **and** modifiers (mandatory ones included)
146
+ - [ ] Add-to-cart button stays disabled until all required choices are made (variant + mandatory modifiers)
147
+ - [ ] A product with a mandatory modifier adds successfully (its selection is sent, cart line appears)
148
+ - [ ] Stock reflected in the UI — sold-out product badged (grid), out-of-stock option choices and variants disabled/labelled (PDP)
149
+ - [ ] Add to cart works; out-of-stock items throw rather than add a dead line
150
+ - [ ] Quantity update / remove reflect in `getCurrentCart()`
151
+ - [ ] Checkout redirects via redirect-session `fullUrl` (no hand-built URL)
152
+ - [ ] Cart re-fetched on return from checkout (clears once the order is placed)
153
+ - [ ] Empty state shown when `countProducts()` is 0
154
+ - [ ] No mock products anywhere
155
+ - [ ] Told the user at least once that they can continue setting up their store in the dashboard and provided deep links.
@@ -0,0 +1,147 @@
1
+ import { wixApiRequest } from "./wix-client.js";
2
+
3
+ // Data model reference: see INSTRUCTIONS.md
4
+ // Product shape (for addToCart): see wix-store-catalog.js
5
+
6
+ // Stores app id — required inside catalogReference for store products.
7
+ const STORES_APP_ID = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
8
+
9
+ /**
10
+ * Wix eCom Cart — key fields for building a cart UI.
11
+ * Full model: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/get-cart.md
12
+ *
13
+ * id {string}, currency {string},
14
+ * lineItems[].id {string} — lineItemId for update/remove (NOT catalogItemId),
15
+ * lineItems[].quantity {number},
16
+ * lineItems[].catalogReference.catalogItemId {string},
17
+ * lineItems[].productName.original {string},
18
+ * lineItems[].price.formattedAmount {string} — after discounts with currency symbol,
19
+ * lineItems[].fullPrice.formattedAmount {string} — before discount (strikethrough),
20
+ * lineItems[].descriptionLines {array} — human-readable option/modifier labels:
21
+ * [{ name: { original }, plainText: { original } OR colorInfo: { original, code } }],
22
+ * lineItems[].image.url {string},
23
+ * lineItems[].availability.status {string} — "AVAILABLE"|"NOT_AVAILABLE"|"PARTIALLY_AVAILABLE"|"NOT_FOUND"
24
+ */
25
+
26
+ /**
27
+ * Add a product to the visitor's current cart.
28
+ *
29
+ * For products with options (variants), pass the chosen variantId — resolve it from
30
+ * product.variantsInfo.variants (from getProductBySlug) by matching the buyer's selected
31
+ * option choices to variant.choices[].optionChoiceIds.
32
+ *
33
+ * For products with modifiers:
34
+ * - TEXT_CHOICES: pass modifierChoices: { [modifier.key]: choiceKey }
35
+ * - FREE_TEXT: pass customTextFields: { [modifier.freeTextSettings.key]: userInput }
36
+ * Mandatory modifiers (modifier.mandatory === true) MUST be included.
37
+ *
38
+ * Throws on out-of-stock so the buyer can't reach checkout with an unbuyable line.
39
+ * Full catalogReference reference: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/e-commerce-integration.md
40
+ *
41
+ * @param {string} catalogItemId Product GUID (product.id).
42
+ * @param {string} [variantId] variantsInfo.variants[].id — required for products with variants.
43
+ * @param {number} [quantity]
44
+ * @param {{ modifierChoices?: Record<string,string>, customTextFields?: Record<string,string> }} [extras]
45
+ * @returns {Promise<object>} Updated cart.
46
+ */
47
+ export async function addToCart(catalogItemId, variantId, quantity = 1, { modifierChoices, customTextFields } = {}) {
48
+ const catalogReferenceOptions = {};
49
+ if (variantId) catalogReferenceOptions.variantId = variantId;
50
+ if (modifierChoices && Object.keys(modifierChoices).length) catalogReferenceOptions.options = modifierChoices;
51
+ if (customTextFields && Object.keys(customTextFields).length) catalogReferenceOptions.customTextFields = customTextFields;
52
+
53
+ const catalogReference = { appId: STORES_APP_ID, catalogItemId };
54
+ if (Object.keys(catalogReferenceOptions).length) catalogReference.options = catalogReferenceOptions;
55
+ const res = await wixApiRequest("/ecom/v1/carts/current/add-to-cart", {
56
+ method: "POST",
57
+ body: { lineItems: [{ catalogReference, quantity }] },
58
+ });
59
+ const line = (res?.cart?.lineItems ?? []).find(
60
+ (l) => l.catalogReference?.catalogItemId === catalogItemId && (!variantId || l.catalogReference?.options?.variantId === variantId),
61
+ );
62
+ // Wix returns 200 even when the line is silently rejected — guard both signals:
63
+ // 1. availability.status set to something other than AVAILABLE
64
+ // 2. no matching line at all (quantity 0 / line absent)
65
+ if (line?.availability?.status && line.availability.status !== "AVAILABLE") {
66
+ throw new Error(`Item not available for sale (status: ${line.availability.status}). Is it in stock?`);
67
+ }
68
+ if (!line || line.quantity === 0) {
69
+ // A missing line usually means a required selection wasn't sent — a mandatory modifier
70
+ // (pass modifierChoices/customTextFields) or the variantId for a product with options —
71
+ // not necessarily out of stock. Verify every required choice is included in this call.
72
+ throw new Error(
73
+ "Item could not be added to the cart. Check that every required selection was sent: " +
74
+ "the variantId for a product with options, and all mandatory modifiers " +
75
+ "(modifierChoices for TEXT_CHOICES, customTextFields for FREE_TEXT). It may also be out of stock.",
76
+ );
77
+ }
78
+ return res?.cart;
79
+ }
80
+
81
+ /** Read the visitor's current cart. Returns null if no cart exists yet. */
82
+ export async function getCurrentCart() {
83
+ try {
84
+ const res = await wixApiRequest("/ecom/v1/carts/current", { method: "GET" });
85
+ return res?.cart ?? null;
86
+ } catch {
87
+ return null;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Create a checkout from the current cart and return the hosted checkout URL.
93
+ * Throws on empty cart, unavailable lines, or a missing redirect URL.
94
+ * Usage: window.location.href = await checkout()
95
+ * @returns {Promise<string>}
96
+ */
97
+ export async function checkout() {
98
+ const cart = await getCurrentCart();
99
+ const lines = cart?.lineItems ?? [];
100
+ if (!lines.length) throw new Error("Cannot check out: the cart is empty.");
101
+ const unavailable = lines.filter((l) => l.availability?.status && l.availability.status !== "AVAILABLE");
102
+ if (unavailable.length) {
103
+ const names = unavailable.map((l) => l.productName?.original ?? l.catalogReference?.catalogItemId).join(", ");
104
+ throw new Error(`Cannot check out: ${unavailable.length} item(s) not available — ${names}.`);
105
+ }
106
+
107
+ const checkoutRes = await wixApiRequest("/ecom/v1/carts/current/create-checkout", {
108
+ method: "POST",
109
+ body: { channelType: "WEB" },
110
+ });
111
+ const checkoutId = checkoutRes?.checkoutId;
112
+ if (!checkoutId) throw new Error("Failed to create checkout from the current cart.");
113
+
114
+ const redirect = await wixApiRequest("/headless/v1/redirect-session", {
115
+ method: "POST",
116
+ body: { ecomCheckout: { checkoutId }, callbacks: { postFlowUrl: window.location.href } },
117
+ });
118
+ const url = redirect?.redirectSession?.fullUrl;
119
+ if (!url) throw new Error("Failed to create the checkout redirect session.");
120
+ return url;
121
+ }
122
+
123
+ /**
124
+ * Update the quantity of a cart line. lineItemId is cart.lineItems[].id, not catalogItemId.
125
+ * Wix caps the result at remaining stock — returned quantity may be lower than requested.
126
+ * @returns {Promise<object>} Updated cart.
127
+ */
128
+ export async function updateCartItemQuantity(lineItemId, quantity) {
129
+ const res = await wixApiRequest("/ecom/v1/carts/current/update-line-items-quantity", {
130
+ method: "POST",
131
+ body: { lineItems: [{ id: lineItemId, quantity }] },
132
+ });
133
+ return res?.cart;
134
+ }
135
+
136
+ /**
137
+ * Remove a line from the current cart by its cart.lineItems[].id.
138
+ * @param {string} lineItemId
139
+ * @returns {Promise<object>} Updated cart.
140
+ */
141
+ export async function removeFromCart(lineItemId) {
142
+ const res = await wixApiRequest("/ecom/v1/carts/current/remove-line-items", {
143
+ method: "POST",
144
+ body: { lineItemIds: [lineItemId] },
145
+ });
146
+ return res?.cart;
147
+ }
@@ -0,0 +1,142 @@
1
+ import { wixApiRequest } from "./wix-client.js";
2
+
3
+ // Data model reference: see INSTRUCTIONS.md
4
+
5
+ /**
6
+ * Wix Stores V3 Product — key fields for the storefront catalog.
7
+ * Full model: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products.md
8
+ *
9
+ * id {string}, name {string}, slug {string}, visible {boolean}, productType "PHYSICAL"|"DIGITAL",
10
+ * mainCategoryId {string},
11
+ * media.main.image {object} — { id, url, height, width, altText },
12
+ * media.itemsInfo.items {array} — gallery: [{ id, altText, image: { id, url, height, width, altText }, mediaType }],
13
+ * actualPriceRange.minValue.formattedAmount {string} — lowest price with currency symbol,
14
+ * actualPriceRange.maxValue.formattedAmount {string} — highest price with currency symbol,
15
+ * compareAtPriceRange.minValue.formattedAmount {string} — strikethrough price (present when on sale),
16
+ * inventory.availabilityStatus {string} — "IN_STOCK"|"OUT_OF_STOCK"|"PARTIALLY_OUT_OF_STOCK",
17
+ * options {array} — product options e.g. Size, Color:
18
+ * [{ id, name, optionRenderType "TEXT_CHOICES"|"COLOR_CHOICES"|"SWATCH_CHOICES",
19
+ * choicesSettings.choices [{ choiceId, key, name, inStock, visible, linkedMedia }] }],
20
+ * modifiers {array} — non-variant customizations (engraving, gift wrap):
21
+ * [{ id, name, mandatory, modifierRenderType "TEXT_CHOICES"|"FREE_TEXT",
22
+ * key, choicesSettings.choices, freeTextSettings.key }],
23
+ * plainDescription {string} — HTML product description,
24
+ * variantsInfo.variants {array} — returned only by getProductBySlug:
25
+ * [{ id, visible, choices [{ optionChoiceIds: { optionId, choiceId } }],
26
+ * price: { actualPrice, compareAtPrice }, media, inventoryStatus: { inStock } }]
27
+ * To resolve a buyer's option selections to a variantId: find the variant whose choices
28
+ * match all selected { optionId, choiceId } pairs, then pass variant.id to addToCart.
29
+ *
30
+ * Category: { id, name, slug, visible, description, image, itemCounter, parentCategory.id }
31
+ * Full model: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories
32
+ */
33
+
34
+ /**
35
+ * Query visible products (one page). Pass nextCursor back as cursor to load the next page.
36
+ * @param {{ limit?: number, cursor?: string }} [options]
37
+ * @returns {Promise<{ products: object[], nextCursor: string|null }>}
38
+ */
39
+ export async function queryProducts({ limit = 100, cursor } = {}) {
40
+ const res = await wixApiRequest("/stores/v3/products/query", {
41
+ method: "POST",
42
+ body: {
43
+ fields: ["CURRENCY", "PLAIN_DESCRIPTION", "MEDIA_ITEMS_INFO"],
44
+ query: {
45
+ ...(cursor ? {} : { filter: { visible: true } }),
46
+ cursorPaging: cursor ? { limit, cursor } : { limit },
47
+ },
48
+ },
49
+ });
50
+ return {
51
+ products: res?.products ?? [],
52
+ nextCursor: res?.pagingMetadata?.cursors?.next ?? null,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Fetch a product by its URL slug. Returns null if not found.
58
+ * Returns the full product including variantsInfo.variants (with per-variant media and choices).
59
+ * @param {string} slug
60
+ * @returns {Promise<object|null>}
61
+ */
62
+ export async function getProductBySlug(slug) {
63
+ const res = await wixApiRequest(`/stores/v3/products/slug/${encodeURIComponent(slug)}`, {
64
+ method: "GET",
65
+ query: { fields: ["CURRENCY", "PLAIN_DESCRIPTION", "MEDIA_ITEMS_INFO"] },
66
+ });
67
+ return res?.product ?? null;
68
+ }
69
+
70
+ /**
71
+ * Query visible products belonging to a category (one page).
72
+ * @param {string} categoryId Category GUID from queryCategories / getCategoryBySlug.
73
+ * @param {{ limit?: number, cursor?: string }} [options]
74
+ * @returns {Promise<{ products: object[], nextCursor: string|null }>}
75
+ */
76
+ export async function queryProductsByCategory(categoryId, { limit = 100, cursor } = {}) {
77
+ const res = await wixApiRequest("/stores/v3/products/search", {
78
+ method: "POST",
79
+ body: {
80
+ fields: ["CURRENCY", "PLAIN_DESCRIPTION", "MEDIA_ITEMS_INFO"],
81
+ search: {
82
+ ...(cursor
83
+ ? { cursorPaging: { limit, cursor } }
84
+ : {
85
+ cursorPaging: { limit },
86
+ filter: {
87
+ visible: true,
88
+ "allCategoriesInfo.categories": { $matchItems: [{ id: categoryId }] },
89
+ },
90
+ }),
91
+ },
92
+ },
93
+ });
94
+ return {
95
+ products: res?.products ?? [],
96
+ nextCursor: res?.pagingMetadata?.cursors?.next ?? null,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Total number of visible products. Used for empty-state logic (0 → prompt user to add products).
102
+ * @returns {Promise<number>}
103
+ */
104
+ export async function countProducts() {
105
+ const res = await wixApiRequest("/stores/v3/products/count", {
106
+ method: "POST",
107
+ body: { filter: { visible: true } },
108
+ });
109
+ return res?.count ?? 0;
110
+ }
111
+
112
+ /**
113
+ * Query Wix Stores categories (one page).
114
+ * @param {{ limit?: number, cursor?: string }} [options]
115
+ * @returns {Promise<{ categories: object[], nextCursor: string|null }>}
116
+ */
117
+ export async function queryCategories({ limit = 100, cursor } = {}) {
118
+ const res = await wixApiRequest("/categories/v1/categories/query", {
119
+ method: "POST",
120
+ body: {
121
+ treeReference: { appNamespace: "@wix/stores", treeKey: null },
122
+ query: { cursorPaging: cursor ? { limit, cursor } : { limit } },
123
+ },
124
+ });
125
+ return {
126
+ categories: res?.categories ?? [],
127
+ nextCursor: res?.pagingMetadata?.cursors?.next ?? null,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Get a single category by its URL slug. Returns null if not found.
133
+ * @param {string} slug
134
+ * @returns {Promise<object|null>}
135
+ */
136
+ export async function getCategoryBySlug(slug) {
137
+ const res = await wixApiRequest(`/categories/v1/categories/slug/${encodeURIComponent(slug)}`, {
138
+ method: "GET",
139
+ query: { "treeReference.appNamespace": "@wix/stores" },
140
+ });
141
+ return res?.category ?? null;
142
+ }