@wix/agent-skills 1.17.0 → 1.18.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.
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +115 -12
- package/skills/wix-app/references/APP_MARKET_REVIEW.md +1 -1
- package/skills/wix-app/references/BACKEND_API.md +18 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +6 -0
- package/skills/wix-app/references/DASHBOARD_MODAL.md +10 -2
- package/skills/wix-app/references/DASHBOARD_PAGE.md +32 -70
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +14 -3
- package/skills/wix-app/references/SITE_PLUGIN.md +12 -0
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +74 -68
- package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +79 -0
- package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +73 -0
- package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +52 -0
- package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +184 -0
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +12 -11
- package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +1 -0
- package/skills/wix-app/references/editor-react-component/BROWSER-SUPPORT.md +56 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +16 -8
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +19 -1
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +3 -0
- package/skills/wix-base44-connector/SKILL.md +133 -44
- package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +78 -0
- package/skills/wix-base44-connector/scripts/utils.js +216 -30
- package/skills/wix-headless/entry/skill.md +1 -1
- package/skills/wix-headless/references/CAPABILITIES.md +9 -1
- package/skills/wix-headless/references/DESIGN.md +1 -0
- package/skills/wix-headless/references/DISCOVERY.md +2 -1
- package/skills/wix-headless/references/IMAGE_GENERATION.md +1 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +4 -1
- package/skills/wix-headless/references/SEED.md +2 -1
- package/skills/wix-headless/references/SETUP.md +1 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +2 -2
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +26 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +26 -10
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +8 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +254 -0
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +13 -0
- package/skills/wix-headless/references/inline-recipes/setup-rentals.md +254 -0
- package/skills/wix-headless/references/non-astro.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +166 -0
- package/skills/wix-headless-fast/entry/skill.md +99 -0
- package/skills/wix-headless-fast/install/deploy.mjs +433 -0
- package/skills/wix-headless-fast/install/fast-path.mjs +141 -0
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +137 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +137 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +113 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +42 -0
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +123 -0
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +70 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +99 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +76 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +62 -0
- package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +99 -0
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +24 -0
- package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +16156 -0
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +72 -0
- package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +287 -0
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +129 -0
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +143 -0
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +119 -0
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +138 -0
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +54 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +206 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +96 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +70 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +76 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +22 -0
- package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +11962 -0
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +61 -0
- package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +302 -0
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +179 -0
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +83 -0
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +57 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +151 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +75 -0
- package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +41 -0
- package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +11262 -0
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +103 -0
- package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +324 -0
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +149 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +66 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +176 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +118 -0
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +117 -0
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +59 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +136 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +128 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +61 -0
- package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +12 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +80 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +21 -0
- package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +11884 -0
- package/skills/wix-headless-fast/references/events/seed/SEED.md +86 -0
- package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +276 -0
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +150 -0
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +317 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +269 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +231 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +135 -0
- package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +10593 -0
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +108 -0
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +320 -0
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +63 -0
- package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +60 -0
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +155 -0
- package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +78 -0
- package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +47 -0
- package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +66 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +88 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +53 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +122 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +50 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +21 -0
- package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +42 -0
- package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +10 -0
- package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +11 -0
- package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +10329 -0
- package/skills/wix-headless-fast/references/members/seed/SEED.md +39 -0
- package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +94 -0
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +134 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +110 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +90 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +115 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +59 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +39 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +58 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +165 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +55 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +30 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +21 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +29 -0
- package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +10197 -0
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +78 -0
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +258 -0
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +133 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +58 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +126 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +37 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +39 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +75 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +45 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +30 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +41 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +40 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +21 -0
- package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +11226 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +71 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +279 -0
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +165 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +142 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +26 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +121 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +155 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +55 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +43 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +177 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +165 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +103 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +254 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +146 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +168 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +47 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +22 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +12 -0
- package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +12560 -0
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +81 -0
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +479 -0
- package/skills/wix-headless-fast/references/shared/CONTENT.md +22 -0
- package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +73 -0
- package/skills/wix-headless-fast/references/shared/DESIGN.md +26 -0
- package/skills/wix-headless-fast/references/shared/app/styles/global.css +52 -0
- package/skills/wix-headless-fast/references/shared/app/wix/config.ts +15 -0
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +27 -0
- package/skills/wix-headless-fast/references/shared/app/wix/money.ts +24 -0
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +76 -0
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +46 -0
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +46 -0
- package/skills/wix-headless-fast/references/shared/seed/images.mjs +145 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +149 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +26 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +121 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +100 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +130 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +48 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +66 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +103 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +48 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +140 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +80 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +101 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +221 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +215 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +109 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +46 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +56 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +23 -0
- package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +12247 -0
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +85 -0
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +415 -0
- package/skills/wix-manage/SKILL.md +18 -2
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +141 -0
- package/skills/wix-manage/references/google-business-profile/connect-google-business-profile.md +134 -0
- package/skills/wix-manage/references/google-business-profile/manage-google-business-profile-locations.md +182 -0
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +3 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +21 -6
- package/skills/wix-manage/references/stores/update-product-with-options.md +83 -0
- package/skills/wix-vibe-headless/SKILL.md +18 -4
- package/skills/wix-vibe-headless/install/deploy.cjs +12 -4
- package/skills/wix-vibe-headless/install/pin-agents-md.cjs +2 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44.md +23 -61
- package/skills/wix-vibe-headless/platforms/generic.md +6 -3
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +67 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +128 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +5 -3
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +250 -0
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +12 -5
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +41 -0
- package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.js +267 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +6 -3
- package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +470 -0
- package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +425 -0
- package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +187 -0
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +238 -0
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +108 -0
- package/skills/wix-vibe-headless/references/forms/seed/SEED.md +1762 -0
- package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +13 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +137 -84
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +15 -8
- package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +37 -0
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +18 -6
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +30 -7
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +46 -10
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +0 -110
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +0 -51
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +0 -49
- package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +0 -92
- package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +0 -139
|
@@ -31,10 +31,12 @@ const clip = (out) => {
|
|
|
31
31
|
return s.length <= BUDGET ? JSON.parse(s) : { truncated: true, total: s.length, head: s.slice(0, BUDGET) };
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
// One transport for every JSON call — Content-Type, optional Bearer, ok-guard.
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
// One transport for every JSON call — Content-Type, optional Bearer, ok-guard. One per verb, so a
|
|
35
|
+
// PATCH/GET/PUT/DELETE endpoint is a helper call, never a hand-rolled fetch. Admin calls are these
|
|
36
|
+
// with the connector token: patch(publicUrl, body, accessToken).
|
|
37
|
+
async function req(method, url, body, token) {
|
|
38
|
+
const r = await fetch(url, { method,
|
|
39
|
+
...(body !== undefined && { body: JSON.stringify(body) }),
|
|
38
40
|
headers: { "Content-Type": "application/json", ...(token && { Authorization: `Bearer ${token}` }) } });
|
|
39
41
|
if (!r.ok) {
|
|
40
42
|
const head = (await r.text()).slice(0, 300);
|
|
@@ -44,6 +46,11 @@ async function post(url, body, token) {
|
|
|
44
46
|
}
|
|
45
47
|
return r.json();
|
|
46
48
|
}
|
|
49
|
+
const post = (url, body, token) => req("POST", url, body, token);
|
|
50
|
+
const get = (url, token) => req("GET", url, undefined, token);
|
|
51
|
+
const patch = (url, body, token) => req("PATCH", url, body, token);
|
|
52
|
+
const put = (url, body, token) => req("PUT", url, body, token);
|
|
53
|
+
const del = (url, token) => req("DELETE", url, undefined, token);
|
|
47
54
|
|
|
48
55
|
function save(name, text) {
|
|
49
56
|
const dir = SCRATCH;
|
|
@@ -95,6 +102,40 @@ async function context(token, section) {
|
|
|
95
102
|
return clip({ total: markdown.length, section: m ? m[0] : "not found — call context(token) for the outline" });
|
|
96
103
|
}
|
|
97
104
|
|
|
105
|
+
// The wix-manage skill, when it is installed in the sandbox, is these same recipes on disk —
|
|
106
|
+
// a read_file instead of a fetch. Indexed by frontmatter name AND filename slug, because the
|
|
107
|
+
// docs title drifts from the local one ("…Connect a domain" vs "…Connect").
|
|
108
|
+
const RECIPE_ROOT = ".agents/skills/wix-manage/references";
|
|
109
|
+
const rkey = (s) => String(s || "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
110
|
+
let LOCAL_RECIPES;
|
|
111
|
+
function localRecipes() {
|
|
112
|
+
if (LOCAL_RECIPES) return LOCAL_RECIPES;
|
|
113
|
+
LOCAL_RECIPES = new Map();
|
|
114
|
+
if (!fs.existsSync(RECIPE_ROOT)) return LOCAL_RECIPES;
|
|
115
|
+
const walk = (rel) => {
|
|
116
|
+
for (const e of fs.readdirSync(RECIPE_ROOT + rel, { withFileTypes: true })) {
|
|
117
|
+
if (e.isDirectory()) { walk(rel + "/" + e.name); continue; }
|
|
118
|
+
if (!e.name.endsWith(".md")) continue;
|
|
119
|
+
const file = RECIPE_ROOT + rel + "/" + e.name;
|
|
120
|
+
const name = (fs.readFileSync(file, "utf8").slice(0, 600).match(/^name:\s*"?(.+?)"?\s*$/m) || [])[1];
|
|
121
|
+
if (name) LOCAL_RECIPES.set(rkey(name), file);
|
|
122
|
+
LOCAL_RECIPES.set(rkey(e.name.replace(/\.md$/, "")), file);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
walk("");
|
|
126
|
+
return LOCAL_RECIPES;
|
|
127
|
+
}
|
|
128
|
+
// title, else the docsUrl slug, else either one as a prefix of the other
|
|
129
|
+
function recipeFile(title, docsUrl) {
|
|
130
|
+
const idx = localRecipes();
|
|
131
|
+
if (!idx.size) return undefined;
|
|
132
|
+
const slug = (docsUrl || "").replace(/\/+$/, "").split("/").pop();
|
|
133
|
+
for (const k of [rkey(title), rkey(slug)]) if (k && idx.has(k)) return idx.get(k);
|
|
134
|
+
const t = rkey(title);
|
|
135
|
+
if (t.length >= 12) for (const [k, v] of idx) if (k.startsWith(t) || t.startsWith(k)) return v;
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
98
139
|
// ── find what to read ─────────────────────────────────────────────────────────
|
|
99
140
|
|
|
100
141
|
// Browse the docs tree — deterministic. menuUrl alone orients (children + counts);
|
|
@@ -109,36 +150,106 @@ async function browse(menuUrl, { include, filter, depth } = {}) {
|
|
|
109
150
|
return { ...s, next: `wx.bash("grep -in 'term' ${s.path} | head -40") // one line per node — or re-browse with a filter` };
|
|
110
151
|
}
|
|
111
152
|
|
|
112
|
-
// Semantic search — ranks, never says "no match". The reduced hits come back inline
|
|
113
|
-
//
|
|
114
|
-
// REST (default) · WIX_HEADLESS
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
153
|
+
// Semantic search — ranks, never says "no match". The reduced hits come back inline AND the full
|
|
154
|
+
// raw content is saved for grep/window follow-ups. { type } picks the corpus, one per request:
|
|
155
|
+
// REST (default) · SKILLS · WIX_HEADLESS · SDK · VELO · CLI · WDS · BUILD_APPS · OVERVIEW ·
|
|
156
|
+
// BUSINESS_SOLUTIONS. A REST search also runs SKILLS — the management recipes rank as their own
|
|
157
|
+
// hits, ahead of the methods, and land in the saved file whole. Each method hit lists the worked
|
|
158
|
+
// requests the docs publish for it; every line number reads with read_file(path, offset: <line>).
|
|
159
|
+
async function search(term, { type = "REST", max = 5, lines = 0, recipes = type === "REST" } = {}) {
|
|
160
|
+
const ask = (document_type, maximum_results) =>
|
|
161
|
+
post("https://www.wixapis.com/mcp-docs-search/v1/docs/search/markdown",
|
|
162
|
+
// lines_in_each_result 0 skips the server's per-section budget, which otherwise cuts every
|
|
163
|
+
// code example after the first 30 lines and the rest after 5, and every recipe at 20 —
|
|
164
|
+
// the saved file would carry stubs instead of the requests and flows the hits point at
|
|
165
|
+
{ search_term: term, document_type, maximum_results, lines_in_each_result: lines });
|
|
166
|
+
const [main, skills] = await Promise.all([ask(type, max),
|
|
167
|
+
recipes ? ask("SKILLS", 3).catch(() => null) : null]); // a recipe corpus miss must not fail the search
|
|
168
|
+
const recipeText = skills?.content ? skills.content.trimEnd() + "\n" : "";
|
|
169
|
+
const content = recipeText + main.content;
|
|
170
|
+
const nl = []; // newline offsets — a match's char offset becomes its line in the saved file
|
|
171
|
+
for (let i = content.indexOf("\n"); i >= 0; i = content.indexOf("\n", i + 1)) nl.push(i);
|
|
172
|
+
const lineAt = (off) => { let lo = 0, hi = nl.length; while (lo < hi) { const m = (lo + hi) >> 1; nl[m] < off ? lo = m + 1 : hi = m; } return lo + 1; };
|
|
173
|
+
// recipes are articles — no "# Method:" header, no code-example delimiters, and no fixed body
|
|
174
|
+
// shape. Two things every one of them has: headings, and the endpoints it calls. Those are the
|
|
175
|
+
// outline — enough to tell whether this is the recipe for the task without reading 400 lines.
|
|
176
|
+
const recipeHits = !recipeText ? [] : recipeText.split(/\n---\n+(?=#### )/).map(b => {
|
|
177
|
+
const at = content.indexOf(b), rows = b.split("\n");
|
|
178
|
+
const steps = [], calls = [];
|
|
179
|
+
let verb = null;
|
|
180
|
+
for (const t of rows) {
|
|
181
|
+
if (/^#{1,4} /.test(t) && !/^#### \[|^## (Resource|Article|Article Link|Article Content):/.test(t))
|
|
182
|
+
steps.push(t.replace(/^#+ /, "").trim().slice(0, 52));
|
|
183
|
+
const c = t.match(/curl\s+-X\s+(GET|POST|PATCH|PUT|DELETE)/i);
|
|
184
|
+
const u = t.match(/https:\/\/www\.wixapis\.com\/[^\s"'`)\\]+/);
|
|
185
|
+
if (c && !u) { verb = c[1].toUpperCase(); continue; } // curl -X VERB, url on the next line
|
|
186
|
+
if (!u) continue;
|
|
187
|
+
const v = (c && c[1].toUpperCase()) || (t.match(/\b(GET|POST|PATCH|PUT|DELETE)\b/) || [])[1] || verb || "";
|
|
188
|
+
verb = null;
|
|
189
|
+
const call = (v + " " + u[0].replace(/\{[^}]*\}|<[^>]*>/g, "{id}")).trim();
|
|
190
|
+
if (!calls.includes(call)) calls.push(call);
|
|
191
|
+
}
|
|
192
|
+
const title = (b.match(/^## Resource: (.+)$/m) || [])[1];
|
|
193
|
+
const docsUrl = (b.match(/#### \[[^\]]+\]\((https:[^)]+)\)/) || [])[1];
|
|
194
|
+
return { recipe: title, docsUrl,
|
|
195
|
+
...(recipeFile(title, docsUrl) && { file: recipeFile(title, docsUrl) }),
|
|
196
|
+
line: at < 0 ? 1 : lineAt(at), lines: rows.length,
|
|
197
|
+
...(steps.length && { steps: steps.slice(0, 6) }),
|
|
198
|
+
...(calls.length && { calls: calls.slice(0, 4) }) };
|
|
199
|
+
}).filter(h => h.docsUrl && h.recipe);
|
|
200
|
+
let cursor = recipeText.length;
|
|
201
|
+
const hits = main.content.split(/\n---\n+(?=#### )/).map(b => {
|
|
202
|
+
const start = content.indexOf(b, cursor); cursor = start + b.length;
|
|
203
|
+
const examples = [...b.matchAll(/--- Code Example: (.+?) ---/g)]
|
|
204
|
+
.map(m => ({ title: m[1].trim(), line: lineAt(start + m.index) }));
|
|
205
|
+
const docsUrl = (b.match(/#### \[[^\]]+\]\((https:[^)]+)\)/) || [])[1];
|
|
206
|
+
const method = (b.match(/^# Method: (.+)$/m) || [])[1];
|
|
207
|
+
// the REST corpus mixes guides in with the methods — an article has no method header, so
|
|
208
|
+
// name it from its own title rather than returning a row of nulls
|
|
209
|
+
if (!method) return { article: (b.match(/^## (?:Resource|Article): (.+)$/m) || [])[1], docsUrl,
|
|
210
|
+
line: start < 0 ? 1 : lineAt(start) };
|
|
211
|
+
return {
|
|
212
|
+
method,
|
|
213
|
+
endpoint: (b.match(/^# Method API Endpoint: (.+)$/m) || [])[1], // "VERB url" — read the verb + url; call wx.<verb>(url, body, token)
|
|
214
|
+
docsUrl,
|
|
122
215
|
gist: ((b.match(/## Method Description:\s*\n([\s\S]{0,400})/) || [])[1] || "")
|
|
123
216
|
.trim().replace(/\s+/g, " ").slice(0, 220),
|
|
124
|
-
|
|
217
|
+
...(examples.length && { examples }),
|
|
218
|
+
}; }).filter(h => h.docsUrl);
|
|
125
219
|
const saved = save("search-" + term.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40) + ".md", content);
|
|
126
|
-
if (!hits.length) return clip({ ...saved, head: content.slice(0, 1200),
|
|
220
|
+
if (!hits.length && !recipeHits.length) return clip({ ...saved, head: content.slice(0, 1200),
|
|
127
221
|
note: `no method blocks parsed — raw head above; wx.bash("grep -in 'term' ${saved.path}") for the rest` });
|
|
128
|
-
|
|
222
|
+
// one URL, one row: a long article is indexed in chunks that rank separately, and a stray skills
|
|
223
|
+
// page sits in the REST corpus, so the same page can rank two or three times
|
|
224
|
+
const seen = new Set();
|
|
225
|
+
const recipeRows = recipeHits.filter(r => !seen.has(r.docsUrl) && seen.add(r.docsUrl));
|
|
226
|
+
const uniq = hits.filter(h => !seen.has(h.docsUrl) && seen.add(h.docsUrl));
|
|
227
|
+
const out = { ...saved, ...(recipeRows.length && { recipes: recipeRows }), hits: uniq };
|
|
228
|
+
// over budget, shed enrichment rather than structure — clip would drop the whole shape, and
|
|
229
|
+
// every title, URL and line number stays useful with the outlines gone
|
|
230
|
+
for (const shed of [() => recipeRows.forEach(r => delete r.calls),
|
|
231
|
+
() => recipeRows.forEach(r => delete r.steps),
|
|
232
|
+
() => uniq.forEach(h => { if (h.examples) h.examples = h.examples.slice(0, 3); }),
|
|
233
|
+
() => uniq.forEach(h => delete h.examples)]) {
|
|
234
|
+
if (JSON.stringify(out).length <= BUDGET) break;
|
|
235
|
+
shed();
|
|
236
|
+
}
|
|
237
|
+
return clip(out);
|
|
129
238
|
}
|
|
130
239
|
|
|
131
240
|
// ── read a page (docs pages and recipes alike) ────────────────────────────────
|
|
132
241
|
|
|
133
242
|
// Fetch + save + map in one round: whole text inline when small, else
|
|
134
243
|
// { path, bytes, lines, outline } — the outline's line numbers feed grep and read_file windows.
|
|
244
|
+
// Examples come back as their own title+line list, so the outline's cap can never drop them.
|
|
135
245
|
async function page(url) {
|
|
136
246
|
const { path: p, lines } = await resolveRef(url);
|
|
137
247
|
const text = lines.join("\n");
|
|
138
248
|
if (text.length <= BUDGET) return text;
|
|
139
249
|
const o = outlineOf(lines);
|
|
140
250
|
const bytes = Buffer.byteLength(text);
|
|
141
|
-
|
|
251
|
+
const ex = pageExamples(lines);
|
|
252
|
+
return { path: p, bytes, lines: lines.length, ...(ex.length && { examples: ex }), ...o,
|
|
142
253
|
next: [
|
|
143
254
|
`wx.bash("grep -in 'term' ${p} | head -40")`,
|
|
144
255
|
bytes <= 45000 ? `read_file ${p} // whole (fits the 45K cap), or a window via offset/limit`
|
|
@@ -146,6 +257,18 @@ async function page(url) {
|
|
|
146
257
|
] };
|
|
147
258
|
}
|
|
148
259
|
|
|
260
|
+
// The page's Examples section, as titles + line numbers — listed on their own so the
|
|
261
|
+
// outline's cap can never drop them.
|
|
262
|
+
function pageExamples(lines) {
|
|
263
|
+
const heads = [];
|
|
264
|
+
lines.forEach((t, i) => { const m = /^(#{1,6}) (.+)$/.exec(t); if (m) heads.push({ line: i + 1, level: m[1].length, text: m[2].trim() }); });
|
|
265
|
+
const at = heads.findIndex(h => /^(examples?|method code examples)$/i.test(h.text));
|
|
266
|
+
if (at < 0) return [];
|
|
267
|
+
const sec = heads[at], rest = heads.slice(at + 1);
|
|
268
|
+
const end = (rest.find(h => h.level < sec.level) || { line: lines.length + 1 }).line;
|
|
269
|
+
return rest.filter(h => h.line < end).map(h => ({ title: h.text, line: h.line }));
|
|
270
|
+
}
|
|
271
|
+
|
|
149
272
|
// ── shell ─────────────────────────────────────────────────────────────────────
|
|
150
273
|
|
|
151
274
|
// Compose native pipelines over .agents/skills/wix-base44-connector/tmp — grep -n, sed -n, mawk, sort, uniq, wc
|
|
@@ -165,17 +288,50 @@ function bash(cmd) {
|
|
|
165
288
|
}
|
|
166
289
|
}
|
|
167
290
|
|
|
291
|
+
// ── request examples ──────────────────────────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
// The docs' own working requests: the code-mode index carries them at
|
|
294
|
+
// methods[].legacyExamples[].content, a doc page under its Examples heading. Both are saved
|
|
295
|
+
// whole and come back as titles + line numbers — read the one you need with read_file.
|
|
296
|
+
const examplesOf = (result) => {
|
|
297
|
+
const methods = result?.methods?.length ? result.methods : (result?.legacyExamples ? [result] : []);
|
|
298
|
+
const out = [];
|
|
299
|
+
for (const m of methods) for (const e of m.legacyExamples || []) {
|
|
300
|
+
const c = e.content || e;
|
|
301
|
+
if (c.request) out.push({ title: c.title || "", request: typeof c.request === "string" ? c.request : JSON.stringify(c.request, null, 1) });
|
|
302
|
+
}
|
|
303
|
+
return out;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// One file, one heading per example — the returned line is where its body starts.
|
|
307
|
+
const saveExamples = (exs, name) => {
|
|
308
|
+
const parts = [];
|
|
309
|
+
let line = 1, index = [];
|
|
310
|
+
for (const e of exs) {
|
|
311
|
+
const block = "## " + e.title + "\n\n" + e.request + "\n";
|
|
312
|
+
index.push({ title: e.title, line: line + 2 });
|
|
313
|
+
parts.push(block);
|
|
314
|
+
line += block.split("\n").length;
|
|
315
|
+
}
|
|
316
|
+
return { ...save("examples-" + name + ".md", parts.join("\n")), index };
|
|
317
|
+
};
|
|
318
|
+
|
|
168
319
|
// ── the spec index ────────────────────────────────────────────────────────────
|
|
169
320
|
|
|
170
|
-
// Inspect a
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
321
|
+
// Inspect a method's schema — request body, responses, enums, filterable-fields map — plus the
|
|
322
|
+
// titles of the docs' own request examples, saved together at examplesPath: read the one that
|
|
323
|
+
// matches your task with read_file(examplesPath, offset: <its line>). Pass a docsUrl
|
|
324
|
+
// (from search/browse — a direct load, no scan) and spec returns that method's schema. Or pass raw
|
|
325
|
+
// `async function(){…}` to query the index yourself: lightIndex (RESOURCES with .methods —
|
|
326
|
+
// operationId, summary, httpMethod, publicUrl [callable], docsUrl) and getResourceSchemaByUrl(docsUrl)
|
|
327
|
+
// → s.methods (each with requestBody, responses, legacyExamples,
|
|
328
|
+
// queryMethodData.queryFieldsCapabilitiesMap; $circular via s.components.schemas). A big result is
|
|
329
|
+
// saved as JSON; grep it for the keys you saw in its head.
|
|
330
|
+
async function spec(arg) {
|
|
331
|
+
const s = String(arg).trim();
|
|
332
|
+
const code = /^async function/.test(s) ? s
|
|
333
|
+
: /^https:\/\/dev\.wix\.com\/docs\//.test(s) ? "async function(){ return await getResourceSchemaByUrl(" + JSON.stringify(s) + "); }"
|
|
334
|
+
: "async function(){ " + s + " }";
|
|
179
335
|
const { result } = await post("https://mcp.wix.com/api/code-mode/search", { code });
|
|
180
336
|
if (result == null || (Array.isArray(result) && !result.length))
|
|
181
337
|
return { result, note: "empty — the query missed the shape; match your docsUrl against s.methods[].docsUrl" };
|
|
@@ -183,8 +339,11 @@ async function spec(code) {
|
|
|
183
339
|
if (text.length <= BUDGET) return result;
|
|
184
340
|
let h = 5381;
|
|
185
341
|
for (const ch of code) h = ((h * 33) ^ ch.charCodeAt(0)) >>> 0; // same query → same file
|
|
342
|
+
const exs = examplesOf(result);
|
|
343
|
+
const ex = exs.length ? saveExamples(exs, h.toString(36)) : null;
|
|
186
344
|
return { ...save("spec-" + h.toString(36) + ".json", text),
|
|
187
345
|
shape: Array.isArray(result) ? `Array(${result.length})` : Object.keys(result || {}).slice(0, 15),
|
|
346
|
+
...(ex && { examplesPath: ex.path, examples: ex.index }),
|
|
188
347
|
head: text.slice(0, 600) };
|
|
189
348
|
}
|
|
190
349
|
|
|
@@ -196,10 +355,29 @@ async function spec(code) {
|
|
|
196
355
|
// category name → its recipes; any other term → search every recipe's name + gist.
|
|
197
356
|
// Read the chosen url with page(url), then grep/window/fields.
|
|
198
357
|
async function mgmtRecipes(q) {
|
|
199
|
-
|
|
358
|
+
// the recipes ARE the wix-manage skill's references — when that skill is installed, both the
|
|
359
|
+
// listing and the files come straight off disk; the manifest fetch is only the not-installed path
|
|
360
|
+
const ROOT = ".agents/skills/wix-manage/references";
|
|
361
|
+
let files;
|
|
362
|
+
if (fs.existsSync(ROOT)) {
|
|
363
|
+
files = [];
|
|
364
|
+
const walk = rel => {
|
|
365
|
+
for (const e of fs.readdirSync(ROOT + rel, { withFileTypes: true })) {
|
|
366
|
+
if (e.isDirectory()) { walk(rel + "/" + e.name); continue; }
|
|
367
|
+
const file = ROOT + rel + "/" + e.name, head = fs.readFileSync(file, "utf8").slice(0, 1000);
|
|
368
|
+
files.push({ path: "references" + rel + "/" + e.name, file, size: fs.statSync(file).size,
|
|
369
|
+
name: (head.match(/^name:\s*"?(.+?)"?\s*$/m) || [])[1] || e.name,
|
|
370
|
+
description: (head.match(/^description:\s*"?(.+?)"?\s*$/m) || [])[1] || "" });
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
walk("");
|
|
374
|
+
} else {
|
|
375
|
+
const { base, files: manifest } = await (await fetch("https://dev.wix.com/docs/skills/manage.manifest.json")).json();
|
|
376
|
+
files = manifest.map(f => ({ ...f, url: base + f.path }));
|
|
377
|
+
}
|
|
200
378
|
const cat = f => (f.path.match(/^references\/([^/]+)\//) || [])[1];
|
|
201
379
|
const row = f => ({ name: f.name, cat: cat(f), gist: (f.description || "").slice(0, 120),
|
|
202
|
-
|
|
380
|
+
...(f.file ? { file: f.file } : { url: f.url }), kb: Math.round(f.size / 1024) });
|
|
203
381
|
if (!q) {
|
|
204
382
|
const cats = {};
|
|
205
383
|
for (const f of files) { const c = cat(f); if (c) cats[c] = (cats[c] || 0) + 1; }
|
|
@@ -217,4 +395,12 @@ async function mgmtRecipes(q) {
|
|
|
217
395
|
return clip(m.map(row));
|
|
218
396
|
}
|
|
219
397
|
|
|
220
|
-
|
|
398
|
+
// Install a Wix app on the site (Apps Installer). Use when discovery finds an API but its app is
|
|
399
|
+
// not installed yet — that is a one-call prerequisite, not a dead end. appDefId from search or the
|
|
400
|
+
// Apps-Created-by-Wix table; siteId from context (the site report).
|
|
401
|
+
async function installApp(appDefId, siteId, token) {
|
|
402
|
+
return post("https://www.wixapis.com/apps-installer-service/v1/app-instance/install",
|
|
403
|
+
{ tenant: { tenantType: "SITE", id: siteId }, appInstance: { appDefId } }, token);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
module.exports = { req, post, get, patch, put, del, clip, context, browse, search, page, bash, spec, mgmtRecipes, installApp };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wix-headless-entry
|
|
3
|
-
description: "Build a complete Wix Managed Headless site from a single prompt, then connect Wix Business Solutions. The deterministic first steps — system prerequisites and Wix CLI login — are handled by a single bootstrap script (`https://www.wix.com/skills/headless/entry/bootstrap.mjs`); the agent then installs the skill and hands the run to it. Triggers: build me a site, create a website, make me a website, new website, online store, I want to sell X, start a business online, launch a site, ecommerce, portfolio, business website, sell online, online shop, connect this to Wix Headless, add Wix Headless to this project, host this on Wix, deploy this to Wix, implement the features of this project using Wix Headless. Use this skill instead of the WixSiteBuilder MCP tool for new-site requests."
|
|
3
|
+
description: "Build a complete Wix Managed Headless site from a single prompt, then connect Wix Business Solutions. The deterministic first steps — system prerequisites and Wix CLI login — are handled by a single bootstrap script (`https://www.wix.com/skills/headless/entry/bootstrap.mjs`); the agent then installs the skill and hands the run to it. Triggers: build me a site, create a website, make me a website, new website, online store, I want to sell X, start a business online, launch a site, ecommerce, portfolio, business website, sell online, online shop, rental site, rentals, connect this to Wix Headless, add Wix Headless to this project, host this on Wix, deploy this to Wix, implement the features of this project using Wix Headless. Use this skill instead of the WixSiteBuilder MCP tool for new-site requests."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Wix Headless Skill — cold-start entry
|
|
@@ -19,7 +19,7 @@ Each built vertical has three parts (the latter two scoped to `create`/backend-o
|
|
|
19
19
|
|
|
20
20
|
## Built verticals — installed, seeded, and described in the Handoff
|
|
21
21
|
|
|
22
|
-
The verticals the skill operates end-to-end today: **stores · blog · cms · forms · events · bookings · pricing-plans · restaurants · portfolio** (with `forms` as the floor when nothing richer is named).
|
|
22
|
+
The verticals the skill operates end-to-end today: **stores · blog · cms · forms · events · bookings · rentals · pricing-plans · restaurants · portfolio** (with `forms` as the floor when nothing richer is named).
|
|
23
23
|
|
|
24
24
|
### stores — sell products
|
|
25
25
|
- **Intent:** sell / shop / products / catalog / merch / store.
|
|
@@ -63,9 +63,17 @@ The verticals the skill operates end-to-end today: **stores · blog · cms · fo
|
|
|
63
63
|
### bookings — appointments and classes
|
|
64
64
|
- **Intent:** book / appointment / schedule / class / session / reserve a slot — a **service or appointment with a provider** (a stylist, trainer, tutor, clinic, studio).
|
|
65
65
|
- **Not bookings — route to `restaurants` instead:** a **table at a restaurant** is a Table Reservations concern, not a bookable service; and a **special dining occasion guests reserve** — a wine tasting, cheese pairing, chef's table, or tasting-menu evening at a restaurant — is a **restaurants → Table Reservations *experience*** (see the restaurants entry), **not** a bookings service. Reach for `bookings` only when what's booked is a standalone service/class with a provider, never a seat at a restaurant.
|
|
66
|
+
- **Not bookings — route to `rentals` instead:** when the **customer chooses how long** they want something, and what's booked is a **thing rather than a person** — a room by the hour, a desk for three days, a van for the weekend. The tell is a duration *range* the customer picks within. A 60-minute haircut is `bookings`; a meeting room booked for 1–8 hours is `rentals`.
|
|
66
67
|
- **Required site features:** a list of services; a page per service; a way to pick a time and book it.
|
|
67
68
|
- **Implementation checklist:** show each service's name, duration, and price; show the staff or provider; show the available time slots; a book action; a confirmation after booking.
|
|
68
69
|
|
|
70
|
+
### rentals — rent a room, vehicle, or piece of equipment
|
|
71
|
+
- **Intent:** rent / rental / hire / lease / "by the hour" / "by the day" / hourly or daily rate / **customer picks how long** — plus the things people rent: meeting room, conference room, co-working desk, hot desk, studio, hall, venue, workspace, vehicle, van, car, boat, bike, equipment, gear, camera, tool. Reach for it whenever what's booked is a **resource** (a room, vehicle, or item) for a **length the customer selects within a range**, not a fixed session with a provider.
|
|
72
|
+
- **Not rentals — route to `bookings` instead:** a session whose length the **business** fixes, delivered by a **person** (a 60-minute massage, a class, a consultation). The word "rent" in a brief sometimes describes the *business model* ("we rent out desks") while the thing being sold is really a fixed session — decide on the **duration model**, not the vocabulary.
|
|
73
|
+
- **Required site features:** a list of rentable items; a page per item; a way to pick a start **and a length** (an end time or an end date) and book it. A rental is priced per hour or per day, so the site must show the **computed total for the chosen length** before the customer commits — a per-unit price alone is not enough.
|
|
74
|
+
- **Implementation checklist:** show each item's name, description, and **per-hour or per-day rate** (say which); show the item's attributes where they exist (capacity, amenities, equipment type); show available start times, then the valid lengths for the chosen start; show the running total for the selected length; a book action; a confirmation after booking.
|
|
75
|
+
- **One unit type per item** — an item is rented by the hour *or* by the day, never both. When a brief wants both for the same room, that's **two** rentals, each with its own rate.
|
|
76
|
+
|
|
69
77
|
### pricing-plans — memberships and subscriptions
|
|
70
78
|
- **Intent:** membership / subscription / plans / paid tiers.
|
|
71
79
|
- **Required site features:** a plans / pricing page; a way to choose and subscribe to a plan. **Subscribing requires a logged-in member** — the plans grid is public, but the *subscribe* action and any *my subscription* surface need the **members** cross-cutting capability (below); it's a hard dependency, not optional.
|
|
@@ -34,6 +34,7 @@ Hold these as the run's design tokens. Reuse them literally in the themed-block
|
|
|
34
34
|
| forms | The form itself — clear single-column, one obvious submit |
|
|
35
35
|
| events | Event list, event detail + RSVP |
|
|
36
36
|
| bookings | Service list, booking widget/flow |
|
|
37
|
+
| rentals | Rentable-item grid, item detail (rate + attributes), date/length picker showing the running total |
|
|
37
38
|
| pricing-plans | Plan comparison, checkout/signup |
|
|
38
39
|
| restaurants | Menu, reservation/ordering flow |
|
|
39
40
|
| portfolio | Project grid, case-study/project detail |
|
|
@@ -32,7 +32,7 @@ Discovery is pure inference — it needs **no authentication** and is **agnostic
|
|
|
32
32
|
|
|
33
33
|
Read the **user intent** (+ optional project signals: `package.json` name, README, visible copy) against the vertical index in `references/CAPABILITIES.md` — each entry there carries the intent signals that point to it. Pick every vertical that genuinely fits → `verticals[]`. Multiple signals → multiple capabilities. On ambiguity, prefer the more specific vertical; if nothing dynamic is named, fall to the **forms** floor (a contact form). **Never return an empty set.**
|
|
34
34
|
|
|
35
|
-
Resolve to the skill's operational set — **stores · blog · cms · forms · events · bookings · pricing-plans · restaurants · portfolio** (`CAPABILITIES.md` § "Built verticals"). If intent points squarely at a vertical outside that set, note it plainly as not-yet-wired (per the index) and resolve the rest; don't force an unrelated capability in its place.
|
|
35
|
+
Resolve to the skill's operational set — **stores · blog · cms · forms · events · bookings · rentals · pricing-plans · restaurants · portfolio** (`CAPABILITIES.md` § "Built verticals"). If intent points squarely at a vertical outside that set, note it plainly as not-yet-wired (per the index) and resolve the rest; don't force an unrelated capability in its place.
|
|
36
36
|
|
|
37
37
|
## 2 · Infer brand
|
|
38
38
|
|
|
@@ -50,6 +50,7 @@ For each capability, build its `intent.<cap>` block — the inputs the seed step
|
|
|
50
50
|
| forms | `{ forms: [{ purpose, fields: [...] }] }` | one `contact` form: name, email, message |
|
|
51
51
|
| events | `{ eventCount, titles: [] }` | `eventCount: 2`, titles brand-derived, future dates |
|
|
52
52
|
| bookings | `{ serviceCount, servicesNamed: [] }` | `serviceCount: 2`, brand-derived service names |
|
|
53
|
+
| rentals | `{ unitType, serviceCount, resourceCount, servicesNamed: [] }` | `unitType: "HOUR"` (`"DAY"` if the brief rents by the day), `serviceCount: 2`, `resourceCount: 2`, brand-derived item names |
|
|
53
54
|
| pricing-plans | `{ planCount, tiersNamed: [] }` | `planCount: 2` (e.g. Basic / Pro), monthly billing |
|
|
54
55
|
| restaurants | `{ menuName, sections: [{ name, itemCount }], ordering?, reservations?, experiences?: [{ name }] }` | one menu, 2 sections, `itemCount: 3` each; add-ons only when the brief names them. **`experiences[]`** = special dining occasions guests reserve (wine/cheese pairing, chef's table) — a restaurant *reservation that is an experience*, **not** the `bookings` vertical (see `CAPABILITIES.md`). |
|
|
55
56
|
| portfolio | `{ collections: [{ name }], projectCount }` | one brand-derived collection, `projectCount: 3` |
|
|
@@ -54,6 +54,7 @@ The pass-2 **write shape is per-entity earned knowledge and lives in each capabi
|
|
|
54
54
|
| Blog post cover | `setup-blog.md` | **Attach images** |
|
|
55
55
|
| CMS item | `setup-cms.md` | **Attach images** |
|
|
56
56
|
| Bookings service | `setup-bookings.md` | **Attach images** |
|
|
57
|
+
| Rental service | `setup-rentals.md` | **Attach images** |
|
|
57
58
|
| Portfolio project / collection | `setup-portfolio.md` | **Attach images** (cover + gallery) |
|
|
58
59
|
| Restaurant menu item | `setup-restaurants.md` | **Attach images** |
|
|
59
60
|
| Event hero (`mainImage`) | `setup-events.md` | **Attach images** |
|
|
@@ -32,7 +32,9 @@ For the current `createClient` + auth-strategy shape, link:
|
|
|
32
32
|
### 3 · Per-capability packages + API docs (package from the map; recipe for shapes)
|
|
33
33
|
For each loaded capability, give the host the **package(s)** from the map below and point it at the capability's **inline recipe** (`references/inline-recipes/how-to-code-*.md`) — the pinned API shape, methods, version, and failure modes live there; don't re-summarize them here. The map is the source of truth for *which* packages; the recipe is the source of truth for *how* to call them. **The recipes are local — Read them with the file tool, don't curl.**
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Before implementing a capability, Read the local recipe linked in its row.
|
|
36
|
+
|
|
37
|
+
| Capability | Package(s) | Required recipe |
|
|
36
38
|
|---|---|---|
|
|
37
39
|
| stores | `@wix/stores` (+ `@wix/ecom`, `@wix/redirects` for cart/checkout) | `how-to-code-a-store.md` — Catalog V3 read + cart/checkout. |
|
|
38
40
|
| blog | `@wix/blog` (+ `@wix/ricos` to render `richContent`; **`@wix/comments` + `@wix/members`** when the blog has comments/members; `@wix/blog` `likes` when the site has members) | `how-to-code-a-blog.md` — Blog V3 read/render + member features (likes, comments). Supplement: <https://dev.wix.com/docs/sdk/business-solutions/blog.md>. |
|
|
@@ -40,6 +42,7 @@ For each loaded capability, give the host the **package(s)** from the map below
|
|
|
40
42
|
| forms | `@wix/forms` | `how-to-code-forms.md` — schema-driven render (read the live form schema, visitor token/no elevate) + Form Submissions v4 write contract. |
|
|
41
43
|
| events | `@wix/events` (+ `@wix/redirects` for the ticketed hosted-checkout redirect) | `how-to-code-events.md` — Events V3 read + the `TICKETING`-vs-`RSVP` branch. |
|
|
42
44
|
| bookings | `@wix/bookings` (+ `@wix/auto_sdk_ecom_cart-v-2`, `@wix/redirects` for the cart/checkout that holds the seat; `@wix/forms` for the schema-driven booking form) | `how-to-code-bookings.md` — Services V2 read + `createBooking → ecom Cart V2 → checkout-or-place`. |
|
|
45
|
+
| rentals | **`@wix/bookings`** — the same package set as bookings (`@wix/auto_sdk_ecom_cart-v-2`, `@wix/redirects`, `@wix/forms`). **There is NO `@wix/rentals` package** and its absence is not a missing capability: Wix Rentals ships no APIs of its own and runs on the Bookings APIs with rentals-specific field values. Never report rentals as "not buildable headlessly". | `how-to-code-rentals.md` — a **delta** on `how-to-code-bookings.md`: filter the catalog by the rentals `appId` (`ff5d6eb1-65e4-4f9a-8b14-64d34c12cc2e`), two-call hourly availability (start slots → end options), the consecutive-day walk for daily, duration-based `previewPrice`, then the **unchanged** booking/cart/checkout sequence carrying the rentals `appId` on `catalogReference`. |
|
|
43
46
|
| pricing-plans | `@wix/pricing-plans` (+ `@wix/members` — login is a hard dep; and for the Bookings-membership integration `@wix/bookings` + `@wix/ecom` (+ `@wix/redirects`)). **Use `@wix/pricing-plans`, NOT the Wix-site `@wix/site-pricing-plans` (`startOnlinePurchase` is site page-code, not headless).** | `how-to-code-pricing-plans.md` — Plans V3 grid + login-gated order/subscribe + book-with-membership. |
|
|
44
47
|
| restaurants | `@wix/restaurants` (menu display — **no `@wix/ecom`** unless online ordering is in the run; reservations are a separate app) | `how-to-code-restaurants.md` — Menus V1 display-only read. |
|
|
45
48
|
| restaurants — online ordering (add-on; only when the Orders app is in the run) | add `@wix/ecom` + `@wix/redirects` to the row above | `how-to-code-restaurant-orders.md` — ordering cart/checkout on top of the menu display. |
|
|
@@ -37,6 +37,7 @@ Each entry states only the entities, where their count/content comes from, the p
|
|
|
37
37
|
- **forms** — *Visitor-fillable forms.* Recipe: **`setup-forms.md`**. One form per `intent.forms.forms` entry; fields from the entry, `purpose` names the form. **Keep (→ handoff):** `formIds[]` + each form's field **`target`** keys — *structural* (the frontend binds each input's `name` = target to submit; targets are **immutable**, so they don't go stale when an owner relabels a field).
|
|
38
38
|
- **events** — *A ticketed or RSVP occasion, or upcoming events.* Recipe: **`setup-events.md`**. Each event is **either `TICKETING`** (paid — it has ticket tiers) **or `RSVP`** (free — the registration form is built-in, so don't seed form fields) per intent. For an **RSVP occasion** (a wedding, party, gathering): one RSVP event with the occasion's real details (title, a **future** date/time, location). For a **ticketed** event: the event plus its ticket tiers (default a single `"General Admission"` tier if none named). For a **listing** site: `intent.events.eventCount` events with brand-appropriate titles and **future** start dates (a default location/timezone is fine). Text-only. **Keep:** nothing for the handoff — events and tiers are discovered **live** (`queryEvents`, `getEventBySlug`, `queryAvailableTickets`); track `eventIds`/`ticketDefinitionIds` only transiently.
|
|
39
39
|
- **bookings** — *Bookable services.* Recipe: **`setup-bookings.md`**. `intent.bookings.serviceCount` services (name + short description fitting `brand`, a simple duration and price); keep the schedule minimal. **Keep:** `serviceIds[]` as **seed-time state** (pricing-plans' coverage step keys on them, below); nothing for the handoff — services are discovered **live** (`queryServices`).
|
|
40
|
+
- **rentals** — *Rentable resources.* Recipe: **`setup-rentals.md`**. A resource **type** per kind of thing rented (rooms, vans, cameras), then `intent.rentals.resourceCount` **resources** inside it (Room A / Room B — parallel capacity comes from more resources, not higher capacity), then `intent.rentals.serviceCount` **rental services**, each carrying the Wix Rentals `appId`, `serviceResources` naming its resource type, a `primaryResourceType`, and a `durationRange` (`HOUR` or `DAY`) with the min/max the request implies — defaulting to **60/480 minutes** hourly and **1/5 days** daily when the brief doesn't say — and a **per-unit** price. **No category** — unlike bookings, rental services don't use one; don't create or assign one. **Order is load-bearing: resource type → resources → services** — a service whose resource type holds no resources has permanently empty availability. Seed resources **24/7** (no working-hours schedule) unless the request names opening hours; that keeps a multi-day rental to a single booking. **Keep:** `resourceTypeIds[]` as seed-time state (services key on them); nothing for the handoff — rentals are discovered **live** (`queryServicesByFilters` filtered by the rentals `appId`).
|
|
40
41
|
- **pricing-plans** — *Membership tiers.* Recipe: **`setup-pricing-plans.md`**. `intent.pricing-plans.planCount` recurring plans (name, price, a monthly billing cycle) fitting `brand`. **When bookings is also in this run and the intent is a membership that covers services** (a studio/gym/class pass), also **attach the covered bookings services to the plan** — seed the bookings services first, since coverage keys on their service ids (see the recipe). **Keep:** the bookings coverage only transiently, to wire it during seeding; nothing for the handoff — plans are discovered **live** (`queryPlans`) and plan→service coverage is read **live** at checkout, never from a frozen map (see `how-to-code-pricing-plans.md`).
|
|
41
42
|
- **restaurants** — *A menu.* Recipe: **`setup-restaurants.md`** (add-ons have their own recipes, below). Create the `itemCount` items per section first (name, description, price fitting `brand`), then the `intent.restaurants.sections` referencing those item ids, then the menu referencing the section ids — **items → sections → menu** (build bottom-up: a section is created with its `itemIds`, a menu with its `sectionIds`, so each child must exist before its parent — see `setup-restaurants.md` STEP 1–3). Text-only. **Keep (seed-time only):** `menuId`, `sectionIds[]`, `itemIds[]` to build the tree; nothing goes to the handoff — the frontend reads menus **live** (`listMenus`/`listSections`/`listItems`). **Online ordering (add-on, on demand)** — only when the request calls for ordering (not just a displayed menu): install the Orders app (per `SETUP.md` §2) and follow **`setup-restaurant-orders.md`**; **seed the menu FIRST, then ordering, in the same unit** (ordering auto-provisions per menu either way — see the concurrency note above — but keeping them one unit gives the ordering verify a menu to confirm against). **Configure the business-location address (`setup-restaurant-orders.md` STEP 0) — required for ordering to work end-to-end; without it Wix limits ordering to "testing only" and checkout breaks.** If the brief gives no address, set a placeholder and flag the owner in the handoff. Completing a *paid* order additionally needs premium + a payment method (dashboard/premium — surface, don't fail). **Keep:** nothing for the handoff — read **live** (`listOperations`/`listFulfillmentMethods`); track `operationId`/`fulfillmentMethodIds[]` transiently. **Table reservations (add-on, on demand, INDEPENDENT of ordering/menu)** — only when the request calls for reservations: install the Table Reservations app (per `SETUP.md` §2) and follow **`setup-restaurant-reservations.md`**; **no menu dependency** (reservations bind to a location, not a menu — do NOT apply the menu-first rule) and **nothing to bulk-seed** (visitors create reservations at runtime); **configure the business-location address (`setup-restaurant-reservations.md` STEP 0)** — the reservation location is the site's business location, so set a real address (placeholder + owner flag if the brief names none; shared with ordering if both are present); enabling online reservations is **premium-gated** (record the precondition, don't fail). **Keep:** nothing for the handoff — read **live** (`listReservationLocations`); track `reservationLocationId` transiently. **Experiences (add-on within Table Reservations, on demand)** — only when the request names **special dining occasions guests reserve** (wine/cheese pairing, chef's table, tasting-menu evening — an *experience*, not a `bookings` service; see `CAPABILITIES.md`): they need the **Table Reservations app** (no extra install) and a configured **business-location address** (`setup-restaurant-reservations.md` STEP 0, same as reservations) and, unlike plain reservations, they **are** seeded — create one Experience per named occasion on the default reservation location, following **`setup-restaurant-experiences.md`**. Booking an experience is **premium-gated** like online reservations (record the precondition, don't fail). **Keep:** nothing for the handoff — experiences are read **live** (`queryExperiences`); track `experienceId`(s) transiently.
|
|
42
43
|
- **portfolio** — *A project showcase.* Recipe: **`setup-portfolio.md`**. Create the `intent.portfolio.collections` (title + short description fitting `brand`), then `intent.portfolio.projectCount` projects assigned to them — **collections before projects** (a project's `collectionIds` must hold real collection ids; see the recipe). Text-only by default (omit `coverImage`; images only when `imagery` is on — §1, §4). **Keep (seed-time only):** `collectionIds{<name>}` to assign projects into their collections; nothing goes to the handoff — the frontend lists collections and projects **live** (`collections.queryCollections` / `projects.queryProjects`), so owner-added content appears with no code change.
|
|
@@ -59,7 +60,7 @@ Don't silently skip a required feature — a bare list-and-detail with none of i
|
|
|
59
60
|
|
|
60
61
|
## 4 · Entity images (opt-in)
|
|
61
62
|
|
|
62
|
-
**Only if `imagery` is on** (from `DISCOVERY.md`). For each seeded image-bearing capability — stores products, blog covers, CMS items, **bookings services**, restaurant items, **portfolio projects + collection covers**, event heroes — generate a brand-contextual image per entity and **attach it as a required pass-2 step** (the seeder created the entity text-first in §2; this writes the image onto it), following `references/IMAGE_GENERATION.md` (generate → import to Wix Media → attach). **The per-entity attach shape lives in that capability's own seed recipe** (`setup-<capability>.md`), next to its create shape — read it there; `IMAGE_GENERATION.md` §3 is a navigation table (entity → recipe → step) that carries no shapes. Use the IDs already in the `seeded` map. Image failures never block — skip and continue (the entity stays text-only). When `imagery` is off, skip this step entirely.
|
|
63
|
+
**Only if `imagery` is on** (from `DISCOVERY.md`). For each seeded image-bearing capability — stores products, blog covers, CMS items, **bookings services**, **rental services**, restaurant items, **portfolio projects + collection covers**, event heroes — generate a brand-contextual image per entity and **attach it as a required pass-2 step** (the seeder created the entity text-first in §2; this writes the image onto it), following `references/IMAGE_GENERATION.md` (generate → import to Wix Media → attach). **The per-entity attach shape lives in that capability's own seed recipe** (`setup-<capability>.md`), next to its create shape — read it there; `IMAGE_GENERATION.md` §3 is a navigation table (entity → recipe → step) that carries no shapes. Use the IDs already in the `seeded` map. Image failures never block — skip and continue (the entity stays text-only). When `imagery` is off, skip this step entirely.
|
|
63
64
|
|
|
64
65
|
## Aggregate
|
|
65
66
|
|
|
@@ -23,6 +23,7 @@ For each capability in `verticals[]`, install its app by `appDefId` (these are t
|
|
|
23
23
|
- **forms** → `225dd912-7dea-4738-8688-4b8c6955ffc2`
|
|
24
24
|
- **events** → `140603ad-af8d-84a5-2c80-a0f60cb47351`
|
|
25
25
|
- **bookings** → `13d21c63-b5ec-5912-8397-c3a5ddb27a97`
|
|
26
|
+
- **rentals** → `ff5d6eb1-65e4-4f9a-8b14-64d34c12cc2e` (Wix Rentals). Rentals provisions the Bookings infrastructure it runs on, so a Rentals-only install is enough — its availability comes back without adding Wix Bookings separately. Rentals has **no APIs of its own**: it is Services V2 with rentals-specific field values (`setup-rentals.md`), and its frontend package is `@wix/bookings` — there is no `@wix/rentals`. Rentals uses no categories; to also offer plain fixed-duration services, install Wix Bookings (`13d21c63-b5ec-5912-8397-c3a5ddb27a97`) as well, seed those with `setup-bookings.md`, and filter every catalog read by `appId` so the two sets stay separate.
|
|
26
27
|
- **pricing-plans** → `1522827f-c56c-a5c9-2ac9-00f9e6ae12d3`
|
|
27
28
|
- **restaurants** → `b278a256-2757-4f19-9313-c05c783bec92` (Wix Restaurants **Menus** — the seedable core). Online ordering and table reservations are **separate optional apps**, install only if intent calls for them: Orders (New) → `9a5d83fd-8570-482e-81ab-cfa88942ee60`, Table Reservations → `f9c07de2-5341-40c6-b096-8eb39de391fb`. **Experiences** (special dining occasions guests reserve — wine tastings, chef's tables; see `CAPABILITIES.md`) are a **feature of the Table Reservations app, not a separate install** — installing Table Reservations is all that's needed to create and book them (`setup-restaurant-experiences.md`).
|
|
28
29
|
- **portfolio** → `d90652a2-f5a1-4c7c-84c4-d4cdcc41f130` (Wix Portfolio; the install ships a default sample collection + projects — `setup-portfolio.md` STEP 0 cleans them)
|
|
@@ -63,7 +63,7 @@ const { items } = await posts
|
|
|
63
63
|
.limit(20)
|
|
64
64
|
.find();
|
|
65
65
|
```
|
|
66
|
-
Doc: <https://dev.wix.com/docs/api-reference/business-solutions/blog/posts/query-posts.md?apiView=SDK>
|
|
66
|
+
Doc: <https://dev.wix.com/docs/api-reference/business-solutions/blog/posts-stats/query-posts.md?apiView=SDK>
|
|
67
67
|
|
|
68
68
|
**⚠️ CRITICAL: request the `RICH_CONTENT` fieldset, or `post.richContent` is `undefined`.** `queryPosts` omits the body by default; without `fieldsets: ["RICH_CONTENT"]` the post comes back with no `richContent`, and the detail page renders blank. (Add `"URL"` to get `post.url`/`slug` for links.)
|
|
69
69
|
|
|
@@ -114,7 +114,7 @@ export default function RicosContentViewer({ content }) {
|
|
|
114
114
|
return <div className="ricos-content"><RicosViewer content={content} plugins={plugins} /></div>;
|
|
115
115
|
}
|
|
116
116
|
```
|
|
117
|
-
Doc: <https://dev.wix.com/docs/
|
|
117
|
+
Doc: <https://dev.wix.com/docs/api-reference/business-solutions/blog/introduction.md?apiView=SDK> (follow it to the current Ricos viewer API; verify with `SearchWixSDKDocumentation "ricos viewer"`).
|
|
118
118
|
|
|
119
119
|
**⚠️ CRITICAL (Astro): render the viewer with `client:only="react"`.** `@wix/ricos` is a React component that breaks under SSR. `<RicosViewer client:only="react" content={post.richContent} />` makes it render client-side only. `@wix/ricos` accepts the camelCase `richContent` from the `@wix/blog` SDK directly — no key renaming.
|
|
120
120
|
|
|
@@ -54,13 +54,16 @@ A concise contract for writing the **frontend code** of a storefront against a C
|
|
|
54
54
|
The exact field paths the storefront reads, and the **plausible-wrong sibling** each is mistaken for — the sections below reference these instead of re-describing them. All `amount`s are **strings**. These are **read** shapes; the cart-add body (under *Adding to cart*) is a separate **write** shape, and the `_id` rule applies to read **entities**, not to request params (note the redirect session's `checkoutId`, which is just the cart's `_id`).
|
|
55
55
|
|
|
56
56
|
```jsonc
|
|
57
|
-
//
|
|
58
|
-
|
|
57
|
+
// Request CURRENCY and PLAIN_DESCRIPTION on every product read — they populate formattedAmount and plainDescription.
|
|
58
|
+
const PRODUCT_FIELDS = ['CURRENCY', 'PLAIN_DESCRIPTION'];
|
|
59
|
+
// productsV3.queryProducts({ fields: PRODUCT_FIELDS }).…find() → result.items[]
|
|
60
|
+
// productsV3.searchProducts({ ..., fields: PRODUCT_FIELDS }) → result.products[]
|
|
59
61
|
product = {
|
|
60
62
|
_id, // links · cart catalogItemId · variant filter (NOT .id → empty → HTTP 500)
|
|
61
63
|
slug, name, visible, // only visible:true is returned to a visitor token
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
currency, // present when CURRENCY is requested
|
|
65
|
+
actualPriceRange: { minValue: { amount, formattedAmount } }, // PRICE (NOT price.actualPrice.amount — that's the seed/WRITE shape → $NaN)
|
|
66
|
+
compareAtPriceRange: { minValue: { amount, formattedAmount } }, // strike-through price
|
|
64
67
|
inventory: { availabilityStatus }, // product-level stock, "IN_STOCK"
|
|
65
68
|
media: { main: { image } }, // image is a wix:image:// id → resolve it (see Rendering images)
|
|
66
69
|
plainDescription, // plain string; description.nodes is the rich-text form
|
|
@@ -90,10 +93,22 @@ Each section below is a **self-contained storefront feature** — implement only
|
|
|
90
93
|
|
|
91
94
|
### Listing products (and the `_id` rule)
|
|
92
95
|
|
|
93
|
-
Query products with `productsV3.queryProducts()` / `.searchProducts()`.
|
|
96
|
+
Query products with `productsV3.queryProducts()` / `.searchProducts()`. **Every product listing,
|
|
97
|
+
category search, and detail read must request `CURRENCY` and `PLAIN_DESCRIPTION`**. Without `CURRENCY`, `formattedAmount` is omitted
|
|
98
|
+
and a product card renders an unsymbolled number such as `34.99` instead of a localized price. Without `PLAIN_DESCRIPTION`, `product.plainDescription` is absent for cards and detail pages.
|
|
94
99
|
Doc: <https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products.md?apiView=SDK>
|
|
95
100
|
|
|
96
|
-
**⚠️ `queryProducts()`
|
|
101
|
+
**⚠️ `queryProducts()` returns a builder, not a Promise.** Pass requested fields when needed, then chain `.eq(...)`/`.limit(...)`/`.find()` — for example:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const PRODUCT_FIELDS = ['CURRENCY', 'PLAIN_DESCRIPTION'];
|
|
105
|
+
const { items } = await productsV3
|
|
106
|
+
.queryProducts({ fields: PRODUCT_FIELDS })
|
|
107
|
+
.limit(50)
|
|
108
|
+
.find();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Do not pass a search/filter object to `queryProducts`; use `searchProducts` for that (see category filtering below). Reuse the same `PRODUCT_FIELDS` constant for every product read so home, shop, category, and detail pages cannot drift.
|
|
97
112
|
|
|
98
113
|
**⚠️ CRITICAL: the entity id is `_id`, NOT `id`.** The SDK normalizes every entity's id to **`_id`**. `product.id` is `undefined` in SDK code. This is the cart-killer: feeding `product.id` into the cart's `catalogItemId` sends an empty string and the add returns **HTTP 500** (`"catalogItemId" has size 0`). Use `product._id` everywhere — in links, as the cart `catalogItemId`, and as the variant-query filter value. (If a field name surprises you, you are probably reading the REST doc view — re-open it with `?apiView=SDK`.)
|
|
99
114
|
|
|
@@ -101,7 +116,7 @@ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v
|
|
|
101
116
|
|
|
102
117
|
**Visibility:** only `visible: true` products are returned to a visitor token, so a missing product usually means it wasn't seeded visible — not a query bug.
|
|
103
118
|
|
|
104
|
-
**Price
|
|
119
|
+
**Price:** after requesting `CURRENCY`, render `actualPriceRange.minValue.formattedAmount` (and the matching `compareAtPriceRange` value) directly. Fall back to raw `.amount` only if the formatted value is unexpectedly absent. Never use `price.actualPrice.amount`: that is the seed/write shape and reads `undefined` → `$NaN`.
|
|
105
120
|
|
|
106
121
|
### Category navigation — list categories live
|
|
107
122
|
|
|
@@ -114,7 +129,7 @@ const res = await categories.queryCategories({
|
|
|
114
129
|
}).exists('name', true).find(); // read the returned array per the SDK doc; ids are `_id`; link each to /category/<slug>
|
|
115
130
|
```
|
|
116
131
|
|
|
117
|
-
**⚠️ The query MUST carry a filter condition — chain `.exists('name', true)` (as above), do NOT call a bare `.find()`.** A `.find()` with no chained filter serializes an empty `"filter": {}`, which `categories/v1/categories/query` rejects with `400 INVALID_FILTER` ("Filter expression cannot contain an empty condition"). **This is fatal on the visitor/manual-client (non-Astro) path** — the call throws and the nav never renders; managed-Astro's server-side transport happens to tolerate the empty filter, so a bare `.find()` *looks* fine there but breaks the moment the same code runs client-side. `.exists('name', true)` is a tautology (every category has a `name`), so it matches **all** categories and is accepted on **both** paths — use this one shape everywhere. **Do NOT filter on `visible`** (`.eq('visible', true)`): unlike `name`, `visible` is **not declared filterable** on `queryCategories`, so it triggers a silently-swallowed `400` and the nav renders blank; if you ever need to hide a category, filter the returned array **client-side**. (`getCategoryBySlug(slug, { appNamespace: '@wix/stores' })` on the category page is unaffected — it takes no query filter.) This is a **distinct API from bookings' `categoriesV2`** — don't copy that module's query shape here; read the exact result-array key + field names from the `@wix/categories` SDK doc: <https://dev.wix.com/docs/
|
|
132
|
+
**⚠️ The query MUST carry a filter condition — chain `.exists('name', true)` (as above), do NOT call a bare `.find()`.** A `.find()` with no chained filter serializes an empty `"filter": {}`, which `categories/v1/categories/query` rejects with `400 INVALID_FILTER` ("Filter expression cannot contain an empty condition"). **This is fatal on the visitor/manual-client (non-Astro) path** — the call throws and the nav never renders; managed-Astro's server-side transport happens to tolerate the empty filter, so a bare `.find()` *looks* fine there but breaks the moment the same code runs client-side. `.exists('name', true)` is a tautology (every category has a `name`), so it matches **all** categories and is accepted on **both** paths — use this one shape everywhere. **Do NOT filter on `visible`** (`.eq('visible', true)`): unlike `name`, `visible` is **not declared filterable** on `queryCategories`, so it triggers a silently-swallowed `400` and the nav renders blank; if you ever need to hide a category, filter the returned array **client-side**. (`getCategoryBySlug(slug, { appNamespace: '@wix/stores' })` on the category page is unaffected — it takes no query filter.) This is a **distinct API from bookings' `categoriesV2`** — don't copy that module's query shape here; read the exact result-array key + field names from the `@wix/categories` SDK doc: <https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/introduction.md?apiView=SDK>.
|
|
118
133
|
|
|
119
134
|
### Filtering products by category
|
|
120
135
|
|
|
@@ -125,6 +140,7 @@ const res = await categories.queryCategories({
|
|
|
125
140
|
```js
|
|
126
141
|
const { products } = await productsV3.searchProducts({
|
|
127
142
|
filter: { 'directCategoriesInfo.categories': { $matchItems: [{ id: categoryId }] } },
|
|
143
|
+
fields: PRODUCT_FIELDS,
|
|
128
144
|
});
|
|
129
145
|
```
|
|
130
146
|
|
|
@@ -169,6 +185,8 @@ await currentCartV2.addLineItemsToCurrentCart({
|
|
|
169
185
|
|
|
170
186
|
**⚠️ CRITICAL: `options.variantId` is MANDATORY for any product that has variants.** Adding by `catalogItemId` alone **fails** — the catalog can't resolve a variant-bearing product without it, and Cart V2 **rejects the add with an explicit error** rather than accepting an invalid line. The cart method's required-params list omits `variantId`, so it's an easy one to miss. Always resolve and include it (part 1 above).
|
|
171
187
|
|
|
188
|
+
**CRITICAL: `ITEM_NOT_FOUND_IN_CATALOG` for a product that exists is a catalog defect, not a stale id.** A `DIGITAL` product whose variant has no `digitalProperties.digitalFile` is rejected under that code, and one with no stock is rejected as `exceeds available inventory` — both read back `visible: true` and in the catalog. Fix the product (`setup-online-store.md` → digital products); re-reading ids won't help.
|
|
189
|
+
|
|
172
190
|
**⚠️ CRITICAL: `options.options` is for MODIFIERS, not variant selection.** Product option selections (Size/Color) are resolved to a **variant** and referenced by `variantId`. `options.options` is only for free-text / TEXT_CHOICES add-on **modifiers**. Do **not** encode Size/Color as `options.options` — that is the coffee-grind bug: the variant never resolves, so Cart V2 rejects the add with an explicit error.
|
|
173
191
|
|
|
174
192
|
### Checkout — redirect to the hosted checkout page
|