@wix/agent-skills 1.21.0 → 1.22.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 (258) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +60 -4
  3. package/skills/wix-app/references/DASHBOARD_PAGE.md +11 -25
  4. package/skills/wix-app/references/DATA_COLLECTION.md +16 -219
  5. package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +182 -0
  6. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +97 -0
  7. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_COLLECTION.md +184 -0
  8. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +203 -0
  9. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_SETTINGS.md +44 -0
  10. package/skills/wix-app/references/dashboard-page/QUERY_AND_PAGING.md +182 -0
  11. package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +133 -0
  12. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +10 -5
  13. package/skills/wix-app/references/data-collection/LIFECYCLE.md +68 -0
  14. package/skills/wix-app/references/data-collection/PERMISSIONS.md +47 -0
  15. package/skills/wix-app/references/data-collection/SCHEMA.md +181 -0
  16. package/skills/wix-app/references/data-collection/WIX_DATA.md +38 -66
  17. package/skills/wix-headless-fast/SKILL.md +61 -223
  18. package/skills/wix-headless-fast/cold-start/bootstrap.mjs +156 -0
  19. package/skills/wix-headless-fast/{entry/skill.md → cold-start/cold-start.md} +11 -12
  20. package/skills/wix-headless-fast/guides/existing-site.md +76 -0
  21. package/skills/wix-headless-fast/guides/reference-mode.md +97 -0
  22. package/skills/wix-headless-fast/install/AGENTS.md +6 -3
  23. package/skills/wix-headless-fast/install/attach.mjs +15 -47
  24. package/skills/wix-headless-fast/install/deploy.mjs +21 -60
  25. package/skills/wix-headless-fast/install/lock.mjs +55 -0
  26. package/skills/wix-headless-fast/install/setup.mjs +32 -12
  27. package/skills/wix-headless-fast/install/templates.mjs +168 -0
  28. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +27 -0
  29. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +83 -37
  30. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +0 -303
  31. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +0 -136
  32. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +0 -112
  33. package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +0 -42
  34. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +0 -31
  35. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +0 -22
  36. package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +0 -137
  37. package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +0 -78
  38. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +0 -117
  39. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +0 -52
  40. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +0 -47
  41. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +0 -58
  42. package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +0 -62
  43. package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +0 -40
  44. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +0 -97
  45. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +0 -24
  46. package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +0 -16156
  47. package/skills/wix-headless-fast/references/blog/rest/posts.ts +0 -42
  48. package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +0 -62
  49. package/skills/wix-headless-fast/references/blog/seed/SEED.md +0 -81
  50. package/skills/wix-headless-fast/references/blog/seed/read-site.mjs +0 -22
  51. package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +0 -287
  52. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +0 -277
  53. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +0 -144
  54. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +0 -119
  55. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +0 -32
  56. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +0 -23
  57. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +0 -218
  58. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +0 -169
  59. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +0 -121
  60. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +0 -92
  61. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +0 -90
  62. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +0 -57
  63. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +0 -72
  64. package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +0 -40
  65. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +0 -76
  66. package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +0 -22
  67. package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +0 -11962
  68. package/skills/wix-headless-fast/references/bookings/rest/booking.ts +0 -113
  69. package/skills/wix-headless-fast/references/bookings/rest/services.ts +0 -55
  70. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +0 -71
  71. package/skills/wix-headless-fast/references/bookings/seed/read-site.mjs +0 -28
  72. package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +0 -302
  73. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +0 -355
  74. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +0 -50
  75. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +0 -37
  76. package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +0 -158
  77. package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +0 -93
  78. package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +0 -177
  79. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +0 -123
  80. package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +0 -75
  81. package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +0 -41
  82. package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +0 -11262
  83. package/skills/wix-headless-fast/references/cms/rest/items.ts +0 -125
  84. package/skills/wix-headless-fast/references/cms/seed/SEED.md +0 -112
  85. package/skills/wix-headless-fast/references/cms/seed/read-site.mjs +0 -20
  86. package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +0 -324
  87. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +0 -307
  88. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +0 -66
  89. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +0 -178
  90. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +0 -118
  91. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +0 -35
  92. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +0 -24
  93. package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +0 -130
  94. package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +0 -97
  95. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +0 -55
  96. package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +0 -113
  97. package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +0 -156
  98. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +0 -97
  99. package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +0 -61
  100. package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +0 -40
  101. package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +0 -12
  102. package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +0 -80
  103. package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +0 -21
  104. package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +0 -11884
  105. package/skills/wix-headless-fast/references/events/rest/events.ts +0 -57
  106. package/skills/wix-headless-fast/references/events/rest/registration.ts +0 -83
  107. package/skills/wix-headless-fast/references/events/seed/SEED.md +0 -95
  108. package/skills/wix-headless-fast/references/events/seed/read-site.mjs +0 -26
  109. package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +0 -276
  110. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +0 -340
  111. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +0 -85
  112. package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +0 -332
  113. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +0 -238
  114. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +0 -54
  115. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +0 -216
  116. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +0 -59
  117. package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +0 -135
  118. package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +0 -10593
  119. package/skills/wix-headless-fast/references/forms/rest/forms.ts +0 -34
  120. package/skills/wix-headless-fast/references/forms/rest/submissions.ts +0 -56
  121. package/skills/wix-headless-fast/references/forms/seed/SEED.md +0 -116
  122. package/skills/wix-headless-fast/references/forms/seed/read-site.mjs +0 -20
  123. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +0 -327
  124. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +0 -316
  125. package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +0 -60
  126. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +0 -155
  127. package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +0 -78
  128. package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +0 -47
  129. package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +0 -66
  130. package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +0 -105
  131. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +0 -59
  132. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +0 -53
  133. package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +0 -122
  134. package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +0 -43
  135. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +0 -29
  136. package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +0 -21
  137. package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +0 -42
  138. package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +0 -10
  139. package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +0 -11
  140. package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +0 -10329
  141. package/skills/wix-headless-fast/references/members/rest/auth.ts +0 -215
  142. package/skills/wix-headless-fast/references/members/rest/members.ts +0 -26
  143. package/skills/wix-headless-fast/references/members/seed/SEED.md +0 -39
  144. package/skills/wix-headless-fast/references/members/seed/read-site.mjs +0 -15
  145. package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +0 -94
  146. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +0 -315
  147. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +0 -110
  148. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +0 -90
  149. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +0 -35
  150. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +0 -85
  151. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +0 -32
  152. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +0 -21
  153. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +0 -31
  154. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +0 -82
  155. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +0 -58
  156. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +0 -177
  157. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +0 -78
  158. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +0 -81
  159. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +0 -55
  160. package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +0 -40
  161. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +0 -30
  162. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +0 -21
  163. package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +0 -29
  164. package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +0 -10197
  165. package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +0 -75
  166. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +0 -86
  167. package/skills/wix-headless-fast/references/portfolio/seed/read-site.mjs +0 -20
  168. package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +0 -256
  169. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +0 -296
  170. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +0 -47
  171. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +0 -101
  172. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +0 -41
  173. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +0 -16
  174. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +0 -22
  175. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +0 -54
  176. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +0 -106
  177. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +0 -64
  178. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +0 -27
  179. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +0 -43
  180. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +0 -22
  181. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +0 -30
  182. package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +0 -41
  183. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +0 -40
  184. package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +0 -21
  185. package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +0 -11226
  186. package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +0 -31
  187. package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +0 -20
  188. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +0 -79
  189. package/skills/wix-headless-fast/references/pricing-plans/seed/read-site.mjs +0 -21
  190. package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +0 -279
  191. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +0 -360
  192. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +0 -142
  193. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +0 -26
  194. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +0 -121
  195. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +0 -155
  196. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +0 -34
  197. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +0 -43
  198. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +0 -34
  199. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +0 -155
  200. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +0 -89
  201. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +0 -71
  202. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +0 -103
  203. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +0 -124
  204. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +0 -185
  205. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +0 -193
  206. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +0 -94
  207. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +0 -74
  208. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +0 -168
  209. package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +0 -47
  210. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +0 -22
  211. package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +0 -12
  212. package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +0 -12560
  213. package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +0 -60
  214. package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +0 -169
  215. package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +0 -68
  216. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +0 -89
  217. package/skills/wix-headless-fast/references/restaurants/seed/read-site.mjs +0 -25
  218. package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +0 -499
  219. package/skills/wix-headless-fast/references/shared/CONTENT.md +0 -22
  220. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +0 -73
  221. package/skills/wix-headless-fast/references/shared/DESIGN.md +0 -26
  222. package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +0 -23
  223. package/skills/wix-headless-fast/references/shared/app/styles/global.css +0 -52
  224. package/skills/wix-headless-fast/references/shared/app/wix/config.ts +0 -15
  225. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +0 -81
  226. package/skills/wix-headless-fast/references/shared/app/wix/money.ts +0 -24
  227. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +0 -89
  228. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +0 -46
  229. package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +0 -46
  230. package/skills/wix-headless-fast/references/shared/rest/client.ts +0 -155
  231. package/skills/wix-headless-fast/references/shared/rest/config.ts +0 -3
  232. package/skills/wix-headless-fast/references/shared/rest/media.ts +0 -65
  233. package/skills/wix-headless-fast/references/shared/seed/images.mjs +0 -145
  234. package/skills/wix-headless-fast/references/shared/seed/read-site.mjs +0 -99
  235. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +0 -612
  236. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +0 -26
  237. package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +0 -155
  238. package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +0 -266
  239. package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +0 -95
  240. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +0 -136
  241. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +0 -48
  242. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +0 -39
  243. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +0 -34
  244. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +0 -121
  245. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +0 -107
  246. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +0 -138
  247. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +0 -314
  248. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +0 -133
  249. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +0 -147
  250. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +0 -289
  251. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +0 -169
  252. package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +0 -65
  253. package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +0 -12247
  254. package/skills/wix-headless-fast/references/storefront/rest/cart.ts +0 -132
  255. package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +0 -123
  256. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +0 -130
  257. package/skills/wix-headless-fast/references/storefront/seed/read-site.mjs +0 -38
  258. 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
- }