@wix/agent-skills 1.13.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 (250) 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 -244
  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/Footer.astro +0 -27
  157. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  158. package/skills/wix-headless/references/astro/templates/Navigation.astro +0 -38
  159. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  160. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  161. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  162. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  163. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  164. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  165. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  166. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  167. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  168. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  169. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  170. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  171. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  172. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  173. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  174. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  175. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  176. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  177. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  178. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  179. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  180. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  181. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  182. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  183. package/skills/wix-headless/references/astro/templates/index.astro +0 -56
  184. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  185. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  186. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  187. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  188. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  189. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  190. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  191. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  192. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  193. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  194. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  195. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  196. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  197. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  198. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  199. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  200. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  201. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  202. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  203. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  204. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  205. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  206. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  207. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  208. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  209. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  210. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  211. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  212. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  213. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  214. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  215. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  216. package/skills/wix-headless/references/seed-recipes.md +0 -49
  217. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  218. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  219. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  220. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  221. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  222. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  223. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  224. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  225. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  226. package/skills/wix-headless/references/shared/STYLING.md +0 -141
  227. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  228. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  229. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  230. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  231. package/skills/wix-headless/references/verticals/blog.md +0 -31
  232. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  233. package/skills/wix-headless/references/verticals/cms.md +0 -30
  234. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  235. package/skills/wix-headless/references/verticals/forms.md +0 -26
  236. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  237. package/skills/wix-headless/references/verticals/stores.md +0 -33
  238. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  239. package/skills/wix-headless/scripts/compose.mjs +0 -525
  240. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  241. package/skills/wix-headless/scripts/flatten.sh +0 -61
  242. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  243. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -148
  244. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  245. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  246. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  247. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  248. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  249. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  250. 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
+ }