@wix/agent-skills 1.21.0 → 1.23.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 (269) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +71 -7
  3. package/skills/wix-app/references/APP_VALIDATION.md +16 -10
  4. package/skills/wix-app/references/DASHBOARD_PAGE.md +11 -25
  5. package/skills/wix-app/references/DATA_COLLECTION.md +16 -219
  6. package/skills/wix-app/references/SDK_MODULE_MAP.md +2 -0
  7. package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +182 -0
  8. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +97 -0
  9. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_COLLECTION.md +184 -0
  10. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +203 -0
  11. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_SETTINGS.md +44 -0
  12. package/skills/wix-app/references/dashboard-page/QUERY_AND_PAGING.md +182 -0
  13. package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +133 -0
  14. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +10 -5
  15. package/skills/wix-app/references/data-collection/LIFECYCLE.md +68 -0
  16. package/skills/wix-app/references/data-collection/PERMISSIONS.md +47 -0
  17. package/skills/wix-app/references/data-collection/SCHEMA.md +181 -0
  18. package/skills/wix-app/references/data-collection/WIX_DATA.md +38 -66
  19. package/skills/wix-headless-fast/SKILL.md +95 -230
  20. package/skills/wix-headless-fast/cold-start/bootstrap.mjs +156 -0
  21. package/skills/wix-headless-fast/{entry/skill.md → cold-start/cold-start.md} +17 -14
  22. package/skills/wix-headless-fast/guides/existing-site.md +78 -0
  23. package/skills/wix-headless-fast/guides/migration.md +43 -0
  24. package/skills/wix-headless-fast/guides/reference-mode.md +100 -0
  25. package/skills/wix-headless-fast/install/AGENTS.md +9 -4
  26. package/skills/wix-headless-fast/install/agents-md.mjs +5 -1
  27. package/skills/wix-headless-fast/install/attach.mjs +25 -47
  28. package/skills/wix-headless-fast/install/check.mjs +87 -0
  29. package/skills/wix-headless-fast/install/context.mjs +126 -0
  30. package/skills/wix-headless-fast/install/deploy.mjs +106 -80
  31. package/skills/wix-headless-fast/install/lock.mjs +55 -0
  32. package/skills/wix-headless-fast/install/setup.mjs +83 -23
  33. package/skills/wix-headless-fast/install/templates.mjs +168 -0
  34. package/skills/wix-manage/SKILL.md +3 -3
  35. package/skills/wix-manage/references/app-installation/install-wix-apps.md +68 -2
  36. package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +37 -4
  37. package/skills/wix-manage/references/ecommerce/ecom-pricing.md +29 -3
  38. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +27 -0
  39. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +83 -37
  40. package/skills/wix-manage/references/sites/site-import.md +101 -37
  41. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +0 -303
  42. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +0 -136
  43. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +0 -112
  44. package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +0 -42
  45. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +0 -31
  46. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +0 -22
  47. package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +0 -137
  48. package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +0 -78
  49. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +0 -117
  50. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +0 -52
  51. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +0 -47
  52. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +0 -58
  53. package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +0 -62
  54. package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +0 -40
  55. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +0 -97
  56. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +0 -24
  57. package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +0 -16156
  58. package/skills/wix-headless-fast/references/blog/rest/posts.ts +0 -42
  59. package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +0 -62
  60. package/skills/wix-headless-fast/references/blog/seed/SEED.md +0 -81
  61. package/skills/wix-headless-fast/references/blog/seed/read-site.mjs +0 -22
  62. package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +0 -287
  63. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +0 -277
  64. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +0 -144
  65. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +0 -119
  66. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +0 -32
  67. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +0 -23
  68. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +0 -218
  69. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +0 -169
  70. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +0 -121
  71. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +0 -92
  72. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +0 -90
  73. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +0 -57
  74. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +0 -72
  75. package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +0 -40
  76. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +0 -76
  77. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +0 -22
  78. package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +0 -11962
  79. package/skills/wix-headless-fast/references/bookings/rest/booking.ts +0 -113
  80. package/skills/wix-headless-fast/references/bookings/rest/services.ts +0 -55
  81. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +0 -71
  82. package/skills/wix-headless-fast/references/bookings/seed/read-site.mjs +0 -28
  83. package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +0 -302
  84. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +0 -355
  85. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +0 -50
  86. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +0 -37
  87. package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +0 -158
  88. package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +0 -93
  89. package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +0 -177
  90. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +0 -123
  91. package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +0 -75
  92. package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +0 -41
  93. package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +0 -11262
  94. package/skills/wix-headless-fast/references/cms/rest/items.ts +0 -125
  95. package/skills/wix-headless-fast/references/cms/seed/SEED.md +0 -112
  96. package/skills/wix-headless-fast/references/cms/seed/read-site.mjs +0 -20
  97. package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +0 -324
  98. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +0 -307
  99. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +0 -66
  100. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +0 -178
  101. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +0 -118
  102. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +0 -35
  103. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +0 -24
  104. package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +0 -130
  105. package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +0 -97
  106. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +0 -55
  107. package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +0 -113
  108. package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +0 -156
  109. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +0 -97
  110. package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +0 -61
  111. package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +0 -40
  112. package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +0 -12
  113. package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +0 -80
  114. package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +0 -21
  115. package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +0 -11884
  116. package/skills/wix-headless-fast/references/events/rest/events.ts +0 -57
  117. package/skills/wix-headless-fast/references/events/rest/registration.ts +0 -83
  118. package/skills/wix-headless-fast/references/events/seed/SEED.md +0 -95
  119. package/skills/wix-headless-fast/references/events/seed/read-site.mjs +0 -26
  120. package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +0 -276
  121. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +0 -340
  122. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +0 -85
  123. package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +0 -332
  124. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +0 -238
  125. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +0 -54
  126. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +0 -216
  127. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +0 -59
  128. package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +0 -135
  129. package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +0 -10593
  130. package/skills/wix-headless-fast/references/forms/rest/forms.ts +0 -34
  131. package/skills/wix-headless-fast/references/forms/rest/submissions.ts +0 -56
  132. package/skills/wix-headless-fast/references/forms/seed/SEED.md +0 -116
  133. package/skills/wix-headless-fast/references/forms/seed/read-site.mjs +0 -20
  134. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +0 -327
  135. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +0 -316
  136. package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +0 -60
  137. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +0 -155
  138. package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +0 -78
  139. package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +0 -47
  140. package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +0 -66
  141. package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +0 -105
  142. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +0 -59
  143. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +0 -53
  144. package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +0 -122
  145. package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +0 -43
  146. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +0 -29
  147. package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +0 -21
  148. package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +0 -42
  149. package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +0 -10
  150. package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +0 -11
  151. package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +0 -10329
  152. package/skills/wix-headless-fast/references/members/rest/auth.ts +0 -215
  153. package/skills/wix-headless-fast/references/members/rest/members.ts +0 -26
  154. package/skills/wix-headless-fast/references/members/seed/SEED.md +0 -39
  155. package/skills/wix-headless-fast/references/members/seed/read-site.mjs +0 -15
  156. package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +0 -94
  157. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +0 -315
  158. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +0 -110
  159. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +0 -90
  160. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +0 -35
  161. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +0 -85
  162. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +0 -32
  163. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +0 -21
  164. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +0 -31
  165. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +0 -82
  166. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +0 -58
  167. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +0 -177
  168. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +0 -78
  169. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +0 -81
  170. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +0 -55
  171. package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +0 -40
  172. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +0 -30
  173. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +0 -21
  174. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +0 -29
  175. package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +0 -10197
  176. package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +0 -75
  177. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +0 -86
  178. package/skills/wix-headless-fast/references/portfolio/seed/read-site.mjs +0 -20
  179. package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +0 -256
  180. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +0 -296
  181. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +0 -47
  182. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +0 -101
  183. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +0 -41
  184. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +0 -16
  185. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +0 -22
  186. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +0 -54
  187. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +0 -106
  188. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +0 -64
  189. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +0 -27
  190. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +0 -43
  191. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +0 -22
  192. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +0 -30
  193. package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +0 -41
  194. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +0 -40
  195. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +0 -21
  196. package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +0 -11226
  197. package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +0 -31
  198. package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +0 -20
  199. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +0 -79
  200. package/skills/wix-headless-fast/references/pricing-plans/seed/read-site.mjs +0 -21
  201. package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +0 -279
  202. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +0 -360
  203. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +0 -142
  204. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +0 -26
  205. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +0 -121
  206. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +0 -155
  207. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +0 -34
  208. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +0 -43
  209. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +0 -34
  210. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +0 -155
  211. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +0 -89
  212. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +0 -71
  213. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +0 -103
  214. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +0 -124
  215. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +0 -185
  216. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +0 -193
  217. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +0 -94
  218. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +0 -74
  219. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +0 -168
  220. package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +0 -47
  221. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +0 -22
  222. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +0 -12
  223. package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +0 -12560
  224. package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +0 -60
  225. package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +0 -169
  226. package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +0 -68
  227. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +0 -89
  228. package/skills/wix-headless-fast/references/restaurants/seed/read-site.mjs +0 -25
  229. package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +0 -499
  230. package/skills/wix-headless-fast/references/shared/CONTENT.md +0 -22
  231. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +0 -73
  232. package/skills/wix-headless-fast/references/shared/DESIGN.md +0 -26
  233. package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +0 -23
  234. package/skills/wix-headless-fast/references/shared/app/styles/global.css +0 -52
  235. package/skills/wix-headless-fast/references/shared/app/wix/config.ts +0 -15
  236. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +0 -81
  237. package/skills/wix-headless-fast/references/shared/app/wix/money.ts +0 -24
  238. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +0 -89
  239. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +0 -46
  240. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +0 -46
  241. package/skills/wix-headless-fast/references/shared/rest/client.ts +0 -155
  242. package/skills/wix-headless-fast/references/shared/rest/config.ts +0 -3
  243. package/skills/wix-headless-fast/references/shared/rest/media.ts +0 -65
  244. package/skills/wix-headless-fast/references/shared/seed/images.mjs +0 -145
  245. package/skills/wix-headless-fast/references/shared/seed/read-site.mjs +0 -99
  246. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +0 -612
  247. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +0 -26
  248. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +0 -155
  249. package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +0 -266
  250. package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +0 -95
  251. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +0 -136
  252. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +0 -48
  253. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +0 -39
  254. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +0 -34
  255. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +0 -121
  256. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +0 -107
  257. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +0 -138
  258. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +0 -314
  259. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +0 -133
  260. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +0 -147
  261. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +0 -289
  262. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +0 -169
  263. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +0 -65
  264. package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +0 -12247
  265. package/skills/wix-headless-fast/references/storefront/rest/cart.ts +0 -132
  266. package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +0 -123
  267. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +0 -130
  268. package/skills/wix-headless-fast/references/storefront/seed/read-site.mjs +0 -38
  269. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +0 -561
