@wix/agent-skills 1.14.0 → 1.15.1

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 (268) hide show
  1. package/README.md +12 -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/DATA_COLLECTION.md +5 -3
  10. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +25 -9
  11. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  12. package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
  13. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  31. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  32. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  33. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -0
  34. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  35. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +151 -0
  36. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  37. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  38. package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +57 -0
  39. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +23 -105
  40. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  41. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  42. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +18 -16
  43. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  44. package/skills/wix-app/references/stores/CATEGORIES.md +22 -0
  45. package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
  46. package/skills/wix-app/references/stores/INVENTORY.md +37 -0
  47. package/skills/wix-app/references/stores/QUERY.md +87 -0
  48. package/skills/wix-app/references/stores/WRITE.md +55 -0
  49. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  50. package/skills/wix-auth/SKILL.md +12 -0
  51. package/skills/wix-auth/references/device-flow.md +140 -0
  52. package/skills/wix-docs/SKILL.md +188 -0
  53. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  54. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  55. package/skills/wix-headless/SKILL.md +89 -130
  56. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  57. package/skills/wix-headless/entry/skill.md +76 -0
  58. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  59. package/skills/wix-headless/references/CAPABILITIES.md +86 -0
  60. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  61. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  62. package/skills/wix-headless/references/FEEDBACK.md +129 -0
  63. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  64. package/skills/wix-headless/references/SDK_HANDOFF.md +99 -0
  65. package/skills/wix-headless/references/SEED.md +45 -187
  66. package/skills/wix-headless/references/SETUP.md +32 -103
  67. package/skills/wix-headless/references/astro.md +98 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  72. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  73. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  74. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  75. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  76. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  77. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  78. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  79. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  80. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +213 -0
  81. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  83. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +232 -0
  84. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  85. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  86. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  87. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +192 -0
  88. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +155 -0
  89. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  90. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
  91. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +197 -0
  92. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +136 -0
  93. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  94. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  95. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  96. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  97. package/skills/wix-headless/references/managed/DEPLOYMENT.md +79 -0
  98. package/skills/wix-headless/references/non-astro.md +59 -0
  99. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  100. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +15 -0
  101. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  102. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +71 -0
  103. package/skills/wix-manage/SKILL.md +29 -3
  104. package/skills/wix-manage/references/analytics/query-site-analytics.md +298 -0
  105. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -0
  106. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  107. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +6 -5
  108. package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
  109. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +299 -0
  110. package/skills/wix-manage/references/cms/cms-schema-management.md +6 -0
  111. package/skills/wix-manage/references/get-paid/how-to-setup-wix-payments.md +30 -7
  112. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +517 -0
  113. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +218 -0
  114. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  115. package/skills/wix-manage/references/sites/site-import.md +270 -0
  116. package/skills/wix-manage/references/stores/create-product-from-image.md +8 -6
  117. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +57 -0
  118. package/skills/wix-vibe-headless/SKILL.md +142 -0
  119. package/skills/wix-vibe-headless/platforms/README.md +22 -0
  120. package/skills/wix-vibe-headless/platforms/base44.md +142 -0
  121. package/skills/wix-vibe-headless/platforms/generic.md +87 -0
  122. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  123. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  124. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +157 -0
  125. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  126. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +217 -0
  127. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  128. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  129. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  130. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +136 -0
  131. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  132. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  133. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  134. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  135. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +223 -0
  136. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  137. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  138. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  139. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  140. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  141. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  142. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  143. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +164 -0
  144. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  145. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +146 -0
  146. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  147. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  148. package/skills/wix-headless/references/BUILD.md +0 -105
  149. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  150. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  151. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  152. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  153. package/skills/wix-headless/references/PLAN-create.md +0 -17
  154. package/skills/wix-headless/references/PLAN.md +0 -92
  155. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  156. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  157. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  158. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  159. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  160. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  161. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  162. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  163. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  164. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  165. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  166. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  167. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  168. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  169. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  170. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  171. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  172. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  173. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  174. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  175. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  176. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  177. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  178. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  179. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  180. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  181. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  182. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  183. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  184. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  185. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  186. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  187. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  188. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  189. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  190. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  191. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  192. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  193. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  194. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  195. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  196. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  197. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  198. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  199. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  200. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  201. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  202. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  203. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  204. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  205. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  206. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  207. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  208. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  209. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  210. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  211. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  212. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  213. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  214. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  215. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  216. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  217. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  218. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  219. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  220. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  221. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  222. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  223. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  224. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  225. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  226. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  227. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  228. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  229. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  230. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  231. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  232. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  233. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  234. package/skills/wix-headless/references/seed-recipes.md +0 -49
  235. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  236. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  237. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  238. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  239. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  240. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  241. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  242. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  243. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  244. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  245. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  246. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  247. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  248. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  249. package/skills/wix-headless/references/verticals/blog.md +0 -31
  250. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  251. package/skills/wix-headless/references/verticals/cms.md +0 -30
  252. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  253. package/skills/wix-headless/references/verticals/forms.md +0 -26
  254. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  255. package/skills/wix-headless/references/verticals/stores.md +0 -33
  256. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  257. package/skills/wix-headless/scripts/compose.mjs +0 -520
  258. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  259. package/skills/wix-headless/scripts/flatten.sh +0 -61
  260. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  261. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  262. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  263. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  264. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  265. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  266. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  267. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  268. package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
