@wix/agent-skills 1.15.1 → 1.16.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/README.md +3 -7
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +4 -1
- package/skills/wix-app/references/APP_TOOLS.md +134 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +1 -1
- package/skills/wix-app/references/DASHBOARD_PAGE.md +0 -10
- package/skills/wix-app/references/DATA_COLLECTION.md +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +28 -9
- package/skills/wix-app/references/SERVICE_PLUGIN.md +12 -10
- package/skills/wix-app/references/app-tools/TOOLS.md +75 -0
- package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +2 -2
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW-REFERENCE.md +172 -0
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +138 -0
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +23 -2
- package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +29 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +1 -1
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +27 -2
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +5 -3
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +18 -1
- package/skills/wix-app/references/editor-react-component/PARTS.md +15 -2
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +2 -3
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +13 -9
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +4 -0
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +3 -1
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +4 -0
- package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +54 -0
- package/skills/wix-app/references/stores/QUERY.md +32 -0
- package/skills/wix-docs/SKILL.md +35 -3
- package/skills/wix-headless/SKILL.md +3 -1
- package/skills/wix-headless/references/CONTENT.md +36 -0
- package/skills/wix-headless/references/DESIGN.md +54 -0
- package/skills/wix-headless/references/DISCOVERY.md +24 -0
- package/skills/wix-headless/references/IMAGE_GENERATION.md +10 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +3 -2
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +32 -23
- package/skills/wix-headless/references/managed/CONNECT.md +1 -1
- package/skills/wix-headless/references/managed/CREATE.md +14 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +25 -0
- package/skills/wix-manage/SKILL.md +123 -7
- package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +41 -0
- package/skills/wix-manage/references/app-installation/app-installation-dashboard-navigation.md +28 -0
- package/skills/wix-manage/references/blog/blog-dashboard-navigation.md +56 -0
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +31 -6
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +3 -1
- package/skills/wix-manage/references/bookings/bookings-dashboard-navigation.md +63 -0
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +10 -1
- package/skills/wix-manage/references/cms/cms-dashboard-navigation.md +44 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +31 -3
- package/skills/wix-manage/references/cms/cms-publishing-flow.md +287 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +27 -1
- package/skills/wix-manage/references/contacts/contacts-dashboard-navigation.md +46 -0
- package/skills/wix-manage/references/contacts/create-a-contact.md +82 -0
- package/skills/wix-manage/references/contacts/update-a-contact.md +119 -0
- package/skills/wix-manage/references/dashboard-navigation/dashboard-navigation.md +82 -0
- package/skills/wix-manage/references/dashboard-navigation/routes.json +1733 -0
- package/skills/wix-manage/references/domains/domains-dashboard-navigation.md +32 -0
- package/skills/wix-manage/references/ecommerce/ecom-pricing.md +4 -4
- package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
- package/skills/wix-manage/references/ecommerce/gift-cards/ecom-gift-cards-goal-sell-gift-cards.md +338 -0
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-discount-rule.md +42 -78
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-bundle-and-save.md +1 -1
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-stock-mover.md +1 -1
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-upsell-boost.md +1 -1
- package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +90 -92
- package/skills/wix-manage/references/events/create-wix-event.md +176 -0
- package/skills/wix-manage/references/events/manage-wix-events.md +174 -0
- package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +44 -0
- package/skills/wix-manage/references/get-paid/get-paid-dashboard-navigation.md +52 -0
- package/skills/wix-manage/references/google-ads/billing-and-payment.md +80 -0
- package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +212 -0
- package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +110 -0
- package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +25 -0
- package/skills/wix-manage/references/google-ads/install-and-create-account.md +155 -0
- package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +63 -0
- package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +155 -0
- package/skills/wix-manage/references/marketing/marketing-dashboard-navigation.md +52 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +99 -1
- package/skills/wix-manage/references/pricing-plans/pricing-plans-dashboard-navigation.md +45 -0
- package/skills/wix-manage/references/restaurants/restaurants-dashboard-navigation.md +68 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +94 -125
- package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +48 -9
- package/skills/wix-manage/references/site-properties/site-properties-dashboard-navigation.md +31 -0
- package/skills/wix-manage/references/sites/create-headless-site.md +52 -0
- package/skills/wix-manage/references/sites/create-site-from-template.md +2 -25
- package/skills/wix-manage/references/sites/site-import.md +163 -190
- package/skills/wix-manage/references/sites/sites-dashboard-navigation.md +29 -0
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +17 -1
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +26 -8
- package/skills/wix-manage/references/stores/stores-dashboard-navigation.md +65 -0
- package/skills/wix-manage/references/stores/update-product-with-options.md +39 -7
- package/skills/wix-replatform/.publish-manifest.json +8 -0
- package/skills/wix-replatform/SKILL.md +693 -0
- package/skills/wix-replatform/lib/artifact-freshness.js +215 -0
- package/skills/wix-replatform/lib/completion-report.js +200 -0
- package/skills/wix-replatform/lib/config-env.js +59 -0
- package/skills/wix-replatform/lib/execution-manifest.js +185 -0
- package/skills/wix-replatform/lib/execution-state.js +173 -0
- package/skills/wix-replatform/lib/import-recovery.js +313 -0
- package/skills/wix-replatform/lib/local-state.js +642 -0
- package/skills/wix-replatform/lib/orchestration-preflight.js +143 -0
- package/skills/wix-replatform/lib/orchestration-router.js +122 -0
- package/skills/wix-replatform/lib/orchestration-state.js +455 -0
- package/skills/wix-replatform/lib/progress-log.js +224 -0
- package/skills/wix-replatform/lib/progress-log.sh +140 -0
- package/skills/wix-replatform/lib/url-preservation-state.js +391 -0
- package/skills/wix-replatform/resources/rp-discovery/SKILL.md +184 -0
- package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +529 -0
- package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +316 -0
- package/skills/wix-replatform/resources/rp-execute-setup/scripts/mint-token.sh +64 -0
- package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +1114 -0
- package/skills/wix-replatform/resources/rp-mapper/SKILL.md +430 -0
- package/skills/wix-replatform/resources/rp-mapper/lib/mapping-resolve.js +217 -0
- package/skills/wix-replatform/resources/rp-mapper/scripts/resolve-mapping.js +148 -0
- package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +378 -0
- package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +183 -0
- package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +478 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/canonical-record.js +136 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fingerprint.js +310 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-layout.js +1081 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-parse.js +618 -0
- package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +949 -0
- package/skills/wix-replatform/resources/rp-source-csv/source-schema.example.json +196 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/bigcommerce.json +66 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/magento.json +60 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/shopify.json +95 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/woocommerce.json +82 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +256 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +249 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +444 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +774 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/source-schema.example.json +71 -0
- package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +632 -0
- package/skills/wix-replatform/resources/rp-target-wix/SPEC-product-reviews-and-blog-authorship.md +330 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +21 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +19 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +39 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +35 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +35 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +36 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +35 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +131 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +21 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +97 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +528 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +38 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +13 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +53 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +84 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +25 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +38 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +90 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/audit-summary.js +64 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +141 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +384 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/ndjson.js +260 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +238 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +237 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +238 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +2219 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +31 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge.js +100 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +107 -0
- package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +344 -0
- package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +250 -0
- package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +1927 -0
- package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +83 -0
- package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +141 -0
- package/skills/wix-replatform/scripts/artifact-freshness.js +76 -0
- package/skills/wix-replatform/scripts/execution-manifest-validate.js +43 -0
- package/skills/wix-replatform/scripts/execution-state-prepare.js +128 -0
- package/skills/wix-replatform/scripts/orchestration-preflight.js +48 -0
- package/skills/wix-replatform/scripts/orchestration-route.js +46 -0
- package/skills/wix-replatform/scripts/orchestration-state.js +245 -0
- package/skills/wix-vibe-headless/SKILL.md +48 -25
- package/skills/wix-vibe-headless/install/deploy.cjs +95 -0
- package/skills/wix-vibe-headless/install/pin-agents-md.cjs +34 -0
- package/skills/wix-vibe-headless/platforms/README.md +8 -4
- package/skills/wix-vibe-headless/platforms/base44.md +133 -98
- package/skills/wix-vibe-headless/platforms/generic.md +99 -64
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +230 -119
- package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +43 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +25 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostGrid.jsx +15 -0
- package/skills/wix-vibe-headless/references/blog/app/context/TaxonomyContext.jsx +35 -0
- package/skills/wix-vibe-headless/references/blog/app/hooks/usePostDetail.js +38 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/Blog.jsx +40 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +54 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +65 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +50 -0
- package/skills/wix-vibe-headless/references/blog/seed/SEED.md +103 -0
- package/skills/wix-vibe-headless/references/blog/seed/seed-blog.js +270 -0
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +260 -149
- package/skills/wix-vibe-headless/references/bookings/app/components/BookingForm.jsx +65 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +47 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceGrid.jsx +13 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/SlotPicker.jsx +126 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +108 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServices.js +58 -0
- package/skills/wix-vibe-headless/references/bookings/app/lib/serviceFacts.js +95 -0
- package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +94 -0
- package/skills/wix-vibe-headless/references/bookings/app/pages/Services.jsx +89 -0
- package/skills/wix-vibe-headless/references/bookings/{wix-bookings-checkout.js → app/rest/wix-bookings-checkout.js} +1 -1
- package/skills/wix-vibe-headless/references/bookings/{wix-bookings-services.js → app/rest/wix-bookings-services.js} +87 -11
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +117 -0
- package/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.js +333 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +169 -147
- package/skills/wix-vibe-headless/references/cms/app/collection.config.js +31 -0
- package/skills/wix-vibe-headless/references/cms/app/lib/wixImage.js +18 -0
- package/skills/wix-vibe-headless/references/cms/{wix-cms.js → app/rest/wix-cms.js} +47 -2
- package/skills/wix-vibe-headless/references/cms/seed/SEED.md +145 -0
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +228 -131
- package/skills/wix-vibe-headless/references/events/app/components/CategoryFilter.jsx +23 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +43 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventGrid.jsx +13 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventRegistration.jsx +29 -0
- package/skills/wix-vibe-headless/references/events/app/components/RsvpForm.jsx +57 -0
- package/skills/wix-vibe-headless/references/events/app/components/TicketPicker.jsx +78 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useEventDetail.js +27 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useEventsList.js +54 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useRsvpForm.js +40 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useTicketing.js +81 -0
- package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +48 -0
- package/skills/wix-vibe-headless/references/events/app/pages/Events.jsx +36 -0
- package/skills/wix-vibe-headless/references/events/{wix-events-browse.js → app/rest/wix-events-browse.js} +1 -1
- package/skills/wix-vibe-headless/references/events/seed/SEED.md +107 -0
- package/skills/wix-vibe-headless/references/events/seed/seed-events.js +251 -0
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +276 -188
- package/skills/wix-vibe-headless/references/members/app/components/LoginForm.jsx +101 -0
- package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +33 -0
- package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +21 -0
- package/skills/wix-vibe-headless/references/members/app/components/SocialButtons.jsx +29 -0
- package/skills/wix-vibe-headless/references/members/app/context/MemberContext.jsx +54 -0
- package/skills/wix-vibe-headless/references/members/app/hooks/useLoginForm.js +77 -0
- package/skills/wix-vibe-headless/references/members/app/pages/Account.jsx +36 -0
- package/skills/wix-vibe-headless/references/members/app/pages/Callback.jsx +32 -0
- package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +35 -0
- package/skills/wix-vibe-headless/references/members/{wix-members-auth.js → app/rest/wix-members-auth.js} +6 -3
- package/skills/wix-vibe-headless/references/members/install/Register.jsx +22 -0
- package/skills/wix-vibe-headless/references/members/seed/SEED.md +22 -0
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +233 -104
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +31 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionGrid.jsx +15 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +39 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectGrid.jsx +15 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectMedia.jsx +29 -0
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/useCollectionProjects.js +35 -0
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/usePortfolioGallery.js +27 -0
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/useProjectDetail.js +25 -0
- package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +39 -0
- package/skills/wix-vibe-headless/references/portfolio/app/pages/Portfolio.jsx +27 -0
- package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +60 -0
- package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +94 -0
- package/skills/wix-vibe-headless/references/portfolio/seed/seed-portfolio.js +236 -0
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +240 -117
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/OrderCard.jsx +35 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +48 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanGrid.jsx +16 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanPrice.jsx +35 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/useMyPlans.js +25 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlanDetail.js +28 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlans.js +30 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/MyPlans.jsx +30 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +53 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/Plans.jsx +28 -0
- package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +86 -0
- package/skills/wix-vibe-headless/references/pricing-plans/seed/seed-pricing-plans.js +251 -0
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +248 -124
- package/skills/wix-vibe-headless/references/restaurants/app/components/ItemDialog.jsx +81 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/MenuItemCard.jsx +71 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/MenuList.jsx +43 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartButton.jsx +21 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +60 -0
- package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +63 -0
- package/skills/wix-vibe-headless/references/restaurants/app/hooks/useItemOrder.js +32 -0
- package/skills/wix-vibe-headless/references/restaurants/app/hooks/useReservation.js +73 -0
- package/skills/wix-vibe-headless/references/restaurants/app/pages/Menu.jsx +28 -0
- package/skills/wix-vibe-headless/references/restaurants/app/pages/Reservations.jsx +102 -0
- package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +180 -0
- package/skills/wix-vibe-headless/references/restaurants/seed/seed-restaurants.js +452 -0
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +56 -0
- package/skills/wix-vibe-headless/references/shared/{wix-client.js → app/rest/wix-client.js} +6 -8
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +6 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +263 -157
- package/skills/wix-vibe-headless/references/storefront/app/components/CartButton.jsx +20 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +150 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +167 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +51 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +54 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +119 -0
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +71 -0
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +109 -0
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useShop.js +98 -0
- package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +27 -0
- package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +139 -0
- package/skills/wix-vibe-headless/references/storefront/app/pages/Shop.jsx +75 -0
- package/skills/wix-vibe-headless/references/storefront/{wix-store-cart.js → app/rest/wix-store-cart.js} +1 -1
- package/skills/wix-vibe-headless/references/storefront/{wix-store-catalog.js → app/rest/wix-store-catalog.js} +20 -1
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +111 -0
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +299 -0
- /package/skills/wix-vibe-headless/references/blog/{wix-blog.js → app/rest/wix-blog.js} +0 -0
- /package/skills/wix-vibe-headless/references/events/{wix-events-registration.js → app/rest/wix-events-registration.js} +0 -0
- /package/skills/wix-vibe-headless/references/portfolio/{wix-portfolio.js → app/rest/wix-portfolio.js} +0 -0
- /package/skills/wix-vibe-headless/references/pricing-plans/{wix-pricing-plans.js → app/rest/wix-pricing-plans.js} +0 -0
- /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-menu.js → app/rest/wix-restaurants-menu.js} +0 -0
- /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-ordering.js → app/rest/wix-restaurants-ordering.js} +0 -0
- /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-reservations.js → app/rest/wix-restaurants-reservations.js} +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const w = require('./wix-writers');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_VERIFIED_SUBSCRIPTION_PATHS = [
|
|
8
|
+
'product.subscriptionDetails',
|
|
9
|
+
'product.subscriptionDetails.allowOneTimePurchases',
|
|
10
|
+
'product.subscriptionDetails.subscriptions[]',
|
|
11
|
+
'product.subscriptionDetails.subscriptions[].id',
|
|
12
|
+
'product.subscriptionDetails.subscriptions[].title',
|
|
13
|
+
'product.subscriptionDetails.subscriptions[].description',
|
|
14
|
+
'product.subscriptionDetails.subscriptions[].frequency',
|
|
15
|
+
'product.subscriptionDetails.subscriptions[].interval',
|
|
16
|
+
'product.subscriptionDetails.subscriptions[].autoRenewal',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function timestamp() {
|
|
20
|
+
return new Date().toISOString();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function defaultProbeProduct({ marker, name } = {}) {
|
|
24
|
+
const suffix = marker || `rp-probe-${Date.now()}`;
|
|
25
|
+
return {
|
|
26
|
+
name: name || `RePlatform subscription probe ${suffix}`,
|
|
27
|
+
productType: 'PHYSICAL',
|
|
28
|
+
visible: false,
|
|
29
|
+
sku: suffix,
|
|
30
|
+
price: { actualPrice: { amount: '1.00' } },
|
|
31
|
+
subscriptionDetails: {
|
|
32
|
+
allowOneTimePurchases: true,
|
|
33
|
+
subscriptions: [{
|
|
34
|
+
title: 'Monthly delivery',
|
|
35
|
+
description: 'Ships every month',
|
|
36
|
+
frequency: 'MONTH',
|
|
37
|
+
interval: 1,
|
|
38
|
+
autoRenewal: true,
|
|
39
|
+
}],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function artifactBase({ command, siteId, endpoint, method }) {
|
|
45
|
+
return {
|
|
46
|
+
schemaVersion: 1,
|
|
47
|
+
command,
|
|
48
|
+
targetSiteIdentifier: siteId || null,
|
|
49
|
+
endpoint,
|
|
50
|
+
method,
|
|
51
|
+
status: 'unknown',
|
|
52
|
+
verifiedPaths: [],
|
|
53
|
+
constraintsDiscovered: [],
|
|
54
|
+
probeRecordId: null,
|
|
55
|
+
cleanup: { attempted: false, status: 'not_applicable' },
|
|
56
|
+
warnings: [],
|
|
57
|
+
recoveryInstructions: [],
|
|
58
|
+
timestamp: timestamp(),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function valueAtPath(root, pathExpr) {
|
|
63
|
+
const parts = String(pathExpr).split('.');
|
|
64
|
+
let values = [root];
|
|
65
|
+
for (const part of parts) {
|
|
66
|
+
const arrayPart = part.endsWith('[]') ? part.slice(0, -2) : null;
|
|
67
|
+
const key = arrayPart || part;
|
|
68
|
+
const next = [];
|
|
69
|
+
for (const value of values) {
|
|
70
|
+
if (!value || typeof value !== 'object') continue;
|
|
71
|
+
const child = value[key];
|
|
72
|
+
if (arrayPart) {
|
|
73
|
+
if (Array.isArray(child)) next.push(...child);
|
|
74
|
+
} else {
|
|
75
|
+
next.push(child);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
values = next;
|
|
79
|
+
}
|
|
80
|
+
return values.filter((value) => value !== undefined && value !== null);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function verifyPaths(root, paths) {
|
|
84
|
+
return paths.map((pathExpr) => ({
|
|
85
|
+
path: pathExpr,
|
|
86
|
+
present: valueAtPath(root, pathExpr).length > 0,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function queryFilterByMarker({ markerPath, markerValue }) {
|
|
91
|
+
if (!markerPath || markerValue == null) {
|
|
92
|
+
throw new Error('product-by-source-marker requires --marker-path and --marker-value');
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
filter: { [markerPath]: { $eq: markerValue } },
|
|
96
|
+
paging: { limit: 100, offset: 0 },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function writeArtifact(file, result) {
|
|
101
|
+
if (!file) return result;
|
|
102
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
103
|
+
fs.writeFileSync(file, `${JSON.stringify(result, null, 2)}\n`);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function verifyStoresSubscriptionCreate({ wix, siteId, artifactPath, probeProduct, marker, cleanup = true } = {}) {
|
|
108
|
+
const createRequest = w.buildCreateStoresProductRequest(probeProduct || defaultProbeProduct({ marker }));
|
|
109
|
+
const artifact = artifactBase({
|
|
110
|
+
command: 'stores subscription-create',
|
|
111
|
+
siteId,
|
|
112
|
+
endpoint: createRequest.url,
|
|
113
|
+
method: createRequest.method,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
let createdProduct;
|
|
117
|
+
try {
|
|
118
|
+
createdProduct = (await wix.send(createRequest)).product;
|
|
119
|
+
artifact.probeRecordId = createdProduct && createdProduct.id;
|
|
120
|
+
if (!artifact.probeRecordId) throw new Error('create response did not include product.id');
|
|
121
|
+
|
|
122
|
+
const getRequest = w.buildGetStoresProductRequest(artifact.probeRecordId);
|
|
123
|
+
artifact.readback = { endpoint: getRequest.url, method: getRequest.method };
|
|
124
|
+
const readProduct = (await wix.send(getRequest)).product;
|
|
125
|
+
artifact.verifiedPaths = verifyPaths({ product: readProduct }, DEFAULT_VERIFIED_SUBSCRIPTION_PATHS);
|
|
126
|
+
artifact.constraintsDiscovered = w.STORES_SUBSCRIPTION_CONTRACT.constraints.map((constraint) => ({ ...constraint }));
|
|
127
|
+
artifact.status = artifact.verifiedPaths.every((entry) => entry.present) ? 'passed' : 'failed';
|
|
128
|
+
if (artifact.status === 'failed') {
|
|
129
|
+
artifact.warnings.push('Subscription readback did not include every expected nested path.');
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
artifact.status = 'failed';
|
|
133
|
+
artifact.error = error && error.message ? error.message : String(error);
|
|
134
|
+
} finally {
|
|
135
|
+
if (cleanup && artifact.probeRecordId) {
|
|
136
|
+
artifact.cleanup.attempted = true;
|
|
137
|
+
artifact.cleanup.endpoint = w.buildDeleteStoresProductRequest(artifact.probeRecordId).url;
|
|
138
|
+
artifact.cleanup.method = 'DELETE';
|
|
139
|
+
try {
|
|
140
|
+
await wix.send(w.buildDeleteStoresProductRequest(artifact.probeRecordId));
|
|
141
|
+
artifact.cleanup.status = 'deleted';
|
|
142
|
+
} catch (error) {
|
|
143
|
+
artifact.cleanup.status = 'failed';
|
|
144
|
+
artifact.cleanup.error = error && error.message ? error.message : String(error);
|
|
145
|
+
artifact.warnings.push(`Probe product cleanup failed for ${artifact.probeRecordId}.`);
|
|
146
|
+
artifact.recoveryInstructions.push(
|
|
147
|
+
`Delete probe product ${artifact.probeRecordId} from Wix Stores or run: verify stores delete-probe --product-id ${artifact.probeRecordId}`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return writeArtifact(artifactPath, artifact);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function verifyStoresProductCount({ wix, siteId, artifactPath, query = { paging: { limit: 1, offset: 0 } } } = {}) {
|
|
157
|
+
const request = w.buildQueryStoresProductsRequest(query);
|
|
158
|
+
const artifact = artifactBase({
|
|
159
|
+
command: 'stores product-count',
|
|
160
|
+
siteId,
|
|
161
|
+
endpoint: request.url,
|
|
162
|
+
method: request.method,
|
|
163
|
+
});
|
|
164
|
+
try {
|
|
165
|
+
const response = await wix.send(request);
|
|
166
|
+
const products = response.products || [];
|
|
167
|
+
artifact.count = Number.isInteger(response.totalCount) ? response.totalCount : products.length;
|
|
168
|
+
artifact.status = 'passed';
|
|
169
|
+
} catch (error) {
|
|
170
|
+
artifact.status = 'failed';
|
|
171
|
+
artifact.error = error && error.message ? error.message : String(error);
|
|
172
|
+
}
|
|
173
|
+
return writeArtifact(artifactPath, artifact);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function verifyStoresProductBySourceMarker({ wix, siteId, artifactPath, markerPath, markerValue } = {}) {
|
|
177
|
+
const query = queryFilterByMarker({ markerPath, markerValue });
|
|
178
|
+
const request = w.buildQueryStoresProductsRequest(query);
|
|
179
|
+
const artifact = artifactBase({
|
|
180
|
+
command: 'stores product-by-source-marker',
|
|
181
|
+
siteId,
|
|
182
|
+
endpoint: request.url,
|
|
183
|
+
method: request.method,
|
|
184
|
+
});
|
|
185
|
+
artifact.marker = { path: markerPath, value: markerValue };
|
|
186
|
+
try {
|
|
187
|
+
const response = await wix.send(request);
|
|
188
|
+
artifact.products = (response.products || []).map((product) => ({
|
|
189
|
+
id: product.id,
|
|
190
|
+
name: product.name,
|
|
191
|
+
slug: product.slug,
|
|
192
|
+
revision: product.revision,
|
|
193
|
+
}));
|
|
194
|
+
artifact.count = artifact.products.length;
|
|
195
|
+
artifact.status = 'passed';
|
|
196
|
+
} catch (error) {
|
|
197
|
+
artifact.status = 'failed';
|
|
198
|
+
artifact.error = error && error.message ? error.message : String(error);
|
|
199
|
+
}
|
|
200
|
+
return writeArtifact(artifactPath, artifact);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function verifyStoresDeleteProbe({ wix, siteId, artifactPath, productId } = {}) {
|
|
204
|
+
if (!productId) throw new Error('delete-probe requires --product-id');
|
|
205
|
+
const request = w.buildDeleteStoresProductRequest(productId);
|
|
206
|
+
const artifact = artifactBase({
|
|
207
|
+
command: 'stores delete-probe',
|
|
208
|
+
siteId,
|
|
209
|
+
endpoint: request.url,
|
|
210
|
+
method: request.method,
|
|
211
|
+
});
|
|
212
|
+
artifact.probeRecordId = productId;
|
|
213
|
+
artifact.cleanup = { attempted: true, endpoint: request.url, method: request.method, status: 'unknown' };
|
|
214
|
+
try {
|
|
215
|
+
await wix.send(request);
|
|
216
|
+
artifact.cleanup.status = 'deleted';
|
|
217
|
+
artifact.status = 'passed';
|
|
218
|
+
} catch (error) {
|
|
219
|
+
artifact.cleanup.status = 'failed';
|
|
220
|
+
artifact.status = 'failed';
|
|
221
|
+
artifact.error = error && error.message ? error.message : String(error);
|
|
222
|
+
artifact.warnings.push(`Probe product cleanup failed for ${productId}.`);
|
|
223
|
+
artifact.recoveryInstructions.push(`Delete probe product ${productId} manually in Wix Stores and keep this artifact with the cleanup evidence.`);
|
|
224
|
+
}
|
|
225
|
+
return writeArtifact(artifactPath, artifact);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = {
|
|
229
|
+
DEFAULT_VERIFIED_SUBSCRIPTION_PATHS,
|
|
230
|
+
defaultProbeProduct,
|
|
231
|
+
queryFilterByMarker,
|
|
232
|
+
valueAtPath,
|
|
233
|
+
verifyPaths,
|
|
234
|
+
verifyStoresSubscriptionCreate,
|
|
235
|
+
verifyStoresProductCount,
|
|
236
|
+
verifyStoresProductBySourceMarker,
|
|
237
|
+
verifyStoresDeleteProbe,
|
|
238
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// rp-target-wix — the DETERMINISTIC canonical -> Wix payload builder.
|
|
4
|
+
//
|
|
5
|
+
// Replaces the per-project, LLM-authored `src/import/transforms/to-wix.js`. Everything in here is
|
|
6
|
+
// vendor-independent: it consumes a CANONICAL record (see wix-target-spec.js CANONICAL_FIELDS) and
|
|
7
|
+
// emits a Wix Stores V3 create body. Nothing about Shopify, Woo, Magento or BigCommerce appears —
|
|
8
|
+
// that asymmetry is exactly why this layer never needed to be regenerated per run.
|
|
9
|
+
//
|
|
10
|
+
// Scalar placement is driven by the spec (payloadPath / wrap / coerce). The four structural
|
|
11
|
+
// builders — variants, options, media, seoData — are spec-*declared* (`via`) but hand-implemented,
|
|
12
|
+
// because a fully declarative encoding of "variants reference option choices BY NAME" would be a
|
|
13
|
+
// worse artifact than the twenty lines it replaces. The spec still owns WHICH canonical fields
|
|
14
|
+
// route through them, so coverage reporting stays complete.
|
|
15
|
+
|
|
16
|
+
const { STORES_V3_TARGET, CATEGORY_TARGET, CANONICAL_FIELDS } = require('./wix-target-spec.js');
|
|
17
|
+
|
|
18
|
+
// --- coercion --------------------------------------------------------------
|
|
19
|
+
// Money must be an OBJECT ({ amount: '20.00' }); a bare number 400s. Decimal STRINGS avoid float
|
|
20
|
+
// drift on amounts that came out of a CSV as text in the first place.
|
|
21
|
+
function money(amount) {
|
|
22
|
+
const n = Number(amount);
|
|
23
|
+
if (!Number.isFinite(n)) throw new Error(`money: "${amount}" is not a finite number`);
|
|
24
|
+
return { amount: n.toFixed(2) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Wix rejects a slug containing ^!“”$%&'*+,.:;<>=?@\/()[]{}_|` ~#. Vendors mint illegal ones
|
|
28
|
+
// routinely — Shopify turns a decimal size into an underscore (ph-5_5), which 400s the whole
|
|
29
|
+
// bulk batch, so this runs on EVERY slug rather than being a vendor quirk.
|
|
30
|
+
function toWixSlug(raw) {
|
|
31
|
+
const slug = String(raw == null ? '' : raw)
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
34
|
+
.replace(/-{2,}/g, '-')
|
|
35
|
+
.replace(/^-+|-+$/g, '');
|
|
36
|
+
if (!slug) throw new Error(`toWixSlug: "${raw}" sanitizes to an empty slug`);
|
|
37
|
+
return slug;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function coerceBoolean(value, fallback) {
|
|
41
|
+
if (value === true || value === false) return value;
|
|
42
|
+
if (value == null || value === '') return fallback;
|
|
43
|
+
const s = String(value).trim().toLowerCase();
|
|
44
|
+
if (['true', '1', 'yes', 'y', 'active', 'published', 'enabled', 'visible'].includes(s)) return true;
|
|
45
|
+
if (['false', '0', 'no', 'n', 'draft', 'archived', 'disabled', 'hidden'].includes(s)) return false;
|
|
46
|
+
return fallback;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const COERCE = { slug: toWixSlug, money, boolean: coerceBoolean };
|
|
50
|
+
|
|
51
|
+
function isBlank(value) {
|
|
52
|
+
return value == null || value === '' || (Array.isArray(value) && value.length === 0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- structural builders ---------------------------------------------------
|
|
56
|
+
// Option definitions are derived from the variants' own choices, so a value that never occurs
|
|
57
|
+
// cannot produce a phantom choice. Order is first-seen, which preserves the source's ordering.
|
|
58
|
+
function buildOptions(product) {
|
|
59
|
+
const names = product.optionNames || [];
|
|
60
|
+
return names.map((name) => {
|
|
61
|
+
const seen = new Map();
|
|
62
|
+
for (const variant of product.variants || []) {
|
|
63
|
+
for (const choice of variant.choices || []) {
|
|
64
|
+
if (choice.optionName === name && !seen.has(choice.choiceName)) {
|
|
65
|
+
seen.set(choice.choiceName, { choiceType: 'CHOICE_TEXT', name: choice.choiceName });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { name, optionRenderType: 'TEXT_CHOICES', choicesSettings: { choices: [...seen.values()] } };
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// A variant references its option choices BY NAME; the API returns server-assigned choice ids.
|
|
74
|
+
// An empty `choices: []` is the correct encoding of "this product has no real options" — it must
|
|
75
|
+
// produce a single default variant, never a synthetic one-choice option.
|
|
76
|
+
function buildVariants(product, { inStock = true } = {}) {
|
|
77
|
+
return (product.variants || []).map((variant) => {
|
|
78
|
+
const out = {
|
|
79
|
+
visible: coerceBoolean(variant.visible, true),
|
|
80
|
+
choices: (variant.choices || []).map((choice) => ({
|
|
81
|
+
optionChoiceNames: {
|
|
82
|
+
optionName: choice.optionName,
|
|
83
|
+
choiceName: choice.choiceName,
|
|
84
|
+
renderType: 'TEXT_CHOICES',
|
|
85
|
+
},
|
|
86
|
+
})),
|
|
87
|
+
price: buildVariantPrice(variant),
|
|
88
|
+
inventoryItem: { inStock },
|
|
89
|
+
};
|
|
90
|
+
if (!isBlank(variant.sku)) out.sku = String(variant.sku);
|
|
91
|
+
// Weight is expected already converted to the site's unit by the adapter; the canonical field
|
|
92
|
+
// carries a number, not a unit-bearing string.
|
|
93
|
+
if (!isBlank(variant.weight)) out.physicalProperties = { weight: Number(variant.weight) };
|
|
94
|
+
return out;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Implements STORES_V3_TARGET.priceResolution.
|
|
99
|
+
//
|
|
100
|
+
// Two vendor idioms converge here. Shopify ships (price, compareAtPrice); Woo / Magento /
|
|
101
|
+
// BigCommerce ship (regular price, sale price) — which the overlays map to
|
|
102
|
+
// (variant.price, variant.discountedPrice). Wix models `actualPrice` (charged) plus
|
|
103
|
+
// `compareAtPrice` (strike-through), so a sale price has to be PROMOTED to actualPrice and the
|
|
104
|
+
// regular price demoted to the strike-through. Getting this backwards silently overcharges every
|
|
105
|
+
// discounted product, which is why the rule lives in the spec instead of being re-decided per run.
|
|
106
|
+
//
|
|
107
|
+
// TRAP (verified): Wix rejects compareAtPrice <= actualPrice. Vendors write 0.00 — and Woo writes
|
|
108
|
+
// an empty cell — to mean "no discount", so a not-strictly-greater compare-at is DROPPED.
|
|
109
|
+
function buildVariantPrice(variant) {
|
|
110
|
+
const listPrice = Number(variant.price);
|
|
111
|
+
const discounted = variant.discountedPrice;
|
|
112
|
+
|
|
113
|
+
let actual = listPrice;
|
|
114
|
+
let compare = variant.compareAtPrice;
|
|
115
|
+
if (!isBlank(discounted) && Number(discounted) > 0 && Number(discounted) < listPrice) {
|
|
116
|
+
actual = Number(discounted);
|
|
117
|
+
compare = listPrice;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const out = { actualPrice: money(actual) };
|
|
121
|
+
if (!isBlank(compare) && Number(compare) > actual) out.compareAtPrice = money(compare);
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// External URLs on media.itemsInfo.items[] are the preferred path: Wix ingests them server-side in
|
|
126
|
+
// the background, avoiding the throttled Media Manager pre-import. The FIRST item becomes the
|
|
127
|
+
// product's main media, so caller-side ordering matters.
|
|
128
|
+
// TRAP: reading these back needs `?fields=MEDIA_ITEMS_INFO`; a plain GET returns only media.main
|
|
129
|
+
// and looks empty even on a successful ingest.
|
|
130
|
+
function buildMedia(product) {
|
|
131
|
+
const items = (product.images || [])
|
|
132
|
+
.filter((image) => image && image.fetchable !== false && !isBlank(image.url))
|
|
133
|
+
.map((image) => ({ url: image.url, ...(isBlank(image.altText) ? {} : { altText: image.altText }) }));
|
|
134
|
+
return items.length ? { itemsInfo: { items } } : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Emit a title tag only when it differs from the product name — a duplicate tag says nothing and
|
|
138
|
+
// is pure noise in the payload.
|
|
139
|
+
function buildSeoData(product) {
|
|
140
|
+
const tags = [];
|
|
141
|
+
if (!isBlank(product.seoTitle) && String(product.seoTitle).trim() !== String(product.name || '').trim()) {
|
|
142
|
+
tags.push({ type: 'title', children: String(product.seoTitle) });
|
|
143
|
+
}
|
|
144
|
+
if (!isBlank(product.seoDescription)) {
|
|
145
|
+
tags.push({ type: 'meta', props: { name: 'description', content: String(product.seoDescription) } });
|
|
146
|
+
}
|
|
147
|
+
return tags.length ? { tags } : null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- product ---------------------------------------------------------------
|
|
151
|
+
function setPath(target, path, value) {
|
|
152
|
+
const parts = path.split('.');
|
|
153
|
+
let cursor = target;
|
|
154
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
155
|
+
cursor[parts[i]] = cursor[parts[i]] || {};
|
|
156
|
+
cursor = cursor[parts[i]];
|
|
157
|
+
}
|
|
158
|
+
cursor[parts[parts.length - 1]] = value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Builds the Stores V3 create body from a canonical product record.
|
|
162
|
+
// `options.inStock` exists because availability is frequently the ONLY importable inventory signal
|
|
163
|
+
// (most vendor exports carry no quantity column); sending quantity 0 would mark a whole catalog
|
|
164
|
+
// out of stock, so the caller states the intent explicitly.
|
|
165
|
+
function buildProduct(product, { inStock = true, target = STORES_V3_TARGET } = {}) {
|
|
166
|
+
if (isBlank(product.name)) throw new Error('buildProduct: product.name is required');
|
|
167
|
+
const body = { ...target.constants };
|
|
168
|
+
|
|
169
|
+
// Spec-driven scalars.
|
|
170
|
+
for (const [canonicalPath, fieldSpec] of Object.entries(target.fields)) {
|
|
171
|
+
if (!fieldSpec.payloadPath) continue; // structural: handled below
|
|
172
|
+
const key = canonicalPath.replace(/^product\./, '');
|
|
173
|
+
let value = product[key];
|
|
174
|
+
const def = CANONICAL_FIELDS[canonicalPath] || {};
|
|
175
|
+
if (isBlank(value) && def.default !== undefined) value = def.default;
|
|
176
|
+
if (isBlank(value) && typeof value !== 'boolean') continue;
|
|
177
|
+
if (fieldSpec.coerce && COERCE[fieldSpec.coerce]) value = COERCE[fieldSpec.coerce](value);
|
|
178
|
+
else if (def.kind === 'boolean') value = coerceBoolean(value, def.default);
|
|
179
|
+
// A spec-declared length cap is enforced here rather than truncated: silently shortening a
|
|
180
|
+
// description is exactly the invisible data loss this pipeline exists to avoid, and the caller
|
|
181
|
+
// is the one who can decide between truncating, splitting into an info section, or skipping.
|
|
182
|
+
if (fieldSpec.maxLength && typeof value === 'string' && value.length > fieldSpec.maxLength) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`buildProduct: "${product.name}" has a ${value.length}-character ${canonicalPath} but Wix caps ${fieldSpec.payloadPath} at ${fieldSpec.maxLength}. Truncate it, move the overflow into an info section, or skip the field — and record the loss in mapping-gaps.json.`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
setPath(body, fieldSpec.payloadPath, fieldSpec.wrap ? fieldSpec.wrap(value) : value);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Structural builders, declared in the spec via `via`.
|
|
191
|
+
const options = buildOptions(product);
|
|
192
|
+
if (options.length) body.options = options;
|
|
193
|
+
const media = buildMedia(product);
|
|
194
|
+
if (media) body.media = media;
|
|
195
|
+
const seoData = buildSeoData(product);
|
|
196
|
+
if (seoData) body.seoData = seoData;
|
|
197
|
+
body.variantsInfo = { variants: buildVariants(product, { inStock }) };
|
|
198
|
+
if (!body.variantsInfo.variants.length) {
|
|
199
|
+
throw new Error(`buildProduct: "${product.name}" produced no variant; every Wix product needs at least one`);
|
|
200
|
+
}
|
|
201
|
+
return body;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// --- category --------------------------------------------------------------
|
|
205
|
+
// Categories are created depth-ascending so a parent id is always resolvable. Dedupe against a
|
|
206
|
+
// live site must key on name + PARENT, never name alone: a real tree repeats a name at different
|
|
207
|
+
// depths (e.g. "Shampoo & Conditioner" under two different parents).
|
|
208
|
+
function buildCategory(category, { categoryIdByPath = new Map(), target = CATEGORY_TARGET } = {}) {
|
|
209
|
+
if (isBlank(category.name)) throw new Error('buildCategory: category.name is required');
|
|
210
|
+
const body = { ...target.constants };
|
|
211
|
+
for (const [canonicalPath, fieldSpec] of Object.entries(target.fields)) {
|
|
212
|
+
if (!fieldSpec.payloadPath) continue;
|
|
213
|
+
const key = canonicalPath.replace(/^category\./, '');
|
|
214
|
+
let value = category[key];
|
|
215
|
+
const def = CANONICAL_FIELDS[canonicalPath] || {};
|
|
216
|
+
if (isBlank(value) && def.default !== undefined) value = def.default;
|
|
217
|
+
if (isBlank(value) && typeof value !== 'boolean') continue;
|
|
218
|
+
if (def.kind === 'boolean') value = coerceBoolean(value, def.default);
|
|
219
|
+
setPath(body, fieldSpec.payloadPath, value);
|
|
220
|
+
}
|
|
221
|
+
const parentId = category.parentPath ? categoryIdByPath.get(category.parentPath) : null;
|
|
222
|
+
if (parentId) body.parentCategory = { id: parentId };
|
|
223
|
+
return body;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
module.exports = {
|
|
227
|
+
money,
|
|
228
|
+
toWixSlug,
|
|
229
|
+
coerceBoolean,
|
|
230
|
+
buildOptions,
|
|
231
|
+
buildVariants,
|
|
232
|
+
buildVariantPrice,
|
|
233
|
+
buildMedia,
|
|
234
|
+
buildSeoData,
|
|
235
|
+
buildProduct,
|
|
236
|
+
buildCategory,
|
|
237
|
+
};
|