@@ -1,561 +0,0 @@
1
- // Storefront seed — a BUILD-TIME script, never shipped in the app. Run it from the project
2
- // root (where wix.config.json lives) with a plan file:
3
- //
4
- // node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json
5
- //
6
- // It mints its own site token via the Wix CLI (the token never leaves this process), installs
7
- // the Wix Stores app if needed, waits for the V3 catalog, bulk-creates products (variants
8
- // expanded, descriptions converted to rich text), creates categories (serially — the shared
9
- // tree 409s on concurrent creates), assigns products, and attaches images. Prints a JSON
10
- // result to stdout.
11
- //
12
- // Plan shape (see SEED.md):
13
- // { "products": [{ "name", "description", "price", "compareAtPrice"?, "quantity",
14
- // "options"?: [{ "name", "type"?: "text"|"color",
15
- // "choices": ["S","M"] | [{ "name", "colorCode" }] }],
16
- // "imageUrl"? | "imagePath"? | "imagePrompt"?, "altText"?,
17
- // "digitalFileUrl"? | "digitalFilePath"?, "digitalFileName"? }],
18
- // "categories"?: { "<category name>": ["<product name>", ...] } }
19
- //
20
- // Seeding is ADDITIVE — this script never deletes or overwrites existing content.
21
- // If a call fails with an unexpected shape, read the live API reference (the authoritative
22
- // source recipe is wix-headless/references/inline-recipes/setup-online-store.md) — never guess.
23
- import { execFileSync } from "node:child_process";
24
- import { basename } from "node:path";
25
- import { readFileSync } from "node:fs";
26
- import { resolveItemImages } from "../../shared/seed/images.mjs";
27
-
28
- const API = "https://www.wixapis.com";
29
- const STORES_APP_ID = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
30
-
31
- // ---- auth: siteId from wix.config.json, token minted by the Wix CLI ----------------------------
32
-
33
- export function makeCtx({ cwd = process.cwd() } = {}) {
34
- const config = JSON.parse(readFileSync(`${cwd}/wix.config.json`, "utf8"));
35
- const siteId = config.siteId ?? config.projectId;
36
- if (!siteId) throw new Error("wix.config.json has no siteId — is this a Wix CLI project?");
37
- // The CLI returns a byte-identical token within a run — mint once, reuse.
38
- const token = execFileSync("npx", ["@wix/cli@latest", "token", "--site", siteId], {
39
- encoding: "utf8",
40
- cwd,
41
- }).trim();
42
- if (!token) throw new Error("The Wix CLI returned no token — run `npx @wix/cli@latest login` first.");
43
- return { token, siteId };
44
- }
45
-
46
- // ---- transport ----------------------------------------------------------------------------------
47
-
48
- async function req(ctx, path, { method = "POST", body } = {}) {
49
- // Retry while the catalog is still provisioning: right after a fresh Stores install the V3
50
- // WRITE path becomes usable later than the read path, so the first bulk-create can 428 even
51
- // after the read probe clears. Wait it out (~80s budget); other errors throw immediately.
52
- // ⚠️ An errored bulk create (seen live with a bare 429 {}) may still have APPLIED
53
- // server-side — creation is idempotent by name in setupStore for exactly that reason.
54
- for (let attempt = 0; ; attempt++) {
55
- const res = await fetch(API + path, {
56
- method,
57
- headers: {
58
- Authorization: `Bearer ${ctx.token}`,
59
- "wix-site-id": ctx.siteId,
60
- "Content-Type": "application/json",
61
- },
62
- body: body ? JSON.stringify(body) : undefined,
63
- });
64
- const json = await res.json().catch(() => ({}));
65
- if (res.ok) return json;
66
- if (isProvisioning(res.status, json) && attempt < 40) {
67
- await sleep(2000);
68
- continue;
69
- }
70
- throw new Error(`${method} ${path} -> ${res.status}: ${JSON.stringify(json).slice(0, 400)}`);
71
- }
72
- }
73
-
74
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
75
-
76
- // A freshly installed catalog signals "not writable yet" with a 428 under (at least) two codes.
77
- const PROVISIONING_CODES = new Set(["CATALOG_V1_SITE_CALLING_CATALOG_V3_API", "CATALOG_V3_SITE_PROVISIONING"]);
78
-
79
- function isProvisioning(status, json) {
80
- if (PROVISIONING_CODES.has(json?.details?.applicationError?.code)) return true;
81
- return status === 428 && /provision/i.test(json?.message || "");
82
- }
83
-
84
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products.md
85
- async function waitForCatalogV3(ctx, { attempts = 40, delayMs = 2000 } = {}) {
86
- for (let i = 0; i < attempts; i++) {
87
- const res = await fetch(`${API}/stores/v3/products/query`, {
88
- method: "POST",
89
- headers: { Authorization: `Bearer ${ctx.token}`, "wix-site-id": ctx.siteId, "Content-Type": "application/json" },
90
- body: JSON.stringify({ query: { paging: { limit: 1 } } }),
91
- });
92
- if (res.ok) return;
93
- const json = await res.json().catch(() => ({}));
94
- if (!isProvisioning(res.status, json)) return;
95
- await sleep(delayMs);
96
- }
97
- }
98
-
99
- // ---- description string -> Wix rich-text nodes --------------------------------------------------
100
- // Descriptions arrive as HTML as often as not. The writable field is `description` (Ricos
101
- // nodes) — the HTML `plainDescription` the storefront renders is derived from them, so markup
102
- // dropped into a TEXT node comes back escaped and the PDP shows literal tags. Convert the tags
103
- // a model actually emits; a tag-free string stays one paragraph.
104
-
105
- const HTML_ENTITIES = { "&amp;": "&", "&lt;": "<", "&gt;": ">", "&quot;": '"', "&#39;": "'", "&nbsp;": " " };
106
-
107
- function decodeEntities(s) {
108
- return s.replace(/&(?:amp|lt|gt|quot|#39|nbsp);/g, (m) => HTML_ENTITIES[m] ?? m);
109
- }
110
-
111
- function mkTextNodes(html) {
112
- const nodes = [];
113
- let bold = 0, italic = 0, last = 0, m;
114
- const tag = /<(\/?)(strong|b|em|i)\s*\/?>/gi;
115
- const push = (raw) => {
116
- const text = decodeEntities(raw.replace(/<[^>]*>/g, ""));
117
- if (!text) return;
118
- const decorations = [];
119
- if (bold > 0) decorations.push({ type: "BOLD" });
120
- if (italic > 0) decorations.push({ type: "ITALIC" });
121
- nodes.push({ type: "TEXT", textData: { text, decorations } });
122
- };
123
- while ((m = tag.exec(html)) !== null) {
124
- push(html.slice(last, m.index));
125
- const step = m[1] ? -1 : 1;
126
- if (/^(strong|b)$/i.test(m[2])) bold = Math.max(0, bold + step);
127
- else italic = Math.max(0, italic + step);
128
- last = tag.lastIndex;
129
- }
130
- push(html.slice(last));
131
- return nodes.length ? nodes : [{ type: "TEXT", textData: { text: "", decorations: [] } }];
132
- }
133
-
134
- function mkDesc(text, i) {
135
- const blocks = String(text ?? "").split(/<\/p\s*>|<br\s*\/?>/i).map((b) => b.trim()).filter(Boolean);
136
- return {
137
- nodes: (blocks.length ? blocks : [""]).map((block, n) => ({
138
- type: "PARAGRAPH", id: `desc-${i}-${n}`,
139
- nodes: mkTextNodes(block),
140
- paragraphData: { textStyle: { textAlignment: "AUTO" } },
141
- })),
142
- metadata: { version: 1, id: `desc-meta-${i}` },
143
- };
144
- }
145
-
146
- // ---- options / variants -------------------------------------------------------------------------
147
-
148
- function buildOptions(options = []) {
149
- return options.map((o) => {
150
- const color = o.type === "color";
151
- return {
152
- name: o.name,
153
- optionRenderType: color ? "SWATCH_CHOICES" : "TEXT_CHOICES",
154
- choicesSettings: {
155
- choices: o.choices.map((c) =>
156
- color
157
- ? { choiceType: "ONE_COLOR", name: c.name, colorCode: c.colorCode }
158
- : { choiceType: "CHOICE_TEXT", name: typeof c === "string" ? c : c.name }),
159
- },
160
- };
161
- });
162
- }
163
-
164
- // Full Cartesian product, each variant priced/stocked from the product; visible:true baked in.
165
- function expandVariants(options = [], { price, compareAtPrice, quantity, inStock }, digitalFileId) {
166
- const base = {
167
- price: {
168
- actualPrice: { amount: String(price) },
169
- ...(compareAtPrice ? { compareAtPrice: { amount: String(compareAtPrice) } } : {}),
170
- },
171
- visible: true,
172
- ...(digitalFileId
173
- ? { digitalProperties: { digitalFile: { id: digitalFileId } }, inventoryItem: { inStock: true } }
174
- // inStock:true == untracked stock — always buyable, no count. Otherwise track a quantity.
175
- : { physicalProperties: {}, inventoryItem: inStock === true
176
- ? { inStock: true }
177
- : { quantity: quantity ?? 0, preorderInfo: { enabled: false } } }),
178
- };
179
- if (!options.length) return [base];
180
- let combos = [[]];
181
- for (const o of options) {
182
- const rt = o.type === "color" ? "SWATCH_CHOICES" : "TEXT_CHOICES";
183
- const names = o.choices.map((c) => (typeof c === "string" ? c : c.name));
184
- combos = combos.flatMap((combo) =>
185
- names.map((choiceName) => [...combo, { optionChoiceNames: { optionName: o.name, choiceName, renderType: rt } }]));
186
- }
187
- return combos.map((choices) => ({ ...base, choices }));
188
- }
189
-
190
- // ---- operations ---------------------------------------------------------------------------------
191
-
192
- // docs: https://dev.wix.com/docs/api-reference/articles/work-with-wix-apis/platform/about-apps-created-by-wix.md
193
- export async function installStoresApp(ctx) {
194
- try {
195
- await req(ctx, "/apps-installer-service/v1/app-instance/install", { body: {
196
- tenant: { tenantType: "SITE", id: ctx.siteId },
197
- appInstance: { appDefId: STORES_APP_ID, enabled: true },
198
- } });
199
- } catch {
200
- // already installed is fine — the readiness wait below still confirms the V3 catalog is live
201
- }
202
- await waitForCatalogV3(ctx);
203
- }
204
-
205
- // Existing products by exact name (for idempotent reruns). `name` is NOT filterable on the
206
- // V3 query — fetch a page and match client-side (seed catalogs are small). Empty map on any
207
- // failure — falling back to create-everything is the additive behavior we had before.
208
- export async function queryProductsByNames(ctx, names) {
209
- const out = new Map();
210
- if (!names.length) return out;
211
- try {
212
- const wanted = new Set(names);
213
- const r = await req(ctx, "/stores/v3/products/query", { body: { query: { cursorPaging: { limit: 100 } } } });
214
- for (const p of r.products ?? []) {
215
- if (wanted.has(p.name) && !out.has(p.name)) out.set(p.name, { id: p.id, slug: p.slug, revision: p.revision });
216
- }
217
- } catch (e) {
218
- console.error(`product name pre-check failed (creating everything): ${String(e.message).slice(0, 120)}`);
219
- }
220
- return out;
221
- }
222
-
223
- // A digital variant is SELLABLE only with BOTH a file and stock: without the file the cart rejects
224
- // it as ITEM_NOT_FOUND_IN_CATALOG, without stock as "exceeds available inventory" — and either way
225
- // the product reads back visible and in the catalog, so nothing surfaces until a buyer tries to buy.
226
- // A digitalFile* field is the only way into DIGITAL here, which makes the file-less product
227
- // unbuildable. The bytes are PUT, not imported by url: an uploaded file is READY at once, while an
228
- // imported one stays PENDING and the cart rejects the product until it settles.
229
- // docs: https://dev.wix.com/docs/api-reference/assets/media/media-manager/files/generate-file-upload-url.md
230
- const FILE_MIME = { pdf: "application/pdf", zip: "application/zip", epub: "application/epub+zip",
231
- mp3: "audio/mpeg", wav: "audio/wav", mp4: "video/mp4", png: "image/png", jpg: "image/jpeg" };
232
-
233
- async function uploadDigitalFile(ctx, { digitalFileUrl, digitalFilePath, digitalFileName }) {
234
- const src = digitalFilePath ?? digitalFileUrl;
235
- const fileName = digitalFileName || decodeURIComponent(basename(new URL(src, "file:").pathname));
236
- const mimeType = FILE_MIME[fileName.split(".").pop().toLowerCase()];
237
- if (!mimeType) throw new Error(`digitalFileName needs one of these extensions (${Object.keys(FILE_MIME).join(", ")}): ${fileName}`);
238
- const bytes = digitalFilePath
239
- ? readFileSync(digitalFilePath)
240
- : await fetch(digitalFileUrl).then((r) => {
241
- // Don't invent a file and don't ship an unbuyable DIGITAL product: seed it PHYSICAL
242
- // with stock (drop digitalFileUrl/digitalFilePath, set inStock or a quantity) and
243
- // tell the user the download still needs a real file.
244
- if (!r.ok) throw new Error(`digitalFileUrl ${digitalFileUrl} -> ${r.status}. No fetchable file: re-seed this product as PHYSICAL with stock and tell the user it needs a real file before it can be sold as a download.`);
245
- return r.arrayBuffer();
246
- });
247
- const { uploadUrl } = await req(ctx, "/site-media/v1/files/generate-upload-url", { body: { mimeType, fileName } });
248
- const res = await fetch(uploadUrl, { method: "PUT", headers: { "Content-Type": mimeType }, body: bytes });
249
- const json = await res.json().catch(() => ({}));
250
- const id = (json.file || json)?.id;
251
- if (!res.ok || !id) throw new Error(`digital file upload failed (${res.status}): ${JSON.stringify(json).slice(0, 200)}`);
252
- return id;
253
- }
254
-
255
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-create-products-with-inventory.md
256
- export async function bulkCreateProducts(ctx, products) {
257
- const fileIds = await Promise.all(products.map((p) =>
258
- p.digitalFileUrl || p.digitalFilePath ? uploadDigitalFile(ctx, p) : null));
259
- const body = {
260
- returnEntity: true,
261
- products: products.map((p, i) => ({
262
- name: p.name,
263
- // DIGITAL drops physicalProperties and can't be POS-visible (DIGITAL_PRODUCT_CANNOT_BE_VISIBLE_IN_POS).
264
- ...(fileIds[i]
265
- ? { productType: "DIGITAL" }
266
- : { productType: "PHYSICAL", physicalProperties: {}, visibleInPos: true }),
267
- visible: true,
268
- description: mkDesc(p.description, i),
269
- options: buildOptions(p.options),
270
- variantsInfo: { variants: expandVariants(p.options, p, fileIds[i]) },
271
- })),
272
- };
273
- const r = await req(ctx, "/stores/v3/bulk/products-with-inventory/create", { body });
274
- // NB: results nest under productResults.results[].item — NOT a top-level `results`.
275
- // The bulk returns 200 even on PARTIAL failure, so never map results by array position:
276
- // pair each result to its input via itemMetadata.originalIndex and drop the ones that
277
- // didn't persist. Positional mapping shifts every id after a failure onto the wrong
278
- // product — which then mislabels categories and attaches images to the wrong items.
279
- const created = [];
280
- const failures = [];
281
- for (const x of r.productResults?.results ?? []) {
282
- const i = x.itemMetadata?.originalIndex;
283
- const src = typeof i === "number" ? products[i] : undefined;
284
- if (!x.itemMetadata?.success || !x.item?.id) {
285
- failures.push({
286
- name: src?.name,
287
- error: x.itemMetadata?.error?.description ?? x.itemMetadata?.error?.code ?? "unknown",
288
- });
289
- continue;
290
- }
291
- created.push({
292
- id: x.item.id, slug: x.item.slug, revision: x.item.revision, name: src?.name,
293
- variantId: x.item.variantsInfo?.variants?.[0]?.id,
294
- hasOptions: (src?.options?.length ?? 0) > 0,
295
- isDigital: !!fileIds[i],
296
- quantity: src?.quantity ?? 0,
297
- inStock: src?.inStock,
298
- });
299
- }
300
- await stockOptionlessProducts(ctx, created);
301
- return {
302
- created: created.map((p) => ({ id: p.id, slug: p.slug, revision: p.revision, name: p.name })),
303
- failures,
304
- };
305
- }
306
-
307
- // The bulk create stocks a variant via its choices; an OPTION-LESS product's single default
308
- // variant is NOT stocked by it and lands OUT_OF_STOCK — stock those explicitly.
309
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products.md
310
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/inventory-items-v3/bulk-create-inventory-items.md
311
- async function stockOptionlessProducts(ctx, created) {
312
- const need = created.filter((p) => !p.hasOptions && !p.isDigital && p.id); // digital variants ship inStock from the create
313
- if (!need.length) return;
314
- const missing = need.filter((p) => !p.variantId).map((p) => p.id);
315
- if (missing.length) {
316
- const q = await req(ctx, "/stores/v3/products/query", { body: { query: { filter: { id: { $in: missing } }, paging: { limit: missing.length } } } });
317
- const vById = new Map((q.products ?? []).map((p) => [p.id, p.variantsInfo?.variants?.[0]?.id]));
318
- need.forEach((p) => { if (!p.variantId) p.variantId = vById.get(p.id); });
319
- }
320
- // inStock:true == untracked stock (always buyable, no count). Only send a quantity when the
321
- // product actually tracks one, or Wix rejects the pair.
322
- const inventoryItems = need
323
- .filter((p) => p.variantId)
324
- .map((p) => ({
325
- productId: p.id,
326
- variantId: p.variantId,
327
- ...(p.inStock === true ? { inStock: true } : { quantity: p.quantity }),
328
- }));
329
- if (inventoryItems.length) {
330
- await req(ctx, "/stores/v3/bulk/inventory-items/create", { body: { inventoryItems } });
331
- }
332
- }
333
-
334
- // Existing categories by name (for idempotent reruns) — a re-run of the seed must reuse
335
- // "Donuts", not create a second one. Empty map on any failure (falls back to create).
336
- export async function queryCategoriesByNames(ctx, names) {
337
- const out = new Map();
338
- if (!names.length) return out;
339
- try {
340
- const r = await req(ctx, "/categories/v1/categories/query", {
341
- body: { treeReference: { appNamespace: "@wix/stores", treeKey: null }, query: { cursorPaging: { limit: 100 } } },
342
- });
343
- const wanted = new Set(names);
344
- for (const c of r.categories ?? []) if (wanted.has(c.name) && !out.has(c.name)) out.set(c.name, c.id);
345
- } catch (e) {
346
- console.error(`category name pre-check failed (creating everything): ${String(e.message).slice(0, 120)}`);
347
- }
348
- return out;
349
- }
350
-
351
- // Categories share the @wix/stores tree revision — concurrent creates 409, so: sequential.
352
- // Idempotent by name: a name that already exists is reused, never duplicated.
353
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/create-category.md
354
- export async function createCategories(ctx, names) {
355
- const existing = await queryCategoriesByNames(ctx, names);
356
- const out = [];
357
- for (const name of names) {
358
- if (existing.has(name)) {
359
- out.push({ id: existing.get(name), name });
360
- continue;
361
- }
362
- const r = await req(ctx, "/categories/v1/categories", {
363
- body: { category: { name, visible: true }, treeReference: { appNamespace: "@wix/stores", treeKey: null } },
364
- });
365
- out.push({ id: r.category?.id, name });
366
- }
367
- return out;
368
- }
369
-
370
-
371
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/bulk-add-items-to-category.md
372
- export async function addProductsToCategories(ctx, mapping) {
373
- for (const [categoryId, productIds] of Object.entries(mapping)) {
374
- await req(ctx, `/categories/v1/bulk/categories/${categoryId}/add-items`, {
375
- body: {
376
- items: productIds.map((catalogItemId) => ({ catalogItemId, appId: STORES_APP_ID })),
377
- treeReference: { appNamespace: "@wix/stores", treeKey: null },
378
- },
379
- });
380
- }
381
- }
382
-
383
- // Bulk image attach in ONE call. items: [{ id, url, altText }] — no revision to pass: the
384
- // current revision is read right before the update, so attach any number of times, any pass.
385
- // Wix re-hosts each url server-side; the media can take a little while to appear on read-back
386
- // (propagation) — normal, not a failure.
387
- // The bulk update returns 200 on PARTIAL failure, like the bulk create: each item's outcome is
388
- // in results[].itemMetadata (success, error, originalIndex). Seen live (runs 85 and 86,
389
- // 2026-09-26): one product of three came back with no media while the call succeeded — most
390
- // likely its revision moved between the read and the update (variant stocking runs just
391
- // before). So: pair results to inputs, retry the misses once with fresh revisions, and report
392
- // what actually persisted. Returns { attached: [id], failures: [{ id, error }] }.
393
- // docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-update-products.md
394
- export async function attachProductImages(ctx, items) {
395
- if (!items?.length) return { attached: [], failures: [] };
396
- const send = async (batch) => {
397
- const ids = batch.map((it) => it.id);
398
- const q = await req(ctx, "/stores/v3/products/query", { body: { query: { filter: { id: { $in: ids } }, paging: { limit: ids.length } } } });
399
- const revById = new Map((q.products ?? []).map((p) => [p.id, p.revision]));
400
- const r = await req(ctx, "/stores/v3/bulk/products/update", {
401
- body: {
402
- products: batch.map((it) => ({
403
- product: { id: it.id, revision: revById.get(it.id), media: { itemsInfo: { items: [{ url: it.url, altText: it.altText }] } } },
404
- })),
405
- },
406
- });
407
- const ok = new Set();
408
- const failed = [];
409
- for (const x of r.results ?? []) {
410
- const src = batch[x.itemMetadata?.originalIndex];
411
- if (!src) continue;
412
- if (x.itemMetadata?.success) ok.add(src.id);
413
- else failed.push({ id: src.id, error: x.itemMetadata?.error?.description ?? x.itemMetadata?.error?.code ?? "unknown" });
414
- }
415
- // an input with no result at all did not persist either
416
- for (const it of batch) if (!ok.has(it.id) && !failed.some((f) => f.id === it.id)) failed.push({ id: it.id, error: "no result for item" });
417
- return { ok, failed };
418
- };
419
- const first = await send(items);
420
- const attached = new Set(first.ok);
421
- let failures = first.failed;
422
- if (failures.length) {
423
- const retry = await send(items.filter((it) => failures.some((f) => f.id === it.id)));
424
- for (const id of retry.ok) attached.add(id);
425
- failures = retry.failed;
426
- }
427
- return { attached: [...attached], failures };
428
- }
429
-
430
- // Reject plans the API would reject halfway through, while nothing has been created yet —
431
- // a mid-batch 400 leaves a half-seeded store that the agent then has to reason about.
432
- export function validateProducts(products) {
433
- const problems = [];
434
- const colorByName = new Map();
435
- products.forEach((p, i) => {
436
- const where = p.name ? `"${p.name}"` : `product #${i + 1}`;
437
- if (!p.name) problems.push(`${where}: name is required`);
438
- if (p.quantity != null && (!Number.isInteger(p.quantity) || p.quantity < 0)) {
439
- problems.push(`${where}: quantity must be a non-negative integer (got ${p.quantity}) — omit it and set inStock:true for untracked stock`);
440
- }
441
- for (const opt of p.options ?? []) {
442
- const seen = new Set();
443
- for (const c of opt.choices ?? []) {
444
- const key = typeof c === "string" ? c : c?.name;
445
- if (seen.has(key)) problems.push(`${where}: option "${opt.name}" repeats the choice "${key}"`);
446
- seen.add(key);
447
- // Wix keys a color choice by name, so the same name with two codes collides.
448
- const code = typeof c === "object" ? c?.colorCode : undefined;
449
- if (code) {
450
- const prev = colorByName.get(key);
451
- if (prev && prev !== code) problems.push(`color "${key}" is ${prev} on one product and ${code} on another — pick one`);
452
- colorByName.set(key, code);
453
- }
454
- }
455
- }
456
- });
457
- if (problems.length) throw new Error(`invalid seed plan:\n - ${problems.join("\n - ")}`);
458
- }
459
-
460
- // Site currency, set BEFORE any product exists (see setupStore). Existing product reads can
461
- // keep reporting the old currency for a short while after this returns — that lag is expected
462
- // and self-resolves, so don't re-verify or retry on it.
463
- // docs: https://dev.wix.com/docs/rest/business-management/site-properties/properties/update-site-properties
464
- async function setSiteCurrency(ctx, currency) {
465
- await req(ctx, "/site-properties/v4/properties", {
466
- method: "PATCH",
467
- body: { properties: { paymentCurrency: currency }, fields: ["paymentCurrency"] },
468
- });
469
- }
470
-
471
- /**
472
- * ONE-CALL seed: install → currency → create products → categories → attach images, ids
473
- * threaded in memory. This is the default path — call it once instead of the individual
474
- * functions.
475
- */
476
- export async function setupStore(ctx, { products = [], categories = {}, currency } = {}) {
477
- validateProducts(products);
478
- await installStoresApp(ctx);
479
- // Before any product exists: a product's price is stored in the site currency at create time,
480
- // so switching afterwards leaves the catalog priced in the old one.
481
- if (currency) await setSiteCurrency(ctx, currency);
482
-
483
- // Idempotent by name: an errored bulk create (429/5xx) may still have applied server-side,
484
- // and SKILL.md tells the agent to re-run a failed seed — creating only the names that don't
485
- // exist yet makes that rerun safe instead of a duplicator.
486
- const existing = await queryProductsByNames(ctx, products.map((p) => p.name));
487
- const toCreate = products.filter((p) => !existing.has(p.name));
488
- const { created, failures } = toCreate.length
489
- ? await bulkCreateProducts(ctx, toCreate)
490
- : { created: [], failures: [] };
491
- const createdByName = new Map(created.map((p) => [p.name, p]));
492
- const withNames = products.map((p) => {
493
- const hit = createdByName.get(p.name) ?? existing.get(p.name);
494
- return { ...(hit ?? {}), name: p.name };
495
- });
496
- const idByName = new Map(withNames.map((p) => [p.name, p.id]));
497
-
498
- const names = Object.keys(categories);
499
- const cats = names.length ? await createCategories(ctx, names) : [];
500
- if (cats.length) {
501
- const mapping = {};
502
- for (const c of cats) {
503
- const ids = (categories[c.name] || []).map((n) => idByName.get(n)).filter(Boolean);
504
- if (ids.length) mapping[c.id] = ids;
505
- }
506
- if (Object.keys(mapping).length) await addProductsToCategories(ctx, mapping);
507
- }
508
-
509
- // Pass 2 — images: resolve (import by url / generate by prompt) in one parallel wave, then
510
- // bulk-attach. Failures leave the product text-only; the seed's exit never depends on images.
511
- const files = await resolveItemImages(ctx, withNames.map((p, i) => ({
512
- url: products[i]?.imageUrl,
513
- path: products[i]?.imagePath,
514
- prompt: products[i]?.imagePrompt,
515
- displayName: `${p.slug || "product"}.png`,
516
- })));
517
- // `p.id` guards this: a product that failed to create has no id, and bulk-updating an
518
- // undefined id would 400 the whole batch and cost every other product its image.
519
- const imageItems = withNames
520
- .map((p, i) => (files[i] && p.id ? { id: p.id, url: files[i].url, altText: products[i]?.altText ?? p.slug } : null))
521
- .filter(Boolean);
522
- // imagesAttached counts the attaches the API CONFIRMED (per-item results), not the ones sent;
523
- // imageFailures names the products left text-only and why. Neither blocks the seed: a re-run of
524
- // the same plan reuses the products and attaches again.
525
- let imagesAttached = 0;
526
- const imageFailures = [];
527
- const nameOf = (id) => withNames.find((p) => p.id === id)?.name;
528
- try {
529
- if (imageItems.length) {
530
- const r = await attachProductImages(ctx, imageItems);
531
- imagesAttached = r.attached.length;
532
- for (const f of r.failures) imageFailures.push({ name: nameOf(f.id), error: f.error });
533
- }
534
- } catch (e) {
535
- for (const it of imageItems) imageFailures.push({ name: nameOf(it.id), error: e?.message ?? String(e) });
536
- }
537
-
538
- // failures is part of the result, not an exception: a partial seed still leaves a usable
539
- // store, and the agent needs the names to report rather than silently shipping a short
540
- // catalog. Re-run the seed to retry them — existing names are skipped, not duplicated.
541
- return { products: withNames, categories: cats, imagesAttached, imageFailures, failures };
542
- }
543
-
544
- // ---- CLI entry ----------------------------------------------------------------------------------
545
-
546
- const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
547
- if (invokedDirectly) {
548
- const planPath = process.argv[2];
549
- if (!planPath) {
550
- console.error("usage: node seed-store.mjs <plan.json> (run from the project root)");
551
- process.exit(1);
552
- }
553
- const plan = JSON.parse(readFileSync(planPath, "utf8"));
554
- const ctx = makeCtx();
555
- setupStore(ctx, plan)
556
- .then((result) => console.log(JSON.stringify(result, null, 2)))
557
- .catch((e) => {
558
- console.error(e.message);
559
- process.exit(1);
560
- });
561
- }