@@ -0,0 +1,387 @@
1
+ import {
2
+ wixApiRequest,
3
+ WIX_API_BASE,
4
+ WIX_CLIENT_ID,
5
+ setSessionTokens,
6
+ clearSession,
7
+ isMember,
8
+ } from "./wix-client.js";
9
+
10
+ /**
11
+ * Custom member login for a client-only headless front end — the REST analog of
12
+ * the SDK `OAuthStrategy` (`register`/`login`/`getMemberTokensForDirectLogin`/
13
+ * `getAuthUrl`/`getMemberTokens`/`logout`). **The front owns its login UI**; the
14
+ * member never bounces to a Wix-hosted login page.
15
+ *
16
+ * Two mechanisms — pick by the brief (a page can use both):
17
+ * (A) DIRECT-CREDENTIAL — your own email+password form. `register()` / `login()`
18
+ * → on SUCCESS an INVISIBLE iframe (`web_message`) mints member tokens. No
19
+ * navigation. Supports custom sign-up fields via `profile`.
20
+ * (B) SOCIAL / SSO — your own "Continue with Google/Facebook" (or custom SSO)
21
+ * button. Full-page redirect to the provider → back to YOUR `/callback` →
22
+ * token exchange. `idp` is always a connection id (see `IDP`).
23
+ *
24
+ * All paths converge on ONE end state: member tokens written onto the shared
25
+ * client via `setSessionTokens` (wix-client.js). Member tokens are the SAME shape
26
+ * as visitor tokens, so EVERY later `wixApiRequest` (cart, orders, "my …" reads)
27
+ * now runs as the member — login just swaps the token set on the client you have.
28
+ *
29
+ * State machine — `register()`/`login()`/`verifyEmail()` return `{ state, ... }`:
30
+ * - "SUCCESS" → logged in; `{ state, member }`.
31
+ * - "REQUIRE_EMAIL_VERIFICATION" → a 6-digit code was emailed; `{ state, stateToken }`.
32
+ * Collect the code, call `verifyEmail(code, stateToken)`.
33
+ * - "REQUIRE_OWNER_APPROVAL" → membership pending owner approval; `{ state }`.
34
+ * Hard failures throw a `MemberAuthError` with a `.code` (invalidCredentials,
35
+ * emailAlreadyExists, …) — surface `.message` in your form.
36
+ *
37
+ * `profile` (register, all optional): firstName, lastName, nickname, picture,
38
+ * language, privacyStatus ("PUBLIC"|"PRIVATE"), labels[], phonesV2[], addresses[],
39
+ * company, position, birthdate, and `customFields` (name→value map). Standard
40
+ * fields work as-is; arbitrary `customFields` keys must first be DEFINED in the
41
+ * Members Area app or they're silently dropped. Social (B) can't collect any of
42
+ * these — that's the reason to pick direct-credential.
43
+ *
44
+ * Docs (curl the .md): custom login <https://dev.wix.com/docs/go-headless/self-managed-headless/authentication/members/custom-login-page/custom-login/custom-login-using-the-js-sdk.md>
45
+ * · external/social login <https://dev.wix.com/docs/rest/business-management/headless-authentication/redirects/create-redirect-session>
46
+ * · Retrieve Tokens <https://dev.wix.com/docs/rest/business-management/headless-authentication/authentication/retrieve-tokens>
47
+ */
48
+
49
+ const AUTH_BASE = "/_api/iam/authentication/v2";
50
+ const VERIFY_URL = "/_api/iam/verification/v1/auth/verify";
51
+ const RECOVERY_URL = "/_api/iam/recovery/v1/send-email";
52
+ const REDIRECT_SESSION_URL = "/_api/redirects-api/v1/redirect-session";
53
+ const CURRENT_MEMBER_URL = "/members/v1/members/my";
54
+ const OAUTH_STASH_KEY = `wix-oauth-data-${WIX_CLIENT_ID}`;
55
+
56
+ /**
57
+ * `idp` connection ids for social login. Google & Facebook are enabled by default
58
+ * on every headless client. For custom SSO / OIDC (Okta, Auth0, …) pass the
59
+ * connection id of a connection you created in the Business Manager instead.
60
+ */
61
+ export const IDP = {
62
+ GOOGLE: "0e6a50f5-b523-4e29-990d-f37fa2ffdd69",
63
+ FACEBOOK: "3ecad13f-52c3-483d-911f-31dbcf2a6d23",
64
+ };
65
+
66
+ /** Auth outcome you can show the user. `.code` is stable; `.message` is human text. */
67
+ export class MemberAuthError extends Error {
68
+ constructor(code, message) {
69
+ super(message || code);
70
+ this.name = "MemberAuthError";
71
+ this.code = code;
72
+ }
73
+ }
74
+
75
+ // ─────────────────────────── (A) direct-credential ───────────────────────────
76
+
77
+ /**
78
+ * Sign up a new member with email + password (+ optional custom `profile`).
79
+ * On SUCCESS the member is logged in (member tokens written to the shared client).
80
+ *
81
+ * @param {string} email
82
+ * @param {string} password
83
+ * @param {object} [profile] see the `profile` note in the file header
84
+ * @returns {Promise<{ state: string, member?: object|null, stateToken?: string }>}
85
+ */
86
+ export async function register(email, password, profile) {
87
+ return runCredentialFlow(`${AUTH_BASE}/register`, {
88
+ loginId: { email },
89
+ password,
90
+ ...(profile ? { profile } : {}),
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Log an existing member in with email + password.
96
+ * @param {string} email
97
+ * @param {string} password
98
+ * @returns {Promise<{ state: string, member?: object|null, stateToken?: string }>}
99
+ */
100
+ export async function login(email, password) {
101
+ return runCredentialFlow(`${AUTH_BASE}/login`, { loginId: { email }, password });
102
+ }
103
+
104
+ /**
105
+ * Continue a "REQUIRE_EMAIL_VERIFICATION" flow with the 6-digit code from the email.
106
+ * @param {string} code verification code the member received by email
107
+ * @param {string} stateToken the `stateToken` returned alongside REQUIRE_EMAIL_VERIFICATION
108
+ * @returns {Promise<{ state: string, member?: object|null, stateToken?: string }>}
109
+ */
110
+ export async function verifyEmail(code, stateToken) {
111
+ let res;
112
+ try {
113
+ res = await wixApiRequest(VERIFY_URL, { body: { code, stateToken } });
114
+ } catch (e) {
115
+ throw mapAuthError(e);
116
+ }
117
+ return resolveState(res);
118
+ }
119
+
120
+ /**
121
+ * Email the member a password-reset link. Wix hosts the reset page and returns
122
+ * them to `redirectUri`, which MUST be an allow-listed authorization redirect URI
123
+ * on the OAuth app (see INSTRUCTIONS.md → dashboard links).
124
+ * @param {string} email
125
+ * @param {string} redirectUri full URL to return to after reset
126
+ */
127
+ export async function sendPasswordResetEmail(email, redirectUri) {
128
+ await wixApiRequest(RECOVERY_URL, {
129
+ body: { email, redirect: { url: redirectUri, clientId: WIX_CLIENT_ID } },
130
+ });
131
+ }
132
+
133
+ async function runCredentialFlow(path, body) {
134
+ let res;
135
+ try {
136
+ res = await wixApiRequest(path, { body });
137
+ } catch (e) {
138
+ throw mapAuthError(e);
139
+ }
140
+ return resolveState(res);
141
+ }
142
+
143
+ // On SUCCESS, finish login and return the member; otherwise hand the state (and
144
+ // stateToken, if any) back to the UI so it can prompt for verification etc.
145
+ async function resolveState(res) {
146
+ if (res.state === "SUCCESS") {
147
+ const member = await completeDirectLogin(res.sessionToken);
148
+ return { state: res.state, member };
149
+ }
150
+ return { state: res.state, stateToken: res.stateToken };
151
+ }
152
+
153
+ // The `getMemberTokensForDirectLogin` analog: turn a session token into member
154
+ // tokens via an INVISIBLE iframe (responseMode: web_message, prompt: none). The
155
+ // member never sees a page — this is the silent plumbing behind your own form.
156
+ async function completeDirectLogin(sessionToken) {
157
+ const pkce = await generatePkce();
158
+ const { fullUrl } = await createRedirectSession({
159
+ authRequest: { responseMode: "web_message", sessionToken, pkce },
160
+ });
161
+ const { code } = await authorizeViaHiddenIframe(fullUrl, pkce.state);
162
+ const tokens = await exchangeCode(code, pkce.codeVerifier);
163
+ setSessionTokens(tokens);
164
+ return getCurrentMember();
165
+ }
166
+
167
+ // ─────────────────────────── (B) social / SSO ───────────────────────────
168
+
169
+ /**
170
+ * Start a social / SSO login. Redirects the whole page to the provider; returns
171
+ * through YOUR `callbackUri`, where you must call `completeSocialLogin()`.
172
+ *
173
+ * @param {string} idp `IDP.GOOGLE` / `IDP.FACEBOOK`, or a custom SSO connection id
174
+ * @param {string} callbackUri your `/callback` URL — MUST be an allow-listed
175
+ * authorization redirect URI on the OAuth app (see INSTRUCTIONS.md)
176
+ * @param {string} [returnTo="/"] where to send the member after login completes
177
+ */
178
+ export async function startSocialLogin(idp, callbackUri, returnTo = "/") {
179
+ const pkce = await generatePkce();
180
+ const { fullUrl } = await createRedirectSession({
181
+ authRequest: { responseMode: "fragment", idp, redirectUri: callbackUri, pkce },
182
+ });
183
+ // The callback runs on a separate page load — carry what the exchange needs.
184
+ window.localStorage.setItem(
185
+ OAUTH_STASH_KEY,
186
+ JSON.stringify({ codeVerifier: pkce.codeVerifier, state: pkce.state, redirectUri: callbackUri, returnTo }),
187
+ );
188
+ window.location.href = fullUrl;
189
+ }
190
+
191
+ /**
192
+ * Finish a social / SSO login on the `/callback` page: reads `#code`/`#state`
193
+ * from the URL, verifies state, exchanges the code, logs the member in.
194
+ * @returns {Promise<{ member: object|null, returnTo: string }>}
195
+ */
196
+ export async function completeSocialLogin() {
197
+ const raw = window.localStorage.getItem(OAUTH_STASH_KEY);
198
+ if (!raw) throw new MemberAuthError("missingOAuthData", "No pending login found — start the login again.");
199
+ window.localStorage.removeItem(OAUTH_STASH_KEY);
200
+ const stash = JSON.parse(raw);
201
+
202
+ const params = new URLSearchParams(window.location.hash.replace(/^#/, ""));
203
+ const error = params.get("error");
204
+ if (error) throw new MemberAuthError(error, params.get("error_description") || error);
205
+ const code = params.get("code");
206
+ const state = params.get("state");
207
+ if (!code || !state) throw new MemberAuthError("missingCode", "No authorization code returned.");
208
+ if (state !== stash.state) throw new MemberAuthError("stateMismatch", "OAuth state mismatch — possible CSRF; login aborted.");
209
+
210
+ const tokens = await exchangeCode(code, stash.codeVerifier, stash.redirectUri);
211
+ setSessionTokens(tokens);
212
+ return { member: await getCurrentMember(), returnTo: stash.returnTo || "/" };
213
+ }
214
+
215
+ // ─────────────────────────── session ───────────────────────────
216
+
217
+ /** True once a member is logged in on this client. Gate account UI on it. */
218
+ export function isLoggedIn() {
219
+ return isMember();
220
+ }
221
+
222
+ /**
223
+ * Read the logged-in member (name / photo / roles / custom fields). Needs the
224
+ * **Members Area app** installed; returns `null` for an anonymous visitor or when
225
+ * the app isn't installed (that's setup, not a code bug — see INSTRUCTIONS.md).
226
+ * @returns {Promise<object|null>}
227
+ */
228
+ export async function getCurrentMember() {
229
+ try {
230
+ const res = await wixApiRequest(CURRENT_MEMBER_URL, { method: "GET", query: { fieldSet: "FULL" } });
231
+ return res?.member || null;
232
+ } catch (e) {
233
+ if (e.status === 401 || e.status === 403 || e.status === 404) return null;
234
+ throw e;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Log the member out: clears the local session immediately, then redirects
240
+ * through the Wix logout URL so the server session is dropped too. After this the
241
+ * client is a clean anonymous visitor again.
242
+ * @param {string} [returnTo] where to land after logout (defaults to the origin)
243
+ */
244
+ export async function logout(returnTo) {
245
+ const postFlowUrl = returnTo || (typeof window !== "undefined" ? window.location.origin : "/");
246
+ let logoutUrl;
247
+ try {
248
+ const { redirectSession } = await wixApiRequest(REDIRECT_SESSION_URL, {
249
+ body: { logout: { clientId: WIX_CLIENT_ID }, callbacks: { postFlowUrl } },
250
+ });
251
+ logoutUrl = redirectSession?.fullUrl;
252
+ } finally {
253
+ clearSession(); // drop local tokens even if building the logout URL failed
254
+ }
255
+ if (logoutUrl && typeof window !== "undefined") window.location.href = logoutUrl;
256
+ }
257
+
258
+ // ─────────────────────────── internals ───────────────────────────
259
+
260
+ async function createRedirectSession({ authRequest }) {
261
+ const { responseMode, sessionToken, idp, redirectUri, pkce } = authRequest;
262
+ // ⚠️ EXACT Wix wire shape — do NOT simplify or inline this. It is NOT the input
263
+ // object: the request needs the `auth.authRequest` wrapper, the PKCE fields FLAT
264
+ // (`codeChallenge` + `codeChallengeMethod`, not a nested `pkce` object; the
265
+ // `codeVerifier` is NOT sent here — it's kept for the token exchange), and
266
+ // `responseType`/`scope` present. Spreading the input straight onto the body →
267
+ // 400 Bad Request.
268
+ const { redirectSession } = await wixApiRequest(REDIRECT_SESSION_URL, {
269
+ body: {
270
+ auth: {
271
+ authRequest: {
272
+ clientId: WIX_CLIENT_ID,
273
+ codeChallenge: pkce.codeChallenge,
274
+ codeChallengeMethod: "S256",
275
+ responseMode,
276
+ responseType: "code",
277
+ scope: "offline_access",
278
+ state: pkce.state,
279
+ ...(redirectUri ? { redirectUri } : {}),
280
+ ...(sessionToken ? { sessionToken } : {}),
281
+ ...(idp ? { idp } : {}),
282
+ },
283
+ },
284
+ },
285
+ });
286
+ if (!redirectSession?.fullUrl) throw new MemberAuthError("noRedirectSession", "Wix did not return an authorization URL.");
287
+ return { fullUrl: redirectSession.fullUrl };
288
+ }
289
+
290
+ // Open the authorize URL in a hidden iframe and wait for the web_message postMessage
291
+ // carrying { code, state }. Matches @wix/sdk iframeUtils.addPostMessageListener.
292
+ function authorizeViaHiddenIframe(authUrl, expectedState) {
293
+ return new Promise((resolve, reject) => {
294
+ const iframe = document.createElement("iframe");
295
+ iframe.style.display = "none";
296
+ let settled = false;
297
+ const cleanup = () => {
298
+ window.removeEventListener("message", onMessage);
299
+ clearTimeout(timer);
300
+ if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
301
+ };
302
+ const onMessage = (e) => {
303
+ if (!e.data || e.data.state !== expectedState) return; // not our message
304
+ settled = true;
305
+ cleanup();
306
+ if (e.data.error) reject(new MemberAuthError(e.data.error, e.data.error_description || e.data.error));
307
+ else resolve({ code: e.data.code, state: e.data.state });
308
+ };
309
+ window.addEventListener("message", onMessage);
310
+ const timer = setTimeout(() => {
311
+ if (!settled) {
312
+ cleanup();
313
+ // The overwhelmingly common cause: this app's ORIGIN is not an allowed
314
+ // authorization redirect URI on the OAuth app, so the browser silently
315
+ // blocks the iframe's postMessage (check the console for a "target origin
316
+ // … does not match" error). Register the origin — see INSTRUCTIONS.md.
317
+ reject(new MemberAuthError(
318
+ "timeout",
319
+ `Login timed out. Most likely this app's origin (${typeof window !== "undefined" ? window.location.origin : "?"}) ` +
320
+ `is not an allowed authorization redirect URI on the Wix OAuth app — register it in the site's Headless Settings.`,
321
+ ));
322
+ }
323
+ }, 120000);
324
+ iframe.src = authUrl;
325
+ document.body.appendChild(iframe);
326
+ });
327
+ }
328
+
329
+ // Exchange an authorization code for member tokens. No Authorization header — this
330
+ // is the token-minting endpoint (mirrors the SDK's fetchTokens). `redirectUri` is
331
+ // sent only for social (it must match the redirect-session's redirectUri).
332
+ async function exchangeCode(code, codeVerifier, redirectUri) {
333
+ if (!code) throw new MemberAuthError("missingCode", "No authorization code to exchange.");
334
+ const res = await fetch(`${WIX_API_BASE}/oauth2/token`, {
335
+ method: "POST",
336
+ headers: { "Content-Type": "application/json" },
337
+ body: JSON.stringify({
338
+ clientId: WIX_CLIENT_ID,
339
+ grantType: "authorization_code",
340
+ code,
341
+ codeVerifier,
342
+ ...(redirectUri ? { redirectUri } : {}),
343
+ }),
344
+ });
345
+ if (!res.ok) {
346
+ let body;
347
+ try { body = await res.json(); } catch { /* ignore */ }
348
+ throw new MemberAuthError(body?.error || "tokenExchangeFailed", `Token exchange failed: ${res.status}${body ? ` ${JSON.stringify(body)}` : ""}`);
349
+ }
350
+ const data = await res.json();
351
+ return { accessToken: data.access_token, refreshToken: data.refresh_token, expiresIn: data.expires_in };
352
+ }
353
+
354
+ function mapAuthError(e) {
355
+ const code = e?.body?.details?.applicationError?.code;
356
+ // Email already exists (register) — route them to log in.
357
+ if (code === "-19995" || e?.status === 409) {
358
+ return new MemberAuthError("emailAlreadyExists", "An account with this email already exists — try logging in instead.");
359
+ }
360
+ // Bad credentials: -19999 identity-not-found (404), -19976 wrong-password (401).
361
+ if (code === "-19999" || code === "-19976" || e?.status === 404 || e?.status === 401) {
362
+ return new MemberAuthError("invalidCredentials", "Incorrect email or password.");
363
+ }
364
+ if (e instanceof MemberAuthError) return e;
365
+ return e; // unknown — bubble the raw transport error (fail loudly)
366
+ }
367
+
368
+ // ── PKCE (RFC 7636, S256) — browser Web Crypto, dependency-free ──
369
+ async function generatePkce() {
370
+ const codeVerifier = randomUnreserved(43);
371
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));
372
+ return { codeVerifier, codeChallenge: base64Url(new Uint8Array(digest)), state: randomUnreserved(24) };
373
+ }
374
+
375
+ function randomUnreserved(length) {
376
+ const mask = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
377
+ const bytes = crypto.getRandomValues(new Uint8Array(length));
378
+ let out = "";
379
+ for (let i = 0; i < length; i++) out += mask[bytes[i] % mask.length];
380
+ return out;
381
+ }
382
+
383
+ function base64Url(bytes) {
384
+ let binary = "";
385
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
386
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
387
+ }
@@ -0,0 +1,111 @@
1
+
2
+ # Wix Portfolio Skill
3
+
4
+ > **Source files (in this skill):** the shared transport `references/shared/wix-client.js` and this vertical's `references/portfolio/wix-portfolio.js`. Copy **both** into your app's `src/rest/` side by side — the helper does `import { wixApiRequest } from "./wix-client.js"`, so they must land in the same folder.
5
+
6
+ Builds a real, client-only Wix portfolio showcase. The browser talks to Wix directly over a
7
+ public `WIX_CLIENT_ID`. Portfolio is **read-only**: never mock projects, never invent media,
8
+ and always render live Wix data (or an honest empty state). The content tree is
9
+ **Collection → Project → Project Item (image/video)**.
10
+
11
+ ## When to use
12
+ - User wants a Wix portfolio / showcase site or asks to "connect Wix Portfolio".
13
+ - Replacing placeholder/mock galleries with live Wix Portfolio data.
14
+ - Adding a collections gallery, project grids, or a project detail page (media gallery +
15
+ details/links) over an existing Wix Portfolio.
16
+
17
+ ## Prerequisites
18
+ 1. A Wix site with **Wix Portfolio installed and content already added** — at least one
19
+ collection with projects, and projects with items (this skill does NOT provision; it is
20
+ read-only over the content).
21
+ 2. The site's public headless **`WIX_CLIENT_ID`**, provided in the handoff prompt (the Wix
22
+ Business Manager surfaces a copyable prompt with the id filled in — see the router `SKILL.md`).
23
+ Paste it into `src/rest/wix-client.js` in place of the placeholder. It is a visitor-facing
24
+ credential (it only mints anonymous visitor tokens), **not** a secret, so
25
+ hardcoding/committing it is fine.
26
+ 3. If the read calls return `403`/`428` before content is published, the Portfolio app or its
27
+ content may not be live yet. That is a **Wix dashboard setup step the owner completes** —
28
+ out of this skill's scope. Flag it and continue; don't fall back to mock data.
29
+
30
+ ## The API (copy as-is; do not re-derive it)
31
+ This skill ships only the REST layer — no UI components. Build the portfolio's UI however the
32
+ project wants; wire it to these two snippets. Copy them into the app (e.g. `src/api/`) and only
33
+ adjust import paths:
34
+ - `src/rest/wix-client.js` — visitor-token mint/refresh + transport. Set `WIX_CLIENT_ID` to the
35
+ id from the prompt (replace the `<YOUR-CLIENT-ID>` placeholder). The visitor refresh token is
36
+ persisted to localStorage; do not re-mint anonymously per load.
37
+ - `src/rest/wix-portfolio.js` — exports:
38
+ - **Collections:** `queryCollections`, `getCollectionBySlug`, `countCollections`
39
+ - **Projects:** `queryProjects`, `queryProjectsByCollection`, `getProjectBySlug`, `getProject`
40
+ - **Project items (media):** `listProjectItems`
41
+
42
+ The Collection, Project, and Project Item shapes are documented as JSDoc comments at the top of
43
+ `wix-portfolio.js`. Read them before building the UI — they describe the key fields (cover
44
+ image/video, media resolutions, details rows) and link to the full API reference.
45
+
46
+ ## How to wire it (UI is the project's choice)
47
+ - **Collections gallery (home)** — `queryCollections()` for the listing (visible collections
48
+ only, in dashboard order); pass `nextCursor` back as `cursor` to load the next page. Render
49
+ `collection.title`, `collection.description`, and `collection.coverImage.imageInfo.url`. Link
50
+ each card to a collection page keyed on `collection.slug`.
51
+ - **Collection page** — `getCollectionBySlug(slug)` for the header (returns null on miss — show
52
+ a not-found state, never invent one). Then `queryProjectsByCollection(collection.id, { limit?, cursor? })`
53
+ for its project grid; paginate exactly like `queryCollections`.
54
+ - **All-work gallery (optional)** — `queryProjects()` to list every visible project across
55
+ collections (newest-first); paginate the same way.
56
+ - **Project grid card** — render `project.title` and the cover: use `project.coverImage.imageInfo.url`
57
+ when present, else the `project.coverVideo.videoInfo.posters[0]` / first
58
+ `coverVideo.videoInfo.resolutions[]` entry. Link each card to a project page keyed on `project.slug`.
59
+ - **Project detail page** — `getProjectBySlug(slug)` for the header (title, description, and the
60
+ `details[]` rows: each has a `label` plus either `text` or `link: { text, url, target }`).
61
+ Then `listProjectItems(project.id)` for the media gallery — render IMAGE items from
62
+ `image.imageInfo.url` and VIDEO items from the first `video.videoInfo.resolutions[]` entry
63
+ (with `video.videoInfo.posters[0]` as the poster). Honor each item's optional `link`.
64
+ - If you already hold a project id (not a slug), use `getProject(projectId)` instead.
65
+ - **Empty state** — if `countCollections()` is 0, show an empty state telling the user to add
66
+ collections and projects in their Wix dashboard. Never invent projects or media.
67
+
68
+ ## Hard rules (do not violate)
69
+ - ✅ Render only live Wix Portfolio data — collections, projects, and items as returned.
70
+ - ❌ Never mock projects, collections, or media — render live data or the empty state.
71
+ - ❌ Never hand-build Wix Media URLs or page permalinks — use the `url`/`imageInfo.url`/
72
+ `videoInfo.resolutions[].url` fields Wix returns.
73
+ - ❌ Never invent project details, captions, dates, or testimonials. Show only what the API returns.
74
+ - ❌ Never show `hidden: true` collections or projects to visitors (the helpers already filter
75
+ `hidden` out — don't add them back).
76
+ - ✅ Set `WIX_CLIENT_ID` from the prompt's value (public client id — safe to hardcode).
77
+ - ✅ Route on `slug` (`getCollectionBySlug` / `getProjectBySlug`); fall back to `getProject(id)`
78
+ only when you hold a GUID.
79
+ - The helpers fail soft on read: single fetches return `null` on a miss so you render a
80
+ not-found/empty state rather than a crash — surface that state, don't paper over it with fake data.
81
+
82
+ ## Beyond the snippets
83
+ The snippets cover the common portfolio paths. If you hit a use case they don't cover (e.g.
84
+ collection SEO tags, project watermark settings, portfolio-wide settings, or filtering by a
85
+ field not shown), make the call yourself with `wixApiRequest` — but look up the exact endpoint,
86
+ HTTP method, and request body in the **official Wix API reference** first; never guess:
87
+ - Wix Portfolio API reference: https://dev.wix.com/docs/api-reference/business-solutions/portfolio.md
88
+ - Portfolio Settings (logo, layout, options): https://dev.wix.com/docs/api-reference/business-solutions/portfolio/portfolio-settings/get-portfolio-settings.md
89
+ - API query language (filters, sort, paging): https://dev.wix.com/docs/rest/articles/getting-started/api-query-language.md
90
+
91
+ Keep the snippets as the default for everything they already do; reach for the API reference
92
+ only for the gap.
93
+
94
+ ## Point the user to their dashboard
95
+ In some cases, users need to access the Wix dashboard in order to edit the portfolio content for their site. To facilitate this, provide the user with deep links directly to the relevant dashboard pages. For portfolio data those pages are:
96
+ - **Portfolio** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-portfolio/projects` (`Dashboard → Portfolio`). Projects and Collections are tabs on this one page: the **Projects** tab adds projects and their media galleries; the **Collections** tab groups projects into collections.
97
+
98
+ Substitute the site's `metaSiteId` to complete the links (you have it from the handoff / `ListWixSites`). Include the in-dashboard navigation as a fallback.
99
+
100
+ ## Verification checklist (before declaring done)
101
+ - [ ] `WIX_CLIENT_ID` set to the prompt's value (not the `<YOUR-CLIENT-ID>` placeholder)
102
+ - [ ] Visitor token persists across reload (no re-mint storm; reads stay fast)
103
+ - [ ] Collections gallery renders live collections with cover images, in dashboard order
104
+ - [ ] Clicking a collection lists its projects via `queryProjectsByCollection`
105
+ - [ ] Project page renders the project's media gallery from `listProjectItems` (images AND videos)
106
+ - [ ] Project `details[]` rows render (text rows and link rows both)
107
+ - [ ] Slug routing works; an unknown slug shows a not-found state (no invented project)
108
+ - [ ] Hidden collections/projects never appear
109
+ - [ ] Empty state shown when `countCollections()` is 0
110
+ - [ ] No mock projects, collections, or media anywhere
111
+ - [ ] Told the user at least once that they can continue setting up their portfolio in the dashboard and provided deep links.