@wix/agent-skills 1.15.0 → 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 +5 -1
- 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 +6 -4
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +28 -7
- package/skills/wix-app/references/SERVICE_PLUGIN.md +12 -10
- package/skills/wix-app/references/STORES_VERSIONING.md +9 -211
- package/skills/wix-app/references/app-tools/TOOLS.md +75 -0
- package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +5 -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 +172 -0
- 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/COMPONENT-PREVIEW.md +57 -0
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +33 -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 +4 -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/CATEGORIES.md +22 -0
- package/skills/wix-app/references/stores/GET_PRODUCT.md +99 -0
- package/skills/wix-app/references/stores/INVENTORY.md +37 -0
- package/skills/wix-app/references/stores/QUERY.md +119 -0
- package/skills/wix-app/references/stores/WRITE.md +55 -0
- package/skills/wix-docs/SKILL.md +35 -3
- package/skills/wix-headless/SKILL.md +9 -2
- package/skills/wix-headless/entry/bootstrap.mjs +2 -2
- package/skills/wix-headless/entry/skill.md +0 -2
- package/skills/wix-headless/references/CAPABILITIES.md +6 -4
- 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 +25 -1
- package/skills/wix-headless/references/FEEDBACK.md +16 -5
- package/skills/wix-headless/references/IMAGE_GENERATION.md +10 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +2 -0
- package/skills/wix-headless/references/SEED.md +3 -1
- package/skills/wix-headless/references/SETUP.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +3 -2
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +42 -2
- package/skills/wix-headless/references/inline-recipes/setup-bookings.md +5 -1
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +1 -1
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +33 -8
- package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +3 -1
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +73 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +38 -2
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +20 -1
- package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +1 -1
- package/skills/wix-headless/references/managed/CONNECT.md +2 -2
- package/skills/wix-headless/references/managed/CREATE.md +15 -1
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +47 -0
- package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +8 -0
- package/skills/wix-headless/references/stripe/DEPLOYMENT.md +8 -0
- package/skills/wix-manage/SKILL.md +131 -12
- package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +41 -0
- package/skills/wix-manage/references/analytics/query-site-analytics.md +66 -9
- package/skills/wix-manage/references/app-installation/app-installation-dashboard-navigation.md +28 -0
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -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/create-booking-service-from-prompt.md +6 -5
- package/skills/wix-manage/references/bookings/create-course-service.md +30 -5
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +85 -42
- 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 +33 -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/get-paid/how-to-setup-wix-payments.md +30 -7
- 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/create-and-publish-social-post.md +225 -144
- package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +19 -10
- 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 +243 -0
- 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/create-product-from-image.md +8 -6
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +16 -10
- 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 +54 -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 +26 -0
- package/skills/wix-vibe-headless/platforms/base44.md +177 -0
- package/skills/wix-vibe-headless/platforms/generic.md +122 -0
- 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 -145
- 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} +90 -12
- 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} +3 -2
- 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/{wix-portfolio.js → app/rest/wix-portfolio.js} +84 -55
- 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 -148
- 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} +25 -2
- 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/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,384 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const wixWriters = require('./wix-writers.js');
|
|
6
|
+
|
|
7
|
+
const CLASSIFICATIONS = new Set([
|
|
8
|
+
'native',
|
|
9
|
+
'cms',
|
|
10
|
+
'native-plus-cms',
|
|
11
|
+
'setup-config',
|
|
12
|
+
'skip-by-default',
|
|
13
|
+
'unsupported-native-gap',
|
|
14
|
+
]);
|
|
15
|
+
const ID_POLICIES = new Set(['client-assigned', 'server-assigned', 'natural-key', 'not-applicable']);
|
|
16
|
+
const VERIFICATIONS = new Set(['verified-live', 'docs', 'source-review', 'internal-only', 'unverified', 'none']);
|
|
17
|
+
const RELIABILITY = new Set(['reliable', 'partially-reliable', 'unreliable', 'unknown']);
|
|
18
|
+
const SAFE_MODE_CONTACT_KINDS = new Set(['email', 'phone']);
|
|
19
|
+
const WRITER_IDS = new Set(Object.keys(wixWriters).filter((name) => typeof wixWriters[name] === 'function'));
|
|
20
|
+
|
|
21
|
+
function knowledgeRoot(rootDir = path.resolve(__dirname, '..')) {
|
|
22
|
+
return path.join(rootDir, 'domains');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function readJson(filePath) {
|
|
26
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeJson(filePath, value) {
|
|
30
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function entityRef(entity) {
|
|
34
|
+
return `${entity.domain}/${entity.entity}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function listDomainDirs(domainsDir) {
|
|
38
|
+
return fs
|
|
39
|
+
.readdirSync(domainsDir, { withFileTypes: true })
|
|
40
|
+
.filter((entry) => entry.isDirectory())
|
|
41
|
+
.map((entry) => entry.name)
|
|
42
|
+
.sort();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function listEntityFiles(domainDir) {
|
|
46
|
+
const entitiesDir = path.join(domainDir, 'entities');
|
|
47
|
+
if (!fs.existsSync(entitiesDir)) return [];
|
|
48
|
+
return fs
|
|
49
|
+
.readdirSync(entitiesDir, { withFileTypes: true })
|
|
50
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
|
|
51
|
+
.map((entry) => entry.name)
|
|
52
|
+
.sort();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function loadDomain(domainsDir, domain) {
|
|
56
|
+
return readJson(path.join(domainsDir, domain, 'domain.json'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function loadEntity(domainsDir, domain, entity) {
|
|
60
|
+
return readJson(path.join(domainsDir, domain, 'entities', `${entity}.json`));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function generateIndex(domainsDir) {
|
|
64
|
+
const index = {
|
|
65
|
+
schemaVersion: 1,
|
|
66
|
+
domains: {},
|
|
67
|
+
sourceAliasIndex: {},
|
|
68
|
+
routeAliasIndex: {},
|
|
69
|
+
flags: {},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
for (const domain of listDomainDirs(domainsDir)) {
|
|
73
|
+
const domainPath = path.join(domain, 'domain.json');
|
|
74
|
+
const domainJson = loadDomain(domainsDir, domain);
|
|
75
|
+
const entities = {};
|
|
76
|
+
for (const fileName of listEntityFiles(path.join(domainsDir, domain))) {
|
|
77
|
+
const entityId = fileName.replace(/\.json$/, '');
|
|
78
|
+
const entityPath = path.join(domain, 'entities', fileName);
|
|
79
|
+
const entity = readJson(path.join(domainsDir, entityPath));
|
|
80
|
+
entities[entityId] = {
|
|
81
|
+
path: `domains/${entityPath}`,
|
|
82
|
+
displayName: entity.displayName,
|
|
83
|
+
classification: entity.target && entity.target.classification,
|
|
84
|
+
reliabilityStatus: entity.reliability && entity.reliability.status,
|
|
85
|
+
reliabilityFlags: (entity.reliability && entity.reliability.flags) || [],
|
|
86
|
+
summary: firstPitfallOrGuidance(entity),
|
|
87
|
+
};
|
|
88
|
+
for (const alias of entity.sourceAliases || []) {
|
|
89
|
+
const key = `${alias.sourceSystem}:${alias.sourceEntity}`;
|
|
90
|
+
if (!index.sourceAliasIndex[key]) index.sourceAliasIndex[key] = [];
|
|
91
|
+
index.sourceAliasIndex[key].push(`${domain}/${entityId}`);
|
|
92
|
+
for (const route of alias.routes || []) {
|
|
93
|
+
if (!index.routeAliasIndex[route]) index.routeAliasIndex[route] = [];
|
|
94
|
+
index.routeAliasIndex[route].push(`${domain}/${entityId}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
for (const flag of (entity.reliability && entity.reliability.flags) || []) {
|
|
98
|
+
if (!index.flags[flag]) index.flags[flag] = [];
|
|
99
|
+
index.flags[flag].push(`${domain}/${entityId}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
index.domains[domain] = {
|
|
103
|
+
path: `domains/${domainPath}`,
|
|
104
|
+
displayName: domainJson.displayName,
|
|
105
|
+
ownerHint: domainJson.ownerHint,
|
|
106
|
+
entities,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const collection of [index.sourceAliasIndex, index.routeAliasIndex, index.flags]) {
|
|
111
|
+
for (const key of Object.keys(collection)) collection[key] = Array.from(new Set(collection[key])).sort();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return index;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function firstPitfallOrGuidance(entity) {
|
|
118
|
+
if (Array.isArray(entity.pitfalls) && entity.pitfalls[0] && entity.pitfalls[0].summary) {
|
|
119
|
+
return entity.pitfalls[0].summary;
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(entity.mappingGuidance) && entity.mappingGuidance[0]) return entity.mappingGuidance[0];
|
|
122
|
+
return '';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function validateKnowledge(domainsDir) {
|
|
126
|
+
const errors = [];
|
|
127
|
+
const indexPath = path.join(domainsDir, 'index.json');
|
|
128
|
+
const index = fs.existsSync(indexPath) ? readJson(indexPath) : null;
|
|
129
|
+
const generated = generateIndex(domainsDir);
|
|
130
|
+
|
|
131
|
+
if (!fs.existsSync(path.join(domainsDir, 'schema.json'))) {
|
|
132
|
+
errors.push('domains/schema.json is missing');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const domain of listDomainDirs(domainsDir)) {
|
|
136
|
+
const domainFile = path.join(domainsDir, domain, 'domain.json');
|
|
137
|
+
if (!fs.existsSync(domainFile)) {
|
|
138
|
+
errors.push(`${domain}: missing domain.json`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const domainJson = readJson(domainFile);
|
|
142
|
+
requireFields(domainJson, ['schemaVersion', 'domain', 'displayName', 'ownerHint', 'defaultImportOrder', 'evidence'], `${domain}/domain.json`, errors);
|
|
143
|
+
if (domainJson.schemaVersion !== 1) errors.push(`${domain}/domain.json: schemaVersion must be 1`);
|
|
144
|
+
if (domainJson.domain !== domain) errors.push(`${domain}/domain.json: domain must match directory`);
|
|
145
|
+
for (const entityId of domainJson.defaultImportOrder || []) {
|
|
146
|
+
if (!fs.existsSync(path.join(domainsDir, domain, 'entities', `${entityId}.json`))) {
|
|
147
|
+
errors.push(`${domain}/domain.json: defaultImportOrder references missing entity ${entityId}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
validateEvidence(domainJson.evidence || [], `${domain}/domain.json`, errors);
|
|
151
|
+
|
|
152
|
+
for (const fileName of listEntityFiles(path.join(domainsDir, domain))) {
|
|
153
|
+
const entityId = fileName.replace(/\.json$/, '');
|
|
154
|
+
const entity = loadEntity(domainsDir, domain, entityId);
|
|
155
|
+
const label = `${domain}/entities/${fileName}`;
|
|
156
|
+
validateEntity(entity, domain, entityId, label, errors);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!index) {
|
|
161
|
+
errors.push('domains/index.json is missing; run domain-knowledge-validate.js --write-index');
|
|
162
|
+
} else {
|
|
163
|
+
const current = JSON.stringify(index);
|
|
164
|
+
const expected = JSON.stringify(generated);
|
|
165
|
+
if (current !== expected) {
|
|
166
|
+
errors.push('domains/index.json is stale; run domain-knowledge-validate.js --write-index');
|
|
167
|
+
}
|
|
168
|
+
validateIndexConsistency(index, domainsDir, errors);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { ok: errors.length === 0, errors, generatedIndex: generated };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function requireFields(value, fields, label, errors) {
|
|
175
|
+
for (const field of fields) {
|
|
176
|
+
if (value[field] === undefined) errors.push(`${label}: missing required field ${field}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function validateEntity(entity, domain, entityId, label, errors) {
|
|
181
|
+
requireFields(entity, ['schemaVersion', 'domain', 'entity', 'displayName', 'target', 'sourceAliases', 'preferredWrite', 'reliability', 'pitfalls', 'mappingGuidance', 'evidence'], label, errors);
|
|
182
|
+
if (entity.schemaVersion !== 1) errors.push(`${label}: schemaVersion must be 1`);
|
|
183
|
+
if (entity.domain !== domain) errors.push(`${label}: domain must match file path`);
|
|
184
|
+
if (entity.entity !== entityId) errors.push(`${label}: entity must match file path`);
|
|
185
|
+
if (!CLASSIFICATIONS.has(entity.target && entity.target.classification)) errors.push(`${label}: invalid target.classification`);
|
|
186
|
+
if (!ID_POLICIES.has(entity.target && entity.target.idPolicy)) errors.push(`${label}: invalid target.idPolicy`);
|
|
187
|
+
if (typeof (entity.target && entity.target.crosswalkRequired) !== 'boolean') errors.push(`${label}: target.crosswalkRequired must be boolean`);
|
|
188
|
+
if (!VERIFICATIONS.has(entity.preferredWrite && entity.preferredWrite.verification)) errors.push(`${label}: invalid preferredWrite.verification`);
|
|
189
|
+
if (!RELIABILITY.has(entity.reliability && entity.reliability.status)) errors.push(`${label}: invalid reliability.status`);
|
|
190
|
+
if (!Array.isArray(entity.reliability && entity.reliability.flags)) errors.push(`${label}: reliability.flags must be an array`);
|
|
191
|
+
if (entity.preferredWrite && entity.preferredWrite.writerId !== null && !WRITER_IDS.has(entity.preferredWrite.writerId)) {
|
|
192
|
+
errors.push(`${label}: writerId ${entity.preferredWrite.writerId} is not exported by wix-writers.js`);
|
|
193
|
+
}
|
|
194
|
+
if (entity.target && entity.target.classification !== 'cms' && entity.target.idPolicy === 'server-assigned' && entity.target.crosswalkRequired !== true) {
|
|
195
|
+
errors.push(`${label}: server-assigned non-CMS targets must require crosswalk`);
|
|
196
|
+
}
|
|
197
|
+
for (const alias of entity.sourceAliases || []) {
|
|
198
|
+
if (!alias.sourceSystem || !alias.sourceEntity) errors.push(`${label}: sourceAliases entries must include sourceSystem and sourceEntity`);
|
|
199
|
+
}
|
|
200
|
+
validateSafeModeContactFields(entity.safeModeContactFields, label, errors);
|
|
201
|
+
validateEvidence(entity.evidence || [], label, errors);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function validateSafeModeContactFields(fields, label, errors) {
|
|
205
|
+
if (fields === undefined) return;
|
|
206
|
+
if (!Array.isArray(fields)) {
|
|
207
|
+
errors.push(`${label}: safeModeContactFields must be an array when present`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
for (const [index, field] of fields.entries()) {
|
|
211
|
+
const fieldLabel = `${label}: safeModeContactFields[${index}]`;
|
|
212
|
+
if (!field || typeof field !== 'object' || Array.isArray(field)) {
|
|
213
|
+
errors.push(`${fieldLabel} must be an object`);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (!SAFE_MODE_CONTACT_KINDS.has(field.kind)) errors.push(`${fieldLabel}.kind must be email or phone`);
|
|
217
|
+
if (!field.targetPath || typeof field.targetPath !== 'string') errors.push(`${fieldLabel}.targetPath must be a non-empty string`);
|
|
218
|
+
if (!field.source || typeof field.source !== 'string') errors.push(`${fieldLabel}.source must be a non-empty string`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function validateEvidence(items, label, errors) {
|
|
223
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
224
|
+
errors.push(`${label}: evidence must be a non-empty array`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
for (const item of items) {
|
|
228
|
+
if (!item.url && !item.path) errors.push(`${label}: every evidence item must include url or path`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function validateIndexConsistency(index, domainsDir, errors) {
|
|
233
|
+
const flagged = new Set(index.flags && index.flags.IMPORT_UNRELIABLE ? index.flags.IMPORT_UNRELIABLE : []);
|
|
234
|
+
const entityFlagged = new Set();
|
|
235
|
+
|
|
236
|
+
for (const [domain, domainEntry] of Object.entries(index.domains || {})) {
|
|
237
|
+
if (!fs.existsSync(path.join(domainsDir, domain, 'domain.json'))) errors.push(`index: missing domain file for ${domain}`);
|
|
238
|
+
for (const [entityId, entityEntry] of Object.entries(domainEntry.entities || {})) {
|
|
239
|
+
const ref = `${domain}/${entityId}`;
|
|
240
|
+
const entityPath = path.join(domainsDir, domain, 'entities', `${entityId}.json`);
|
|
241
|
+
if (!fs.existsSync(entityPath)) {
|
|
242
|
+
errors.push(`index: missing entity file for ${ref}`);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const entity = readJson(entityPath);
|
|
246
|
+
if ((entity.reliability.flags || []).includes('IMPORT_UNRELIABLE')) entityFlagged.add(ref);
|
|
247
|
+
if (entityEntry.path !== `domains/${domain}/entities/${entityId}.json`) {
|
|
248
|
+
errors.push(`index: invalid path for ${ref}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const ref of entityFlagged) {
|
|
254
|
+
if (!flagged.has(ref)) errors.push(`index: ${ref} has IMPORT_UNRELIABLE but is missing from flags`);
|
|
255
|
+
}
|
|
256
|
+
for (const ref of flagged) {
|
|
257
|
+
if (!entityFlagged.has(ref)) errors.push(`index: ${ref} is flagged IMPORT_UNRELIABLE but entity file is not`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function loadIndex(domainsDir) {
|
|
262
|
+
return readJson(path.join(domainsDir, 'index.json'));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function listDomains(domainsDir) {
|
|
266
|
+
const index = loadIndex(domainsDir);
|
|
267
|
+
return Object.entries(index.domains || {}).map(([domain, info]) => ({
|
|
268
|
+
domain,
|
|
269
|
+
displayName: info.displayName,
|
|
270
|
+
ownerHint: info.ownerHint,
|
|
271
|
+
path: info.path,
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function listEntities(domainsDir, domain) {
|
|
276
|
+
const index = loadIndex(domainsDir);
|
|
277
|
+
const domainInfo = index.domains && index.domains[domain];
|
|
278
|
+
if (!domainInfo) throw new Error(`Unknown domain: ${domain}`);
|
|
279
|
+
return Object.entries(domainInfo.entities || {}).map(([entity, info]) => ({
|
|
280
|
+
ref: `${domain}/${entity}`,
|
|
281
|
+
entity,
|
|
282
|
+
displayName: info.displayName,
|
|
283
|
+
classification: info.classification,
|
|
284
|
+
reliabilityStatus: info.reliabilityStatus,
|
|
285
|
+
reliabilityFlags: info.reliabilityFlags,
|
|
286
|
+
path: info.path,
|
|
287
|
+
}));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function readEntityByRef(domainsDir, ref) {
|
|
291
|
+
const [domain, entity] = ref.split('/');
|
|
292
|
+
if (!domain || !entity) throw new Error(`Invalid ref: ${ref}`);
|
|
293
|
+
return loadEntity(domainsDir, domain, entity);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function resolveSource(domainsDir, { sourceSystem, sourceEntity, route }) {
|
|
297
|
+
const index = loadIndex(domainsDir);
|
|
298
|
+
const refs = new Set();
|
|
299
|
+
if (sourceSystem && sourceEntity) {
|
|
300
|
+
for (const ref of index.sourceAliasIndex[`${sourceSystem}:${sourceEntity}`] || []) refs.add(ref);
|
|
301
|
+
}
|
|
302
|
+
if (route) {
|
|
303
|
+
for (const [pattern, patternRefs] of Object.entries(index.routeAliasIndex || {})) {
|
|
304
|
+
if (route === pattern || routeMatches(pattern, route)) {
|
|
305
|
+
for (const ref of patternRefs) refs.add(ref);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return Array.from(refs).sort().map((ref) => {
|
|
310
|
+
const entity = readEntityByRef(domainsDir, ref);
|
|
311
|
+
return {
|
|
312
|
+
ref,
|
|
313
|
+
confidence: bestAliasConfidence(entity, { sourceSystem, sourceEntity, route }),
|
|
314
|
+
sourceAliases: (entity.sourceAliases || []).filter((alias) => aliasMatches(alias, { sourceSystem, sourceEntity, route })),
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function routeMatches(pattern, route) {
|
|
320
|
+
if (!pattern.includes('{') && !pattern.includes(':')) return false;
|
|
321
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\{[^}]+\\\}/g, '[^/]+').replace(/:[A-Za-z0-9_-]+/g, '[^/]+');
|
|
322
|
+
return new RegExp(`^${escaped}$`).test(route);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function aliasMatches(alias, query) {
|
|
326
|
+
const sourceMatches = (!query.sourceSystem || alias.sourceSystem === query.sourceSystem) && (!query.sourceEntity || alias.sourceEntity === query.sourceEntity);
|
|
327
|
+
const routeMatchesAlias = !query.route || (alias.routes || []).some((pattern) => query.route === pattern || routeMatches(pattern, query.route));
|
|
328
|
+
return sourceMatches && routeMatchesAlias;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function bestAliasConfidence(entity, query) {
|
|
332
|
+
const order = { high: 3, medium: 2, low: 1 };
|
|
333
|
+
let best = 'low';
|
|
334
|
+
for (const alias of entity.sourceAliases || []) {
|
|
335
|
+
if (aliasMatches(alias, query) && (order[alias.confidence] || 0) > (order[best] || 0)) best = alias.confidence;
|
|
336
|
+
}
|
|
337
|
+
return best;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function listFlagged(domainsDir, flag) {
|
|
341
|
+
const index = loadIndex(domainsDir);
|
|
342
|
+
return (index.flags && index.flags[flag] ? index.flags[flag] : []).map((ref) => {
|
|
343
|
+
const entity = readEntityByRef(domainsDir, ref);
|
|
344
|
+
return {
|
|
345
|
+
ref,
|
|
346
|
+
displayName: entity.displayName,
|
|
347
|
+
classification: entity.target.classification,
|
|
348
|
+
reliabilityStatus: entity.reliability.status,
|
|
349
|
+
summary: firstPitfallOrGuidance(entity),
|
|
350
|
+
};
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function summarizeEntities(domainsDir, refs, { includeEvidence = false } = {}) {
|
|
355
|
+
return refs.map((ref) => {
|
|
356
|
+
const entity = readEntityByRef(domainsDir, ref);
|
|
357
|
+
const summary = {
|
|
358
|
+
ref,
|
|
359
|
+
displayName: entity.displayName,
|
|
360
|
+
target: entity.target,
|
|
361
|
+
preferredWrite: entity.preferredWrite,
|
|
362
|
+
reliability: entity.reliability,
|
|
363
|
+
pitfalls: entity.pitfalls,
|
|
364
|
+
mappingGuidance: entity.mappingGuidance,
|
|
365
|
+
setupRequirements: entity.setupRequirements || [],
|
|
366
|
+
fieldContracts: entity.fieldContracts || [],
|
|
367
|
+
};
|
|
368
|
+
if (includeEvidence) summary.evidence = entity.evidence;
|
|
369
|
+
return summary;
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
module.exports = {
|
|
374
|
+
knowledgeRoot,
|
|
375
|
+
generateIndex,
|
|
376
|
+
validateKnowledge,
|
|
377
|
+
writeJson,
|
|
378
|
+
listDomains,
|
|
379
|
+
listEntities,
|
|
380
|
+
readEntityByRef,
|
|
381
|
+
resolveSource,
|
|
382
|
+
listFlagged,
|
|
383
|
+
summarizeEntities,
|
|
384
|
+
};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// rp-target-wix — NDJSON record-stream I/O for migration data files.
|
|
4
|
+
//
|
|
5
|
+
// Every migration writes the same shape of intermediate data: a long stream of per-entity
|
|
6
|
+
// records that a later stage scans, batches and cursors through. That is a line-delimited
|
|
7
|
+
// format, not a single JSON document, so it is built once here rather than re-derived by
|
|
8
|
+
// codegen per project.
|
|
9
|
+
//
|
|
10
|
+
// WHY NDJSON over a JSON array (`{ entity, records: [...] }`):
|
|
11
|
+
// - scan: counting or filtering never parses the whole file, and never holds it in memory
|
|
12
|
+
// - batch: a bulk endpoint's page is `readBatches(file, 100)` — no slicing a giant array.
|
|
13
|
+
// Use `readBatchesByLimits` when the endpoint caps several dimensions at once (Wix bulk
|
|
14
|
+
// product create caps products, variants AND options per request).
|
|
15
|
+
// - cursor: resume from record N is a byte offset or a line skip, not a full re-parse
|
|
16
|
+
// - append: a producer can emit records as it finds them; a crash leaves a valid prefix
|
|
17
|
+
// - a single malformed record is one bad line, not an unparseable file
|
|
18
|
+
//
|
|
19
|
+
// USE IT FOR record streams (`data/source-extract/<entity>.ndjson`, crosswalks, audit logs).
|
|
20
|
+
// Do NOT use it for single documents — a manifest, mapping plan, decisions or completion
|
|
21
|
+
// report is one object and stays `.json`. Line-delimiting a document buys nothing and makes
|
|
22
|
+
// it unreadable.
|
|
23
|
+
//
|
|
24
|
+
// Dependency-free and streaming, like the source adapters' transport modules. Codegen
|
|
25
|
+
// vendors a copy into the project (e.g. `src/lib/ndjson.js`) and imports from it.
|
|
26
|
+
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const fsp = require('node:fs/promises');
|
|
29
|
+
const path = require('node:path');
|
|
30
|
+
const readline = require('node:readline');
|
|
31
|
+
|
|
32
|
+
const EXT = '.ndjson';
|
|
33
|
+
|
|
34
|
+
function ensureDir(filePath) {
|
|
35
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// One record per line. JSON.stringify escapes embedded newlines, so a record can never
|
|
39
|
+
// break the line framing.
|
|
40
|
+
function encodeRecord(record) {
|
|
41
|
+
if (record === undefined) throw new Error('ndjson: cannot encode undefined');
|
|
42
|
+
const line = JSON.stringify(record);
|
|
43
|
+
if (line === undefined) throw new Error('ndjson: record is not JSON-serialisable');
|
|
44
|
+
return `${line}\n`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// --- writing ---------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
// Streaming write of a whole stream. Accepts arrays, generators and async generators, so a
|
|
50
|
+
// reader can hand over records as it produces them without materialising them all.
|
|
51
|
+
async function writeRecords(filePath, records) {
|
|
52
|
+
ensureDir(filePath);
|
|
53
|
+
const out = fs.createWriteStream(filePath, { flags: 'w' });
|
|
54
|
+
let count = 0;
|
|
55
|
+
try {
|
|
56
|
+
for await (const record of records) {
|
|
57
|
+
count += 1;
|
|
58
|
+
if (!out.write(encodeRecord(record))) {
|
|
59
|
+
await new Promise((resolve) => out.once('drain', resolve));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} finally {
|
|
63
|
+
await new Promise((resolve, reject) => {
|
|
64
|
+
out.end((err) => (err ? reject(err) : resolve()));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return count;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Incremental appender for producers that emit over time (crosswalk, audit log). `flush`
|
|
71
|
+
// is per-record and O(1) — the whole point of not rewriting a JSON array each time.
|
|
72
|
+
function createAppender(filePath) {
|
|
73
|
+
ensureDir(filePath);
|
|
74
|
+
let count = 0;
|
|
75
|
+
return {
|
|
76
|
+
file: filePath,
|
|
77
|
+
append(record) {
|
|
78
|
+
fs.appendFileSync(filePath, encodeRecord(record));
|
|
79
|
+
count += 1;
|
|
80
|
+
return record;
|
|
81
|
+
},
|
|
82
|
+
appendAll(records) {
|
|
83
|
+
let buffer = '';
|
|
84
|
+
for (const record of records) buffer += encodeRecord(record);
|
|
85
|
+
if (buffer) fs.appendFileSync(filePath, buffer);
|
|
86
|
+
count += records.length;
|
|
87
|
+
return records.length;
|
|
88
|
+
},
|
|
89
|
+
written: () => count,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// --- reading ---------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
// Streams records without loading the file. readline handles \n, \r\n and the chunk-boundary
|
|
96
|
+
// case that a naive split('\n') gets wrong.
|
|
97
|
+
//
|
|
98
|
+
// A blank line is skipped (a trailing newline is normal). A malformed line throws, naming the
|
|
99
|
+
// line number — silently dropping records would corrupt a migration invisibly.
|
|
100
|
+
async function* readRecords(filePath, { skipMalformed = false, onMalformed = null } = {}) {
|
|
101
|
+
if (!fs.existsSync(filePath)) return;
|
|
102
|
+
const input = fs.createReadStream(filePath, { encoding: 'utf8' });
|
|
103
|
+
const rl = readline.createInterface({ input, crlfDelay: Infinity });
|
|
104
|
+
let lineNumber = 0;
|
|
105
|
+
try {
|
|
106
|
+
for await (const line of rl) {
|
|
107
|
+
lineNumber += 1;
|
|
108
|
+
if (line.trim() === '') continue;
|
|
109
|
+
let record;
|
|
110
|
+
try {
|
|
111
|
+
record = JSON.parse(line);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
const detail = `${filePath}:${lineNumber} is not valid JSON (${err.message})`;
|
|
114
|
+
if (onMalformed) onMalformed({ file: filePath, line: lineNumber, raw: line, error: err.message });
|
|
115
|
+
if (skipMalformed) continue;
|
|
116
|
+
throw new Error(`ndjson: ${detail}. Refusing to continue — a dropped record is invisible data loss.`);
|
|
117
|
+
}
|
|
118
|
+
yield record;
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
rl.close();
|
|
122
|
+
input.destroy();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// The batching primitive bulk endpoints consume. `size` is a record count; when a target
|
|
127
|
+
// caps something else too (Wix bulk product create allows 100 products AND 1000 variants
|
|
128
|
+
// per request) use readBatchesBy instead.
|
|
129
|
+
async function* readBatches(filePath, size = 100, options = {}) {
|
|
130
|
+
if (!Number.isInteger(size) || size < 1) throw new Error('ndjson: batch size must be a positive integer');
|
|
131
|
+
let batch = [];
|
|
132
|
+
for await (const record of readRecords(filePath, options)) {
|
|
133
|
+
batch.push(record);
|
|
134
|
+
if (batch.length >= size) {
|
|
135
|
+
yield batch;
|
|
136
|
+
batch = [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (batch.length) yield batch;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Batch against SEVERAL simultaneous caps. Real bulk endpoints rarely have just one: Wix
|
|
143
|
+
// bulk product create allows 100 products AND 1000 variants AND 100 options AND 100
|
|
144
|
+
// modifiers AND 100 infoSections per request, and exceeding any single one rejects the whole
|
|
145
|
+
// batch. Batching on record count alone silently produces requests that 428.
|
|
146
|
+
//
|
|
147
|
+
// readBatchesByLimits(file, {
|
|
148
|
+
// limits: { records: 100, variants: 1000, options: 100 },
|
|
149
|
+
// cost: (p) => ({ variants: p.variants.length, options: p.options.length }),
|
|
150
|
+
// })
|
|
151
|
+
//
|
|
152
|
+
// `records` is implicit and always counted. Any dimension absent from `limits` is ignored,
|
|
153
|
+
// so a cost function may report more than the caller caps.
|
|
154
|
+
//
|
|
155
|
+
// A single record that alone exceeds a cap is still emitted, in a batch of one, with
|
|
156
|
+
// `oversized` reported through `onOversized`. Dropping it would be silent data loss and
|
|
157
|
+
// splitting it is impossible — the caller has to decide (skip it, or fail loudly).
|
|
158
|
+
async function* readBatchesByLimits(filePath, { limits = {}, cost = () => ({}), onOversized = null, ...options } = {}) {
|
|
159
|
+
const caps = { records: 100, ...limits };
|
|
160
|
+
for (const [dim, cap] of Object.entries(caps)) {
|
|
161
|
+
if (!(Number.isFinite(cap) && cap >= 1)) throw new Error(`ndjson: limit "${dim}" must be a number >= 1 (got ${cap})`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let batch = [];
|
|
165
|
+
let running = {};
|
|
166
|
+
const costOf = (record) => {
|
|
167
|
+
const c = cost(record) || {};
|
|
168
|
+
return { ...c, records: 1 };
|
|
169
|
+
};
|
|
170
|
+
const wouldExceed = (c) => Object.keys(caps).some((dim) => (running[dim] || 0) + (c[dim] || 0) > caps[dim]);
|
|
171
|
+
const aloneExceeds = (c) => Object.keys(caps).some((dim) => (c[dim] || 0) > caps[dim]);
|
|
172
|
+
|
|
173
|
+
for await (const record of readRecords(filePath, options)) {
|
|
174
|
+
const c = costOf(record);
|
|
175
|
+
if (batch.length && wouldExceed(c)) {
|
|
176
|
+
yield batch;
|
|
177
|
+
batch = [];
|
|
178
|
+
running = {};
|
|
179
|
+
}
|
|
180
|
+
if (aloneExceeds(c) && onOversized) {
|
|
181
|
+
const over = Object.keys(caps).filter((dim) => (c[dim] || 0) > caps[dim]);
|
|
182
|
+
onOversized({ record, cost: c, exceeded: over, caps });
|
|
183
|
+
}
|
|
184
|
+
batch.push(record);
|
|
185
|
+
for (const dim of Object.keys(c)) running[dim] = (running[dim] || 0) + c[dim];
|
|
186
|
+
}
|
|
187
|
+
if (batch.length) yield batch;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Single-dimension convenience wrapper over readBatchesByLimits, kept because plenty of
|
|
191
|
+
// endpoints really do have just one cap.
|
|
192
|
+
async function* readBatchesBy(filePath, { maxCount = 100, maxCost = Infinity, cost = () => 1, ...options } = {}) {
|
|
193
|
+
const limits = { records: maxCount };
|
|
194
|
+
if (Number.isFinite(maxCost)) limits.cost = maxCost;
|
|
195
|
+
yield* readBatchesByLimits(filePath, { ...options, limits, cost: (r) => ({ cost: cost(r) }) });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Cursor: skip `offset` records, take at most `limit`. Used for resume — no need to parse
|
|
199
|
+
// what has already been processed into objects.
|
|
200
|
+
async function* readSlice(filePath, { offset = 0, limit = Infinity, ...options } = {}) {
|
|
201
|
+
let index = 0;
|
|
202
|
+
let taken = 0;
|
|
203
|
+
for await (const record of readRecords(filePath, options)) {
|
|
204
|
+
if (index++ < offset) continue;
|
|
205
|
+
if (taken++ >= limit) return;
|
|
206
|
+
yield record;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Cheap count — parses nothing, just counts non-empty lines.
|
|
211
|
+
async function countRecords(filePath) {
|
|
212
|
+
if (!fs.existsSync(filePath)) return 0;
|
|
213
|
+
const input = fs.createReadStream(filePath, { encoding: 'utf8' });
|
|
214
|
+
const rl = readline.createInterface({ input, crlfDelay: Infinity });
|
|
215
|
+
let count = 0;
|
|
216
|
+
try {
|
|
217
|
+
for await (const line of rl) if (line.trim() !== '') count += 1;
|
|
218
|
+
} finally {
|
|
219
|
+
rl.close();
|
|
220
|
+
input.destroy();
|
|
221
|
+
}
|
|
222
|
+
return count;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Escape hatch for small streams that genuinely need to be in memory at once (a 6-record
|
|
226
|
+
// category list). Named so that using it on a large stream is an obvious mistake.
|
|
227
|
+
async function readAllRecords(filePath, options = {}) {
|
|
228
|
+
const out = [];
|
|
229
|
+
for await (const record of readRecords(filePath, options)) out.push(record);
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// --- migration of existing projects ---------------------------------------
|
|
234
|
+
|
|
235
|
+
// Converts a legacy `{ entity, recordCount, records: [...] }` document to NDJSON. Kept so a
|
|
236
|
+
// project generated before this format change can be moved forward without re-extracting.
|
|
237
|
+
async function convertLegacyJsonFile(jsonPath, ndjsonPath, { recordsKey = 'records' } = {}) {
|
|
238
|
+
const parsed = JSON.parse(await fsp.readFile(jsonPath, 'utf8'));
|
|
239
|
+
const records = Array.isArray(parsed) ? parsed : parsed[recordsKey];
|
|
240
|
+
if (!Array.isArray(records)) {
|
|
241
|
+
throw new Error(`ndjson: ${jsonPath} has no array at "${recordsKey}" to convert`);
|
|
242
|
+
}
|
|
243
|
+
const count = await writeRecords(ndjsonPath, records);
|
|
244
|
+
return { from: jsonPath, to: ndjsonPath, count };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = {
|
|
248
|
+
EXT,
|
|
249
|
+
encodeRecord,
|
|
250
|
+
writeRecords,
|
|
251
|
+
createAppender,
|
|
252
|
+
readRecords,
|
|
253
|
+
readBatches,
|
|
254
|
+
readBatchesBy,
|
|
255
|
+
readBatchesByLimits,
|
|
256
|
+
readSlice,
|
|
257
|
+
countRecords,
|
|
258
|
+
readAllRecords,
|
|
259
|
+
convertLegacyJsonFile,
|
|
260
|
+
};
|