@wix/agent-skills 1.33.1 → 1.35.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 -0
- package/package.json +1 -1
- package/skills/wix-export/SKILL.md +83 -0
- package/skills/wix-harmony-editor/.publish-manifest.json +8 -0
- package/skills/wix-harmony-editor/COPY-SITE.md +494 -0
- package/skills/wix-harmony-editor/SKILL.md +317 -0
- package/skills/wix-harmony-editor/scripts/copy/build-description.mjs +249 -0
- package/skills/wix-harmony-editor/scripts/copy/check-description.mjs +225 -0
- package/skills/wix-harmony-editor/scripts/copy/check-mirror.mjs +89 -0
- package/skills/wix-harmony-editor/scripts/copy/compare-preview.mjs +76 -0
- package/skills/wix-harmony-editor/scripts/copy/deploy-description.mjs +212 -0
- package/skills/wix-harmony-editor/scripts/copy/desc-hash.mjs +44 -0
- package/skills/wix-harmony-editor/scripts/copy/editor-run.mjs +45 -0
- package/skills/wix-harmony-editor/scripts/copy/export-page.mjs +53 -0
- package/skills/wix-harmony-editor/scripts/copy/find-interactions.mjs +76 -0
- package/skills/wix-harmony-editor/scripts/copy/hide-scrollbars.mjs +16 -0
- package/skills/wix-harmony-editor/scripts/copy/import-media.mjs +150 -0
- package/skills/wix-harmony-editor/scripts/copy/link-url.mjs +27 -0
- package/skills/wix-harmony-editor/scripts/copy/lint-api.mjs +56 -0
- package/skills/wix-harmony-editor/scripts/copy/measure-source.mjs +211 -0
- package/skills/wix-harmony-editor/scripts/copy/merge-mirrors.mjs +29 -0
- package/skills/wix-harmony-editor/scripts/copy/mirror-site.mjs +92 -0
- package/skills/wix-harmony-editor/scripts/copy/mobile-stage.mjs +56 -0
- package/skills/wix-harmony-editor/scripts/copy/preview-widths.mjs +73 -0
- package/skills/wix-harmony-editor/scripts/copy/render-plate.mjs +67 -0
- package/skills/wix-harmony-editor/scripts/copy/serve-mirror.mjs +102 -0
- package/skills/wix-harmony-editor/scripts/copy/snapshot-capabilities.mjs +40 -0
- package/skills/wix-harmony-editor/scripts/copy/split-sections.mjs +191 -0
- package/skills/wix-harmony-editor/scripts/copy/upload-media-manager.mjs +61 -0
- package/skills/wix-harmony-editor/scripts/launch.sh +359 -0
- package/skills/wix-headless/SKILL.md +7 -2
- package/skills/wix-headless/references/CAPABILITIES.md +6 -1
- package/skills/wix-headless/references/DISCOVERY.md +2 -1
- package/skills/wix-headless/references/SDK_HANDOFF.md +6 -1
- package/skills/wix-headless/references/SEED.md +3 -2
- package/skills/wix-headless/references/SETUP.md +1 -0
- package/skills/wix-headless/references/inline-recipes/experience-donations.md +102 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-donations.md +146 -0
- package/skills/wix-headless/references/inline-recipes/setup-donations.md +15 -0
- package/skills/wix-headless/references/managed/CREATE.md +1 -1
- package/skills/wix-headless-kit/SKILL.md +110 -21
- package/skills/wix-headless-kit/guides/api-run.md +15 -13
- package/skills/wix-headless-kit/guides/existing-site.md +31 -0
- package/skills/wix-headless-kit/guides/migration.md +49 -17
- package/skills/wix-headless-kit/install/agents-md.mjs +1 -1
- package/skills/wix-headless-kit/install/context.mjs +61 -6
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-headless-kit/install/setup.mjs +107 -12
- package/skills/wix-headless-replatform/.publish-manifest.json +8 -0
- package/skills/wix-headless-replatform/SKILL.md +116 -0
- package/skills/wix-headless-replatform/agents/openai.yaml +6 -0
- package/skills/wix-headless-replatform/references/control-and-visual-fidelity-contract.md +43 -0
- package/skills/wix-headless-replatform/references/extraction-schema.md +187 -0
- package/skills/wix-headless-replatform/references/interaction-runtime-contract.md +137 -0
- package/skills/wix-headless-replatform/references/layout-blueprint-contract.md +41 -0
- package/skills/wix-headless-replatform/references/output-contract.md +93 -0
- package/skills/wix-headless-replatform/references/post-build-gap-contract.md +125 -0
- package/skills/wix-headless-replatform/references/qa-checklist.md +83 -0
- package/skills/wix-headless-replatform/references/safety.md +19 -0
- package/skills/wix-headless-replatform/references/ui-normalization-contract.md +70 -0
- package/skills/wix-headless-replatform/references/workflow.md +157 -0
- package/skills/wix-headless-replatform/registry/README.md +20 -0
- package/skills/wix-headless-replatform/registry/approvals/button/shadcn-default-ac60ef5.json +28 -0
- package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/button.tsx +54 -0
- package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/capabilities.json +60 -0
- package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/contract.md +48 -0
- package/skills/wix-headless-replatform/registry/licenses/shadcn-ui-ac60ef5-MIT.txt +21 -0
- package/skills/wix-headless-replatform/registry/registry.json +36 -0
- package/skills/wix-headless-replatform/resources/rp-facelift/SKILL.md +32 -0
- package/skills/wix-headless-replatform/resources/rp-project-context/SKILL.md +62 -0
- package/skills/wix-headless-replatform/resources/rp-qa-gap-loop/SKILL.md +64 -0
- package/skills/wix-headless-replatform/resources/rp-source-evidence/SKILL.md +42 -0
- package/skills/wix-headless-replatform/resources/rp-ui-implementation/SKILL.md +73 -0
- package/skills/wix-headless-replatform/schemas/build-plan.schema.json +10 -0
- package/skills/wix-headless-replatform/schemas/candidate.schema.json +19 -0
- package/skills/wix-headless-replatform/schemas/common.schema.json +33 -0
- package/skills/wix-headless-replatform/schemas/decision-patch.schema.json +10 -0
- package/skills/wix-headless-replatform/schemas/decision-request.schema.json +10 -0
- package/skills/wix-headless-replatform/schemas/extraction-gap.schema.json +28 -0
- package/skills/wix-headless-replatform/schemas/extraction-manifest.schema.json +34 -0
- package/skills/wix-headless-replatform/schemas/final-report.schema.json +10 -0
- package/skills/wix-headless-replatform/schemas/foundation.schema.json +11 -0
- package/skills/wix-headless-replatform/schemas/metadata.schema.json +11 -0
- package/skills/wix-headless-replatform/schemas/observation-packet.schema.json +17 -0
- package/skills/wix-headless-replatform/schemas/page-capture.schema.json +11 -0
- package/skills/wix-headless-replatform/schemas/page-resolution.schema.json +33 -0
- package/skills/wix-headless-replatform/schemas/shared-chrome.schema.json +11 -0
- package/skills/wix-headless-replatform/schemas/unit.schema.json +40 -0
- package/skills/wix-headless-replatform/scripts/assemble-extraction.mjs +844 -0
- package/skills/wix-headless-replatform/scripts/benchmark-extraction.mjs +163 -0
- package/skills/wix-headless-replatform/scripts/browser-extraction-preflight.mjs +42 -0
- package/skills/wix-headless-replatform/scripts/build-extracted-homepage.mjs +228 -0
- package/skills/wix-headless-replatform/scripts/clear-wix-starter.mjs +87 -0
- package/skills/wix-headless-replatform/scripts/create-wix-project.mjs +505 -0
- package/skills/wix-headless-replatform/scripts/discover-urls.mjs +142 -0
- package/skills/wix-headless-replatform/scripts/extract-assets.mjs +140 -0
- package/skills/wix-headless-replatform/scripts/extract-design-system.mjs +637 -0
- package/skills/wix-headless-replatform/scripts/extract-interactions.mjs +1903 -0
- package/skills/wix-headless-replatform/scripts/extract-page.mjs +2461 -0
- package/skills/wix-headless-replatform/scripts/extract-seo.mjs +25 -0
- package/skills/wix-headless-replatform/scripts/extract-tokens.mjs +63 -0
- package/skills/wix-headless-replatform/scripts/finalize-extraction-report.mjs +126 -0
- package/skills/wix-headless-replatform/scripts/finalize-gap-review.mjs +180 -0
- package/skills/wix-headless-replatform/scripts/finalize-gap-review.test.mjs +98 -0
- package/skills/wix-headless-replatform/scripts/finalize-migration-website.mjs +103 -0
- package/skills/wix-headless-replatform/scripts/generate-control-state-contract.mjs +176 -0
- package/skills/wix-headless-replatform/scripts/generate-design-md.mjs +261 -0
- package/skills/wix-headless-replatform/scripts/generate-extraction-validators.mjs +25 -0
- package/skills/wix-headless-replatform/scripts/generate-invoice-download-backend.mjs +342 -0
- package/skills/wix-headless-replatform/scripts/generate-layout-blueprint.mjs +140 -0
- package/skills/wix-headless-replatform/scripts/generate-repeater-cms.mjs +22 -0
- package/skills/wix-headless-replatform/scripts/generate-route-plan.mjs +143 -0
- package/skills/wix-headless-replatform/scripts/generate-scene-contract.mjs +288 -0
- package/skills/wix-headless-replatform/scripts/generate-ui-normalization.mjs +266 -0
- package/skills/wix-headless-replatform/scripts/generate-visual-assets.mjs +111 -0
- package/skills/wix-headless-replatform/scripts/lib/browser-tooling.mjs +570 -0
- package/skills/wix-headless-replatform/scripts/lib/common.mjs +329 -0
- package/skills/wix-headless-replatform/scripts/lib/component-capabilities.mjs +121 -0
- package/skills/wix-headless-replatform/scripts/lib/component-registry.mjs +282 -0
- package/skills/wix-headless-replatform/scripts/lib/content-boundary.mjs +61 -0
- package/skills/wix-headless-replatform/scripts/lib/extraction-contract.mjs +374 -0
- package/skills/wix-headless-replatform/scripts/lib/extraction-detectors.mjs +243 -0
- package/skills/wix-headless-replatform/scripts/lib/extraction-validators.generated.mjs +1139 -0
- package/skills/wix-headless-replatform/scripts/lib/font-contract.mjs +326 -0
- package/skills/wix-headless-replatform/scripts/lib/frontend-automation-state.mjs +184 -0
- package/skills/wix-headless-replatform/scripts/lib/gap-analysis.mjs +396 -0
- package/skills/wix-headless-replatform/scripts/lib/html-extract.mjs +168 -0
- package/skills/wix-headless-replatform/scripts/lib/interaction-bootstrap.mjs +10 -0
- package/skills/wix-headless-replatform/scripts/lib/interaction-runtime.mjs +194 -0
- package/skills/wix-headless-replatform/scripts/lib/interaction-timeline.mjs +348 -0
- package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/classifiers.mjs +141 -0
- package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/geometry.mjs +43 -0
- package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/vocabulary.mjs +18 -0
- package/skills/wix-headless-replatform/scripts/lib/ui-normalization.css +64 -0
- package/skills/wix-headless-replatform/scripts/optimize-svg.mjs +31 -0
- package/skills/wix-headless-replatform/scripts/post-build-gap.mjs +358 -0
- package/skills/wix-headless-replatform/scripts/provision-repeater-cms.mjs +25 -0
- package/skills/wix-headless-replatform/scripts/record-gap-decision.mjs +39 -0
- package/skills/wix-headless-replatform/scripts/replay-extraction.mjs +107 -0
- package/skills/wix-headless-replatform/scripts/resolve-home-page.mjs +96 -0
- package/skills/wix-headless-replatform/scripts/site-clone.mjs +658 -0
- package/skills/wix-headless-replatform/scripts/validate-font-contract.mjs +29 -0
- package/skills/wix-headless-replatform/scripts/verify-interactions.mjs +669 -0
- package/skills/wix-headless-replatform/scripts/visual-qa.mjs +370 -0
- package/skills/wix-headless-templates/donations/INSTRUCTIONS.md +37 -24
- package/skills/wix-headless-templates/donations/app/components/donations/DonateForm.tsx +49 -23
- package/skills/wix-headless-templates/donations/app/components/donations/DonationReceipt.tsx +6 -5
- package/skills/wix-headless-templates/donations/app/components/donations/GoalProgress.tsx +5 -5
- package/skills/wix-headless-templates/donations/app/wix/donations/campaigns.ts +4 -4
- package/skills/wix-headless-templates/donations/app/wix/donations/donate.ts +7 -3
- package/skills/wix-headless-templates/donations/app/wix/donations/donation-store.ts +2 -2
- package/skills/wix-headless-templates/donations/app/wix/donations/donations-core.ts +12 -9
- package/skills/wix-headless-templates/donations/project/src/components/donations/DonateForm.tsx +49 -23
- package/skills/wix-headless-templates/donations/project/src/components/donations/DonationReceipt.tsx +6 -5
- package/skills/wix-headless-templates/donations/project/src/components/donations/GoalProgress.tsx +5 -5
- package/skills/wix-headless-templates/donations/project/src/wix/donations/campaigns.ts +4 -4
- package/skills/wix-headless-templates/donations/project/src/wix/donations/donate.ts +7 -3
- package/skills/wix-headless-templates/donations/project/src/wix/donations/donation-store.ts +2 -2
- package/skills/wix-headless-templates/donations/project/src/wix/donations/donations-core.ts +12 -9
- package/skills/wix-headless-templates/donations/rest/campaigns.ts +4 -4
- package/skills/wix-headless-templates/donations/rest/donate.ts +7 -3
- package/skills/wix-headless-templates/donations/seed/SEED.md +12 -7
- package/skills/wix-headless-templates/donations/tests/donation-contract.test.mjs +71 -0
- package/skills/wix-headless-templates/donations/tests/donation-form.test.cjs +137 -0
- package/skills/wix-headless-templates/restaurants/INSTRUCTIONS.md +28 -14
- package/skills/wix-headless-templates/restaurants/app/components/restaurants/OrderCartDrawer.tsx +87 -5
- package/skills/wix-headless-templates/restaurants/app/hooks/restaurants/useOrderCart.ts +9 -1
- package/skills/wix-headless-templates/restaurants/app/wix/restaurants/order-store.ts +33 -4
- package/skills/wix-headless-templates/restaurants/app/wix/restaurants/ordering-core.ts +100 -5
- package/skills/wix-headless-templates/restaurants/app/wix/restaurants/ordering.ts +101 -15
- package/skills/wix-headless-templates/restaurants/app/wix/restaurants/types.ts +35 -1
- package/skills/wix-headless-templates/restaurants/project/src/components/restaurants/OrderCartDrawer.tsx +87 -5
- package/skills/wix-headless-templates/restaurants/project/src/hooks/restaurants/useOrderCart.ts +9 -1
- package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/order-store.ts +33 -4
- package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/ordering-core.ts +100 -5
- package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/ordering.ts +101 -15
- package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/types.ts +35 -1
- package/skills/wix-headless-templates/restaurants/rest/ordering.ts +91 -14
- package/skills/wix-import/SKILL.md +90 -0
- package/skills/wix-manage/SKILL.md +14 -4
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +2 -0
- package/skills/wix-manage/references/dashboard-navigation/dashboard-navigation.md +2 -1
- package/skills/wix-manage/references/dashboard-navigation/routes.json +249 -0
- package/skills/wix-manage/references/donations/create-and-update-donation-campaigns.md +139 -0
- package/skills/wix-manage/references/events/events-dashboard-navigation.md +81 -0
- package/skills/wix-manage/references/seo/manage-content-plan.md +207 -222
- package/skills/wix-manage/references/sites/manage-oauth-apps.md +1 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +128 -8
- package/skills/wix-readme/SKILL.md +166 -0
- package/skills/wix-replatform/.publish-manifest.json +5 -5
- package/skills/wix-replatform/CLI.md +162 -0
- package/skills/wix-replatform/README.md +52 -0
- package/skills/wix-replatform/SKILL.md +229 -684
- package/skills/wix-replatform/console/account.css +1 -0
- package/skills/wix-replatform/console/app.css +1 -0
- package/skills/wix-replatform/console/app.js +117 -0
- package/skills/wix-replatform/console/index.html +4 -0
- package/skills/wix-replatform/console/json.css +11 -0
- package/skills/wix-replatform/lib/agent-adapters/index.js +71 -0
- package/skills/wix-replatform/lib/app-page-routing.js +75 -0
- package/skills/wix-replatform/lib/artifact-freshness.js +11 -1
- package/skills/wix-replatform/lib/blocked-data-requests.js +427 -0
- package/skills/wix-replatform/lib/census-reconciliation.js +426 -0
- package/skills/wix-replatform/lib/completion-report.js +1393 -13
- package/skills/wix-replatform/lib/config-env.js +85 -11
- package/skills/wix-replatform/lib/confirmed-pictures.js +212 -0
- package/skills/wix-replatform/lib/dependency-readiness.js +423 -0
- package/skills/wix-replatform/lib/dependency-recovery.js +21 -0
- package/skills/wix-replatform/lib/destination-choice.js +163 -0
- package/skills/wix-replatform/lib/destination-handoff.js +62 -0
- package/skills/wix-replatform/lib/destination-http.js +81 -0
- package/skills/wix-replatform/lib/destination-route.js +98 -0
- package/skills/wix-replatform/lib/destination.js +919 -0
- package/skills/wix-replatform/lib/discovered-census.js +231 -0
- package/skills/wix-replatform/lib/discovered-set-status.js +255 -0
- package/skills/wix-replatform/lib/document-url-guard.js +203 -0
- package/skills/wix-replatform/lib/entity-qa-attestation.js +339 -0
- package/skills/wix-replatform/lib/entity-verification.js +18 -0
- package/skills/wix-replatform/lib/execution-capability.js +120 -0
- package/skills/wix-replatform/lib/execution-log.js +170 -0
- package/skills/wix-replatform/lib/execution-manifest.js +20 -2
- package/skills/wix-replatform/lib/execution-state.js +22 -0
- package/skills/wix-replatform/lib/expectations-ledger.js +96 -0
- package/skills/wix-replatform/lib/generated-plugin-handoff.js +140 -0
- package/skills/wix-replatform/lib/import-readiness-contract.js +23 -0
- package/skills/wix-replatform/lib/import-recovery.js +800 -6
- package/skills/wix-replatform/lib/imported-redirect-targets.js +608 -0
- package/skills/wix-replatform/lib/internal-link-reconciliation.js +166 -0
- package/skills/wix-replatform/lib/link-audit.js +173 -0
- package/skills/wix-replatform/lib/link-census.js +194 -0
- package/skills/wix-replatform/lib/link-plan.js +148 -0
- package/skills/wix-replatform/lib/link-verification.js +274 -0
- package/skills/wix-replatform/lib/link-write-journal.js +42 -0
- package/skills/wix-replatform/lib/local-state.js +869 -30
- package/skills/wix-replatform/lib/member-completion.js +658 -0
- package/skills/wix-replatform/lib/member-crosswalk.js +69 -0
- package/skills/wix-replatform/lib/member-outcome-registry.js +265 -0
- package/skills/wix-replatform/lib/migration-completion.js +311 -0
- package/skills/wix-replatform/lib/operator-identity.js +47 -0
- package/skills/wix-replatform/lib/orchestration-decisions.js +248 -0
- package/skills/wix-replatform/lib/orchestration-preflight.js +401 -19
- package/skills/wix-replatform/lib/orchestration-router.js +608 -32
- package/skills/wix-replatform/lib/orchestration-state.js +188 -12
- package/skills/wix-replatform/lib/order-reference-readiness.js +30 -0
- package/skills/wix-replatform/lib/payments-outcome-registry.js +200 -0
- package/skills/wix-replatform/lib/plugin-cms-fallback.js +618 -0
- package/skills/wix-replatform/lib/preservation-approval-stamp.js +54 -0
- package/skills/wix-replatform/lib/product-media-deferral.js +96 -0
- package/skills/wix-replatform/lib/progress-log.js +19 -3
- package/skills/wix-replatform/lib/read-back-approval.js +184 -0
- package/skills/wix-replatform/lib/read-back-comparison.js +136 -0
- package/skills/wix-replatform/lib/read-back-contract.js +243 -0
- package/skills/wix-replatform/lib/read-back-extract.js +118 -0
- package/skills/wix-replatform/lib/read-back-gate.js +734 -0
- package/skills/wix-replatform/lib/read-back-reads.js +94 -0
- package/skills/wix-replatform/lib/read-back-state.js +107 -0
- package/skills/wix-replatform/lib/read-back-summary.js +81 -0
- package/skills/wix-replatform/lib/rerun-conflicts.js +334 -0
- package/skills/wix-replatform/lib/run-controller.js +401 -0
- package/skills/wix-replatform/lib/seo-outcome-registry.js +45 -0
- package/skills/wix-replatform/lib/setup-verification.js +148 -0
- package/skills/wix-replatform/lib/source-access.js +231 -0
- package/skills/wix-replatform/lib/source-coverage.js +1130 -0
- package/skills/wix-replatform/lib/source-secrets.js +277 -0
- package/skills/wix-replatform/lib/structured-link-patch.js +59 -0
- package/skills/wix-replatform/lib/task-state.js +110 -0
- package/skills/wix-replatform/lib/url-preservation-report.js +319 -0
- package/skills/wix-replatform/lib/url-preservation-state.js +349 -1
- package/skills/wix-replatform/lib/url-redirect-plan.js +728 -0
- package/skills/wix-replatform/lib/url-verification.js +315 -0
- package/skills/wix-replatform/lib/user-action-effects.js +42 -0
- package/skills/wix-replatform/lib/user-actions.js +264 -0
- package/skills/wix-replatform/lib/website-handoff.js +1220 -0
- package/skills/wix-replatform/lib/wix-client-callers.js +54 -0
- package/skills/wix-replatform/lib/wix-dashboard-url.js +35 -0
- package/skills/wix-replatform/lib/write-verification-state.js +92 -0
- package/skills/wix-replatform/lib/write-verification.js +274 -0
- package/skills/wix-replatform/resources/rp-destination/SKILL.md +137 -0
- package/skills/wix-replatform/resources/rp-discovery/SKILL.md +189 -16
- package/skills/wix-replatform/resources/rp-discovery/scripts/promote-source-coverage.js +47 -0
- package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +869 -29
- package/skills/wix-replatform/resources/rp-execute-import/scripts/shipping-estimate-audit.js +70 -0
- package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +195 -29
- package/skills/wix-replatform/resources/rp-execute-setup/scripts/execute-setup.js +442 -0
- package/skills/wix-replatform/resources/rp-execution-policy/SKILL.md +112 -0
- package/skills/wix-replatform/resources/rp-execution-policy/lib/coupon-currency-plan-report.js +143 -0
- package/skills/wix-replatform/resources/rp-execution-policy/lib/payments-report.js +244 -0
- package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +2082 -38
- package/skills/wix-replatform/resources/rp-mapper/SKILL.md +528 -11
- package/skills/wix-replatform/resources/rp-mapper/lib/structure-bridge-planning.js +303 -0
- package/skills/wix-replatform/resources/rp-mapper/scripts/coverage-reconcile.js +88 -0
- package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +6 -5
- package/skills/wix-replatform/resources/rp-preflight/SKILL.md +40 -0
- package/skills/wix-replatform/resources/rp-project-intake/SKILL.md +55 -0
- package/skills/wix-replatform/resources/rp-quick-runtime/SKILL.md +60 -0
- package/skills/wix-replatform/resources/rp-quick-runtime/lib/import-progress.js +89 -0
- package/skills/wix-replatform/resources/rp-quick-runtime/lib/limit-recovery.js +36 -0
- package/skills/wix-replatform/resources/rp-quick-shopify/SKILL.md +123 -0
- package/skills/wix-replatform/resources/rp-quick-shopify/quick-mode.json +124 -0
- package/skills/wix-replatform/resources/rp-quick-shopify/scripts/quick-mode.js +522 -0
- package/skills/wix-replatform/resources/rp-quick-woocommerce/SKILL.md +56 -0
- package/skills/wix-replatform/resources/rp-quick-woocommerce/quick-mode.json +91 -0
- package/skills/wix-replatform/resources/rp-quick-woocommerce/scripts/quick-mode.js +255 -0
- package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +80 -10
- package/skills/wix-replatform/resources/rp-setup-discovery/scripts/quick-setup-plan.js +44 -0
- package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +3 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
- package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +21 -1
- package/skills/wix-replatform/resources/rp-source-inputs/SKILL.md +66 -0
- package/skills/wix-replatform/resources/rp-source-sfcc/SKILL.md +153 -0
- package/skills/wix-replatform/resources/rp-source-shopify/SKILL.md +394 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/discovery-reads.js +200 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/import-findings.js +34 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk-sqlite-worker.js +44 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk.js +1470 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-cli.js +446 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-gift-cards.js +474 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-protected-fields.js +879 -0
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-session.js +426 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/articles.graphql +24 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/blogs.graphql +18 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/collections.graphql +40 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/customers.graphql +76 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/delivery-profiles.graphql +51 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/discounts.graphql +35 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/files.graphql +20 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/gift-cards.graphql +18 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/inventory.graphql +31 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/locations.graphql +21 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/menus.graphql +21 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/metaobjects.graphql +30 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-core.graphql +150 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-fulfillments.graphql +36 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-refunds.graphql +48 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/pages.graphql +20 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-address.graphql +8 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-email.graphql +6 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-name.graphql +8 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-phone.graphql +8 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-address.graphql +12 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-email.graphql +6 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-name.graphql +13 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-phone.graphql +13 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/products.graphql +73 -0
- package/skills/wix-replatform/resources/rp-source-shopify/queries/url-redirects.graphql +13 -0
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-discovery.js +693 -0
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-extract.js +283 -0
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-gift-card-remedy.js +191 -0
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-session-check.js +115 -0
- package/skills/wix-replatform/resources/rp-source-shopify/source-schema.example.json +201 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/PLUGIN-READ-PLANS.md +146 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +736 -22
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/blocked-data-handlers.js +73 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.js +115 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.test-fixture.js +91 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/coupon-discovery.js +440 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/currency-gate.js +125 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.js +252 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.json +183 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-summary.mjs +333 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/invoice-discovery.js +426 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-disposition.js +400 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-entity-read-plan.js +633 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-knowledge.js +1127 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.js +194 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.json +100 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/sampled-ids-batch.js +157 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/secret-value-guard.js +234 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/store-currency.js +101 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/structure-bridge-reader.js +755 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.js +1074 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.test-fixture.js +149 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/woo-inventory.js +25 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +204 -9
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-image-url.js +186 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-plugin-detect.js +1422 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-redirect-sources.js +343 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +262 -26
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/yoast-product-seo-evidence.js +272 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/README.md +373 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/advanced-custom-fields.json +101 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/back-in-stock-notifier-for-woocommerce.json +81 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/capabilities-pending-decision.json +128 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/cms-fallback-eligible.json +18 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/contact-form-7.json +104 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/facebook-for-woocommerce.json +44 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/fingerprint-aliases.json +49 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/free-gifts-for-woocommerce.json +94 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/index.json +863 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mailpoet.json +52 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/membership-for-woocommerce.json +161 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mwb-bookings-for-woocommerce.json +180 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/no-migration-needed.json +286 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pricing-deals-for-woocommerce.json +79 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/product-extras-for-woocommerce.json +120 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pw-woocommerce-gift-cards.json +128 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/requires-development.json +88 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seo-by-rank-math.json +80 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seriously-simple-podcasting.json +46 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/strong-testimonials.json +55 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/subscriptions-for-woocommerce.json +99 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/tablepress.json +55 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/the-events-calendar.json +160 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/ti-woocommerce-wishlist.json +81 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-checkout-field-editor-pro.json +60 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-discount-rules.json +74 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-product-bundle.json +75 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-gift-cards.json +60 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-other-payment-gateway.json +44 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-pdf-invoices-packing-slips.json +57 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-product-addon.json +141 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-progressive-discounts.json +54 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wordpress-seo.json +158 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpc-composite-products.json +97 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpforms-lite.json +86 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/capture-store-settings.js +90 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge-validate.js +51 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge.js +103 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +1668 -33
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-plugin-inventory.js +273 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/secret-value-patterns.v1.json +11 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/sub-parts.json +13 -0
- package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +446 -32
- package/skills/wix-replatform/resources/rp-target-wix/app-page-routing-capabilities.json +36 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +38 -2
- package/skills/wix-replatform/resources/rp-target-wix/domains/SERVICE-PLUGINS.md +322 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/docs-survey.json +70 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +19 -9
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +42 -8
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +54 -8
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +66 -10
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +38 -7
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/docs-survey.json +332 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/domain.json +75 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/booking.json +181 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service-add-on.json +130 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service.json +180 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/docs-survey.json +161 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +15 -4
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +39 -7
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +47 -8
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +7 -1
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/docs-survey.json +204 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +17 -4
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +159 -15
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +80 -12
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +48 -8
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/docs-survey.json +306 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +30 -7
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/delivery-profile.json +171 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/discount-rule.json +182 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-applied-discount.json +129 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-invoice-document.json +192 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item-options.json +123 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +60 -12
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-merchant-note.json +112 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-payment.json +191 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +140 -13
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +383 -18
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/pickup-location.json +122 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/shipping-option.json +98 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/wishlist-item.json +86 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/docs-survey.json +128 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/domain.json +38 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/category.json +73 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/event.json +107 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/registration-form.json +89 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/rsvp.json +118 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/ticket-definition.json +93 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/docs-survey.json +86 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/domain.json +56 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form-submission.json +121 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form.json +134 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/docs-survey.json +41 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/domain.json +50 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card-product.json +87 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card.json +130 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +914 -24
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/docs-survey.json +41 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +14 -4
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +45 -8
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +41 -7
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/docs-survey.json +119 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +13 -3
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +41 -7
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/custom-field.json +82 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member-about.json +77 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +165 -13
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/docs-survey.json +41 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/domain.json +46 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan-order.json +137 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan.json +133 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/docs-survey.json +7 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/domain.json +36 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/amazon-shop-connect.json +71 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/ebay-shop-connect.json +67 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/google-youtube-connect.json +72 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/meta-catalog-connect.json +74 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/pinterest-catalog-connect.json +69 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/tiktok-catalog-connect.json +61 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +28 -1
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/docs-survey.json +109 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/domain.json +73 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/item-seo-tags.json +148 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/seo-pattern.json +91 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/site-seo-tags.json +79 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/site/docs-survey.json +32 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/site/domain.json +49 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/site/entities/business-contact.json +103 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/docs-survey.json +203 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +27 -10
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/back-in-stock-request.json +119 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/brand.json +111 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +52 -8
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +214 -10
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +93 -13
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-customization.json +111 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +52 -10
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +145 -14
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/promotion.json +84 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/review.json +178 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/subscription-product.json +87 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/docs-survey.json +106 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/domain.json +64 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/manual-tax-mapping.json +119 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-group.json +94 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-region.json +113 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-settings.json +65 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-author-attribution.js +165 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-client.js +60 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-duplicates.js +201 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-ledger.js +306 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-report.js +229 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-stage.js +55 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build.js +715 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-media.js +567 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/bulk-results.js +73 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/contact-extended-fields.js +194 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/contact-import.js +133 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +13 -4
- package/skills/wix-replatform/resources/rp-target-wix/lib/coupon-redemption-drift.js +491 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/customer-member.js +104 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/data-extension-schema.js +673 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +453 -2
- package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-balance-reconciliation.js +119 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-build.js +566 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-redemption-build.js +249 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-applied-discount-build.js +63 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-buyer.js +187 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-channel-build.js +64 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-history.js +1027 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-invoice-contract.js +90 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-payment-mapping.js +420 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/pricing-plan-definition.js +177 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/product-import.js +151 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/product-seo.js +723 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/seo-redirects.js +353 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-build.js +1437 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-option-stage.js +354 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/store-config-build.js +134 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-category-attach.js +221 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-inventory.js +248 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +86 -3
- package/skills/wix-replatform/resources/rp-target-wix/lib/tax-build.js +231 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/url-mapper-transport.js +28 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/value-utils.js +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/variant-correlation.js +34 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +166 -15
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +19 -2
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +4079 -257
- package/skills/wix-replatform/resources/rp-target-wix/lib/write-verification.js +3 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/docs-survey-sync.js +150 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +40 -2
- package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +6 -1
- package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +148 -8
- package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +83 -15
- package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +1119 -215
- package/skills/wix-replatform/resources/rp-telemetry/lib/transcript-digest.js +318 -0
- package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +2 -2
- package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +106 -4
- package/skills/wix-replatform/resources/rp-website-continuation/SKILL.md +94 -0
- package/skills/wix-replatform/scripts/census-reconciliation.js +25 -0
- package/skills/wix-replatform/scripts/cms-fallback.js +89 -0
- package/skills/wix-replatform/scripts/discovered-census.js +26 -0
- package/skills/wix-replatform/scripts/discovered-set-status.js +33 -0
- package/skills/wix-replatform/scripts/entity-qa-attestation.js +130 -0
- package/skills/wix-replatform/scripts/execution-health-check.js +415 -0
- package/skills/wix-replatform/scripts/execution-manifest-validate.js +1 -1
- package/skills/wix-replatform/scripts/execution-state-prepare.js +1 -1
- package/skills/wix-replatform/scripts/expectations-ledger.js +44 -0
- package/skills/wix-replatform/scripts/import-contract-verify.js +183 -0
- package/skills/wix-replatform/scripts/internal-link-audit.js +90 -0
- package/skills/wix-replatform/scripts/item-seo-tags-verify.js +205 -0
- package/skills/wix-replatform/scripts/migration-completion.js +76 -0
- package/skills/wix-replatform/scripts/orchestration-preflight.js +21 -2
- package/skills/wix-replatform/scripts/orchestration-route.js +32 -1
- package/skills/wix-replatform/scripts/orchestration-state.js +146 -10
- package/skills/wix-replatform/scripts/read-back-gate.js +175 -0
- package/skills/wix-replatform/scripts/replatform-console.js +146 -0
- package/skills/wix-replatform/scripts/replatform.js +436 -0
- package/skills/wix-replatform/scripts/source-secrets.js +77 -0
- package/skills/wix-replatform/scripts/url-redirects.js +286 -0
- package/skills/wix-replatform/scripts/user-action.js +58 -0
- package/skills/wix-replatform/scripts/website-handoff-generate.js +64 -0
- package/skills/wix-replatform/scripts/wix-dashboard-url.js +54 -0
- package/skills/wix-replatform/scripts/wix-destination.js +143 -0
- package/skills/wix-replatform/scripts/wix-headless-scaffold.js +762 -0
- package/skills/wix-vibe-headless/SKILL.md +7 -3
- package/skills/wix-vibe-headless/install/deploy.cjs +61 -34
- package/skills/wix-vibe-headless/platforms/base44.md +1 -0
- package/skills/wix-vibe-headless/references/donations/INSTRUCTIONS.md +88 -0
- package/skills/wix-vibe-headless/references/donations/app/rest/wix-donations.js +112 -0
- package/skills/wix-vibe-headless/references/donations/seed/SEED.md +11 -0
- package/skills/wix-vibe-headless/references/donations/tests/donations.test.mjs +258 -0
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +5 -2
- package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +4 -1
- package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-ordering.js +67 -3
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-client.js +4 -3
- package/skills/wix-replatform/resources/rp-target-wix/SPEC-product-reviews-and-blog-authorship.md +0 -330
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Complete, resumable extraction of one structure-bridge-plugin entity.
|
|
4
|
+
//
|
|
5
|
+
// rp-import-codegen vendors this file into a generated project's `src/lib/` next to
|
|
6
|
+
// wix-wp-plugin-v2-client.js and calls extractStructureBridgeEntity() once per entity. The read
|
|
7
|
+
// contract in rp-import-codegen's "`structure-bridge-plugin` entities" section is implemented
|
|
8
|
+
// here once, deterministically, instead of being re-authored in every generated reader.
|
|
9
|
+
//
|
|
10
|
+
// Two variants, chosen by the artifacts discovery persisted -- never by the caller's guess:
|
|
11
|
+
// - single-request: `…--<table>.structure-request.json`, paged to exhaustion;
|
|
12
|
+
// - EAV: `…--<table>.eav-read-plan.json`, every batch run verbatim.
|
|
13
|
+
//
|
|
14
|
+
// Invariants both variants share:
|
|
15
|
+
// - rows go to `<entity>.ndjson.tmp`, one page at a time, with a checkpoint after every append;
|
|
16
|
+
// - reconciliation runs before promotion, and a failure leaves `.tmp` + checkpoint in place;
|
|
17
|
+
// - the governing artifact is re-hashed immediately before promotion, even on a run that never
|
|
18
|
+
// resumed, so a request/plan replaced mid-read blocks promotion;
|
|
19
|
+
// - the file is promoted first and the manifest written second;
|
|
20
|
+
// - an existing final file is reused only when its manifest still matches what is on disk.
|
|
21
|
+
//
|
|
22
|
+
// Complete-identity rule: an EAV plan with a non-empty
|
|
23
|
+
// `droppedIdentityColumns` is never promoted, and this reader never sets
|
|
24
|
+
// `acknowledgedDroppedColumns` -- not even when the plan on disk carries it.
|
|
25
|
+
//
|
|
26
|
+
// Every outcome is returned, never thrown, so one table's failure does not abort the others.
|
|
27
|
+
// `status` is `promoted`, `reused` or `deferred`; a deferred result names a `code` and `reasons`.
|
|
28
|
+
|
|
29
|
+
const fs = require('node:fs');
|
|
30
|
+
const fsp = require('node:fs/promises');
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
const { createHash } = require('node:crypto');
|
|
33
|
+
const client = require('./wix-wp-plugin-v2-client.js');
|
|
34
|
+
|
|
35
|
+
const PAGE_LIMIT = client.MAX_PAGE_LIMIT;
|
|
36
|
+
const SIDECAR_PREFIX = 'wix-wp-plugin-v2';
|
|
37
|
+
|
|
38
|
+
function slugify(value) {
|
|
39
|
+
return String(value).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'entity';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Artifact paths for one entity. Mirrors wp-discovery.js's sidecar naming. */
|
|
43
|
+
function structureBridgePaths({ dataDir, entity, table }) {
|
|
44
|
+
if (!dataDir || !entity || !table) throw new Error('structureBridgePaths needs dataDir, entity and table');
|
|
45
|
+
if (!/^[A-Za-z0-9_]+$/.test(table)) throw new Error('bridge table must be a bare identifier');
|
|
46
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(entity) || entity === '.' || entity === '..') throw new Error('invalid bridge entity');
|
|
47
|
+
const discoveryDir = path.join(dataDir, 'wp-discovery');
|
|
48
|
+
const extractDir = path.join(dataDir, 'source-extract');
|
|
49
|
+
const requestFile = `${SIDECAR_PREFIX}--${slugify(table)}.structure-request.json`;
|
|
50
|
+
const planFile = `${SIDECAR_PREFIX}--${slugify(table)}.eav-read-plan.json`;
|
|
51
|
+
const finalPath = path.join(extractDir, `${entity}.ndjson`);
|
|
52
|
+
return {
|
|
53
|
+
discoveryDir,
|
|
54
|
+
extractDir,
|
|
55
|
+
requestFile,
|
|
56
|
+
requestPath: path.join(discoveryDir, requestFile),
|
|
57
|
+
planFile,
|
|
58
|
+
planPath: path.join(discoveryDir, planFile),
|
|
59
|
+
finalPath,
|
|
60
|
+
tmpPath: `${finalPath}.tmp`,
|
|
61
|
+
checkpointPath: `${finalPath}.tmp.cursor.json`,
|
|
62
|
+
manifestPath: path.join(extractDir, `${entity}.manifest.json`),
|
|
63
|
+
deferredPath: path.join(extractDir, `${entity}.deferred.json`),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Canonical (key-sorted) hash of an approved structureRequest, so formatting cannot change it. */
|
|
68
|
+
function structureRequestHash(request) {
|
|
69
|
+
const canonical = (value) => {
|
|
70
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
71
|
+
if (value && typeof value === 'object') {
|
|
72
|
+
return Object.keys(value).sort().reduce((out, key) => { out[key] = canonical(value[key]); return out; }, {});
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
};
|
|
76
|
+
return createHash('sha256').update(JSON.stringify(canonical(request))).digest('hex').slice(0, 16);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function readJsonIfExists(filePath) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(await fsp.readFile(filePath, 'utf8'));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error.code === 'ENOENT') return null;
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function exists(filePath) {
|
|
89
|
+
try { await fsp.access(filePath); return true; } catch { return false; }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function writeJsonAtomic(filePath, value) {
|
|
93
|
+
const staging = `${filePath}.writing`;
|
|
94
|
+
await fsp.writeFile(staging, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
95
|
+
await fsp.rename(staging, filePath);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function removeIfExists(filePath) {
|
|
99
|
+
await fsp.rm(filePath, { force: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Line count and sha256 of a file, streamed so a large extraction is never held in memory. */
|
|
103
|
+
function fileStats(filePath) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
if (!fs.existsSync(filePath)) { resolve({ lines: 0, digest: null, exists: false }); return; }
|
|
106
|
+
const hash = createHash('sha256');
|
|
107
|
+
let lines = 0;
|
|
108
|
+
fs.createReadStream(filePath)
|
|
109
|
+
.on('data', (chunk) => {
|
|
110
|
+
hash.update(chunk);
|
|
111
|
+
for (let i = 0; i < chunk.length; i += 1) if (chunk[i] === 10) lines += 1;
|
|
112
|
+
})
|
|
113
|
+
.on('error', reject)
|
|
114
|
+
.on('end', () => resolve({ lines, digest: hash.digest('hex'), exists: true }));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function appendRows(tmpPath, rows) {
|
|
119
|
+
if (rows.length === 0) return;
|
|
120
|
+
await fsp.appendFile(tmpPath, rows.map((row) => `${JSON.stringify(row)}\n`).join(''), 'utf8');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function discardInProgress(paths) {
|
|
124
|
+
await removeIfExists(paths.tmpPath);
|
|
125
|
+
await removeIfExists(paths.checkpointPath);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isPlainObject(value) {
|
|
129
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const isCount = (value) => Number.isInteger(value) && value >= 0;
|
|
133
|
+
const isCursor = (value) => typeof value === 'string' && value !== '';
|
|
134
|
+
|
|
135
|
+
/** A checkpoint that is not even JSON is as unusable as a malformed one: restart, never throw. */
|
|
136
|
+
async function readCheckpoint(paths) {
|
|
137
|
+
try {
|
|
138
|
+
return await readJsonIfExists(paths.checkpointPath);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (error instanceof SyntaxError) return { unparseable: true };
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Schema and cross-field invariants of a persisted single-request checkpoint. A checkpoint is
|
|
147
|
+
* only ever written after a page, so `snapshotTotal` is always the first page's frozen total.
|
|
148
|
+
* Without this, a checkpoint missing `hasNext` skipped the page loop and could promote a
|
|
149
|
+
* partial file whose `rowsWritten` happened to equal `snapshotTotal`.
|
|
150
|
+
*/
|
|
151
|
+
function singleRequestCheckpointProblem(checkpoint) {
|
|
152
|
+
if (typeof checkpoint.hasNext !== 'boolean') return 'hasNext must be a boolean';
|
|
153
|
+
if (!isCount(checkpoint.rowsWritten)) return 'rowsWritten must be a non-negative integer';
|
|
154
|
+
if (!isCount(checkpoint.snapshotTotal)) return 'snapshotTotal must be a non-negative integer';
|
|
155
|
+
if (checkpoint.hasNext && !isCursor(checkpoint.cursor)) return 'hasNext is true but cursor is not a non-empty string';
|
|
156
|
+
if (!checkpoint.hasNext && checkpoint.cursor !== null) return 'hasNext is false but cursor is not null';
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function batchProblem(batch, label) {
|
|
161
|
+
if (!isPlainObject(batch)) return `${label} must be an object`;
|
|
162
|
+
if (!Number.isInteger(batch.batchIndex)) return `${label}.batchIndex must be an integer`;
|
|
163
|
+
if (!isCount(batch.rowsRead)) return `${label}.rowsRead must be a non-negative integer`;
|
|
164
|
+
if (!isCount(batch.snapshotTotal)) return `${label}.snapshotTotal must be a non-negative integer`;
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Schema and cross-field invariants of a persisted EAV checkpoint, against the plan it claims.
|
|
170
|
+
* Batches run in plan order, so `completedBatches` must be exactly the plan's first N batches
|
|
171
|
+
* and `activeBatch`, when present, the next one, mid-page; the batch row counts must add up to
|
|
172
|
+
* `rowsWritten`. Anything else is a checkpoint that would resume into the wrong batch layout.
|
|
173
|
+
*/
|
|
174
|
+
function eavCheckpointProblem(checkpoint, plan, planFile) {
|
|
175
|
+
if (checkpoint.planFile !== planFile) return `planFile must be ${planFile}`;
|
|
176
|
+
if (!isCount(checkpoint.rowsWritten)) return 'rowsWritten must be a non-negative integer';
|
|
177
|
+
if (!Array.isArray(checkpoint.completedBatches)) return 'completedBatches must be an array';
|
|
178
|
+
if (checkpoint.activeBatch !== null && !isPlainObject(checkpoint.activeBatch)) return 'activeBatch must be null or an object';
|
|
179
|
+
const order = client.eavReadPlanBatches(plan).map((b) => b.batchIndex);
|
|
180
|
+
let rows = 0;
|
|
181
|
+
for (const [i, batch] of checkpoint.completedBatches.entries()) {
|
|
182
|
+
const problem = batchProblem(batch, `completedBatches[${i}]`);
|
|
183
|
+
if (problem) return problem;
|
|
184
|
+
if (batch.batchIndex !== order[i]) return `completedBatches[${i}] is batch ${batch.batchIndex}, but the plan runs batch ${order[i]} there`;
|
|
185
|
+
rows += batch.rowsRead;
|
|
186
|
+
}
|
|
187
|
+
const active = checkpoint.activeBatch;
|
|
188
|
+
if (active) {
|
|
189
|
+
const problem = batchProblem(active, 'activeBatch');
|
|
190
|
+
if (problem) return problem;
|
|
191
|
+
const expected = order[checkpoint.completedBatches.length];
|
|
192
|
+
if (active.batchIndex !== expected) return `activeBatch is batch ${active.batchIndex}, but the next batch in the plan is ${expected === undefined ? 'none' : expected}`;
|
|
193
|
+
if (active.hasNext !== true) return 'activeBatch.hasNext must be true (a finished batch belongs in completedBatches)';
|
|
194
|
+
if (!isCursor(active.cursor)) return 'activeBatch.cursor must be a non-empty string';
|
|
195
|
+
rows += active.rowsRead;
|
|
196
|
+
}
|
|
197
|
+
if (rows !== checkpoint.rowsWritten) return `the batches account for ${rows} row(s) but rowsWritten is ${checkpoint.rowsWritten}`;
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function deferred(variant, code, reasons, extra = {}) {
|
|
202
|
+
return { status: 'deferred', variant, code, reasons: Array.isArray(reasons) ? reasons : [reasons], ...extra };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function makeRedactionTally() {
|
|
206
|
+
const columns = new Set();
|
|
207
|
+
let count = 0;
|
|
208
|
+
return {
|
|
209
|
+
add(meta) {
|
|
210
|
+
if (!meta || !meta.redactedValueCount) return;
|
|
211
|
+
count += meta.redactedValueCount;
|
|
212
|
+
for (const column of meta.redactedColumns || []) columns.add(column);
|
|
213
|
+
},
|
|
214
|
+
report() { return { redactedValueCount: count, redactedColumns: [...columns].sort() }; },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** One page, through the vendored client -- never a hand-built request or response check. */
|
|
219
|
+
async function fetchPage(transport, structureRequest, cursor) {
|
|
220
|
+
return client.queryStructure({
|
|
221
|
+
httpClient: transport.httpClient,
|
|
222
|
+
siteBaseUrl: transport.siteBaseUrl,
|
|
223
|
+
auth: transport.auth,
|
|
224
|
+
structureRequest,
|
|
225
|
+
cursor,
|
|
226
|
+
limit: PAGE_LIMIT,
|
|
227
|
+
onSourceRead: transport.onSourceRead,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function stalledCursorReason(table, previousCursor, next) {
|
|
232
|
+
if (next === null || next === undefined || next === '') {
|
|
233
|
+
return `table ${table}: hasNext is true but no next cursor was returned -- refusing to loop or truncate`;
|
|
234
|
+
}
|
|
235
|
+
if (next === previousCursor) {
|
|
236
|
+
return `table ${table}: hasNext is true but the next cursor is unchanged from the one just used -- refusing to loop or truncate`;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// --- single-request variant --------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
async function singleRequestReuse(paths, requestHash) {
|
|
244
|
+
if (!(await exists(paths.finalPath))) return { reusable: false, reasons: ['no final file'] };
|
|
245
|
+
const manifest = await readJsonIfExists(paths.manifestPath);
|
|
246
|
+
if (!manifest || manifest.kind !== 'structure-request') return { reusable: false, reasons: ['no structure-request manifest -- the existing file is stale'] };
|
|
247
|
+
const reasons = [];
|
|
248
|
+
if (manifest.structureRequestHash !== requestHash) reasons.push(`the approved query changed since this file was written (${manifest.structureRequestHash} -> ${requestHash})`);
|
|
249
|
+
if (manifest.sidecarFile !== paths.requestFile) reasons.push(`manifest names ${manifest.sidecarFile} but this entity's request is ${paths.requestFile}`);
|
|
250
|
+
const stats = await fileStats(paths.finalPath);
|
|
251
|
+
if (manifest.ndjsonDigest !== stats.digest) reasons.push('the extracted file does not match the digest its manifest recorded');
|
|
252
|
+
if (manifest.rowsWritten !== stats.lines) reasons.push(`the extracted file holds ${stats.lines} row(s) but its manifest recorded ${manifest.rowsWritten}`);
|
|
253
|
+
if (manifest.rowsWritten !== manifest.snapshotTotal) reasons.push('the manifest records an unreconciled extraction');
|
|
254
|
+
return { reusable: reasons.length === 0, reasons, manifest };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function extractSingleRequest({ entity, table, paths, transport, now, redactions }) {
|
|
258
|
+
const variant = 'single-request';
|
|
259
|
+
const structureRequest = await readJsonIfExists(paths.requestPath);
|
|
260
|
+
if (!structureRequest) {
|
|
261
|
+
return deferred(variant, 'missing-artifact', `table ${table}: ${paths.requestFile} does not exist -- discovery has not produced an approved query for this entity, so it cannot be extracted yet`);
|
|
262
|
+
}
|
|
263
|
+
// `table` is always the fulfillment's own, applied last, so a sidecar can never redirect the read.
|
|
264
|
+
const request = { ...structureRequest, table };
|
|
265
|
+
const requestHash = structureRequestHash(structureRequest);
|
|
266
|
+
|
|
267
|
+
const reuse = await singleRequestReuse(paths, requestHash);
|
|
268
|
+
if (reuse.reusable && !(await exists(paths.tmpPath))) {
|
|
269
|
+
return { status: 'reused', variant, entity, rowsWritten: reuse.manifest.rowsWritten, manifestPath: paths.manifestPath, finalPath: paths.finalPath, reasons: [] };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
await fsp.mkdir(paths.extractDir, { recursive: true });
|
|
273
|
+
let checkpoint = await readCheckpoint(paths);
|
|
274
|
+
const resumeNotes = [];
|
|
275
|
+
if (checkpoint && checkpoint.unparseable === true) {
|
|
276
|
+
resumeNotes.push('the checkpoint is not valid JSON -- discarding its rows and restarting');
|
|
277
|
+
await discardInProgress(paths);
|
|
278
|
+
checkpoint = null;
|
|
279
|
+
}
|
|
280
|
+
if (checkpoint && (!isPlainObject(checkpoint) || checkpoint.planHash !== undefined || typeof checkpoint.structureRequestHash !== 'string')) {
|
|
281
|
+
resumeNotes.push('checkpoint belongs to a different read variant -- restarting');
|
|
282
|
+
await discardInProgress(paths);
|
|
283
|
+
checkpoint = null;
|
|
284
|
+
}
|
|
285
|
+
if (checkpoint && checkpoint.structureRequestHash !== requestHash) {
|
|
286
|
+
resumeNotes.push('the approved query was replaced since this read started -- discarding its rows and restarting under the current request');
|
|
287
|
+
await discardInProgress(paths);
|
|
288
|
+
checkpoint = null;
|
|
289
|
+
}
|
|
290
|
+
const malformed = checkpoint ? singleRequestCheckpointProblem(checkpoint) : null;
|
|
291
|
+
if (malformed) {
|
|
292
|
+
resumeNotes.push(`the checkpoint is malformed (${malformed}) -- discarding its rows and restarting`);
|
|
293
|
+
await discardInProgress(paths);
|
|
294
|
+
checkpoint = null;
|
|
295
|
+
}
|
|
296
|
+
if (checkpoint) {
|
|
297
|
+
const { lines } = await fileStats(paths.tmpPath);
|
|
298
|
+
if (lines !== checkpoint.rowsWritten) {
|
|
299
|
+
resumeNotes.push(`the in-progress file holds ${lines} row(s) but its checkpoint recorded ${checkpoint.rowsWritten} -- restarting`);
|
|
300
|
+
await discardInProgress(paths);
|
|
301
|
+
checkpoint = null;
|
|
302
|
+
}
|
|
303
|
+
} else if (await exists(paths.tmpPath)) {
|
|
304
|
+
await removeIfExists(paths.tmpPath); // a .tmp with no checkpoint cannot be resumed safely
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let state = checkpoint || { cursor: null, hasNext: true, rowsWritten: 0, snapshotTotal: null, structureRequestHash: requestHash };
|
|
308
|
+
let queries = 0;
|
|
309
|
+
if (!checkpoint) await fsp.writeFile(paths.tmpPath, '', 'utf8');
|
|
310
|
+
|
|
311
|
+
while (state.hasNext) {
|
|
312
|
+
let page;
|
|
313
|
+
try {
|
|
314
|
+
// eslint-disable-next-line no-await-in-loop
|
|
315
|
+
page = await fetchPage(transport, request, state.cursor);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
return deferred(variant, 'page-error', `table ${table}: ${error.message}`, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
318
|
+
}
|
|
319
|
+
queries += 1;
|
|
320
|
+
redactions.add(page.redactionMetadata);
|
|
321
|
+
const { hasNext, total, cursors } = page.pagingMetadata;
|
|
322
|
+
const stalled = hasNext ? stalledCursorReason(table, state.cursor, cursors.next) : null;
|
|
323
|
+
if (stalled) {
|
|
324
|
+
// Nothing from this page is appended: the checkpoint still describes the file exactly.
|
|
325
|
+
return deferred(variant, 'stalled-cursor', stalled, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
326
|
+
}
|
|
327
|
+
// eslint-disable-next-line no-await-in-loop
|
|
328
|
+
await appendRows(paths.tmpPath, page.rows);
|
|
329
|
+
state = {
|
|
330
|
+
cursor: hasNext ? cursors.next : null,
|
|
331
|
+
hasNext,
|
|
332
|
+
rowsWritten: state.rowsWritten + page.rows.length,
|
|
333
|
+
snapshotTotal: state.snapshotTotal === null ? total : state.snapshotTotal,
|
|
334
|
+
structureRequestHash: requestHash,
|
|
335
|
+
};
|
|
336
|
+
// eslint-disable-next-line no-await-in-loop
|
|
337
|
+
await writeJsonAtomic(paths.checkpointPath, state);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (state.rowsWritten !== state.snapshotTotal) {
|
|
341
|
+
return deferred(variant, 'count-mismatch', `table ${table}: read ${state.rowsWritten} row(s) but the first page's frozen snapshot total was ${state.snapshotTotal} -- not promoting`, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Final re-hash: a sidecar replaced while this read ran straight through is caught only here.
|
|
345
|
+
const current = await readJsonIfExists(paths.requestPath);
|
|
346
|
+
const currentHash = current ? structureRequestHash(current) : null;
|
|
347
|
+
if (currentHash !== state.structureRequestHash) {
|
|
348
|
+
return deferred(variant, 'request-replaced', `table ${table}: the approved query changed during this read (${state.structureRequestHash} -> ${currentHash || 'missing'}) -- not promoting; the next run restarts under the current request`, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Promote first, manifest second: a crash between them leaves a file the reuse check refuses.
|
|
352
|
+
await fsp.rename(paths.tmpPath, paths.finalPath);
|
|
353
|
+
await removeIfExists(paths.checkpointPath);
|
|
354
|
+
const stats = await fileStats(paths.finalPath);
|
|
355
|
+
const manifest = {
|
|
356
|
+
kind: 'structure-request',
|
|
357
|
+
sidecarFile: paths.requestFile,
|
|
358
|
+
structureRequestHash: state.structureRequestHash,
|
|
359
|
+
rowsWritten: state.rowsWritten,
|
|
360
|
+
snapshotTotal: state.snapshotTotal,
|
|
361
|
+
ndjsonDigest: stats.digest,
|
|
362
|
+
extractedAt: new Date(now()).toISOString(),
|
|
363
|
+
};
|
|
364
|
+
await writeJsonAtomic(paths.manifestPath, manifest);
|
|
365
|
+
return { status: 'promoted', variant, entity, rowsWritten: state.rowsWritten, queries, manifestPath: paths.manifestPath, finalPath: paths.finalPath, reasons: [], resumeNotes };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// --- EAV variant -------------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
const BEST_EFFORT_MARKER = 'the extraction is best-effort';
|
|
371
|
+
|
|
372
|
+
/** Split reconcileEavReadPlan()'s problems into blocking ones and the best-effort qualifier. */
|
|
373
|
+
function splitBestEffort(plan, problems) {
|
|
374
|
+
const bestEffort = plan.orderByIsUnique === false ? problems.filter((p) => p.includes(BEST_EFFORT_MARKER)) : [];
|
|
375
|
+
return { blocking: problems.filter((p) => !bestEffort.includes(p)), bestEffort };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Plan-level conditions no amount of reading can fix. Checked before any request is sent. */
|
|
379
|
+
function eavPlanBlockers(plan, table) {
|
|
380
|
+
const blockers = [];
|
|
381
|
+
if (Array.isArray(plan.droppedIdentityColumns) && plan.droppedIdentityColumns.length > 0) {
|
|
382
|
+
blockers.push({
|
|
383
|
+
code: 'dropped-identity-columns',
|
|
384
|
+
reason: `table ${table}: ${plan.droppedIdentityColumns.length} identity column(s) do not fit the 50-column select cap (${plan.droppedIdentityColumns.join(', ')}). `
|
|
385
|
+
+ 'This fulfillment kind requires complete identity: narrow the mapping or redesign the read plan, then regenerate it. '
|
|
386
|
+
+ 'An acknowledgedDroppedColumns flag on the plan does not change this.',
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (plan.keysTruncated) {
|
|
390
|
+
blockers.push({ code: 'keys-truncated', reason: `table ${table}: key discovery hit its cap, so the key list is incomplete -- this extraction cannot be treated as whole-table` });
|
|
391
|
+
}
|
|
392
|
+
return blockers;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function extractEav({ entity, table, paths, transport, now, redactions }) {
|
|
396
|
+
const variant = 'eav';
|
|
397
|
+
const plan = await readJsonIfExists(paths.planPath);
|
|
398
|
+
const integrity = client.verifyEavReadPlanIntegrity(plan);
|
|
399
|
+
if (integrity) return deferred(variant, 'plan-integrity', integrity, { entity });
|
|
400
|
+
const planHash = client.eavReadPlanHash(plan);
|
|
401
|
+
|
|
402
|
+
const blockers = eavPlanBlockers(plan, table);
|
|
403
|
+
if (blockers.length > 0) {
|
|
404
|
+
// In-progress files are left exactly as they are.
|
|
405
|
+
return deferred(variant, blockers[0].code, blockers.map((b) => b.reason), { entity });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (await exists(paths.finalPath) && !(await exists(paths.tmpPath))) {
|
|
409
|
+
const manifest = await readJsonIfExists(paths.manifestPath);
|
|
410
|
+
const stats = await fileStats(paths.finalPath);
|
|
411
|
+
const reasons = client.eavExtractionIsReusable(plan, manifest, { planFile: paths.planFile, ndjsonDigest: stats.digest, ndjsonRowCount: stats.lines });
|
|
412
|
+
const blocking = reasons.filter((r) => !(plan.orderByIsUnique === false && r.includes(BEST_EFFORT_MARKER)));
|
|
413
|
+
if (blocking.length === 0) {
|
|
414
|
+
return { status: 'reused', variant, entity, rowsWritten: manifest.rowsWritten, bestEffort: plan.orderByIsUnique === false, manifestPath: paths.manifestPath, finalPath: paths.finalPath, reasons: [] };
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
await fsp.mkdir(paths.extractDir, { recursive: true });
|
|
419
|
+
let checkpoint = await readCheckpoint(paths);
|
|
420
|
+
const resumeNotes = [];
|
|
421
|
+
if (checkpoint && checkpoint.unparseable === true) {
|
|
422
|
+
resumeNotes.push('the checkpoint is not valid JSON -- discarding its rows and restarting');
|
|
423
|
+
await discardInProgress(paths);
|
|
424
|
+
checkpoint = null;
|
|
425
|
+
}
|
|
426
|
+
if (checkpoint && (!isPlainObject(checkpoint) || typeof checkpoint.planHash !== 'string')) {
|
|
427
|
+
resumeNotes.push('checkpoint belongs to a different read variant -- restarting');
|
|
428
|
+
await discardInProgress(paths);
|
|
429
|
+
checkpoint = null;
|
|
430
|
+
}
|
|
431
|
+
if (checkpoint && checkpoint.planHash !== planHash) {
|
|
432
|
+
resumeNotes.push('the read plan was replaced since this read started -- discarding its rows and restarting under the current plan');
|
|
433
|
+
await discardInProgress(paths);
|
|
434
|
+
checkpoint = null;
|
|
435
|
+
}
|
|
436
|
+
const malformed = checkpoint ? eavCheckpointProblem(checkpoint, plan, paths.planFile) : null;
|
|
437
|
+
if (malformed) {
|
|
438
|
+
resumeNotes.push(`the checkpoint is malformed (${malformed}) -- discarding its rows and restarting`);
|
|
439
|
+
await discardInProgress(paths);
|
|
440
|
+
checkpoint = null;
|
|
441
|
+
}
|
|
442
|
+
if (checkpoint) {
|
|
443
|
+
const { lines } = await fileStats(paths.tmpPath);
|
|
444
|
+
if (lines !== checkpoint.rowsWritten) {
|
|
445
|
+
resumeNotes.push(`the in-progress file holds ${lines} row(s) but its checkpoint recorded ${checkpoint.rowsWritten} -- restarting`);
|
|
446
|
+
await discardInProgress(paths);
|
|
447
|
+
checkpoint = null;
|
|
448
|
+
}
|
|
449
|
+
} else if (await exists(paths.tmpPath)) {
|
|
450
|
+
await removeIfExists(paths.tmpPath);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
let state = checkpoint || { planFile: paths.planFile, planHash, activeBatch: null, completedBatches: [], rowsWritten: 0 };
|
|
454
|
+
if (!checkpoint) await fsp.writeFile(paths.tmpPath, '', 'utf8');
|
|
455
|
+
const done = new Set(state.completedBatches.map((b) => b.batchIndex));
|
|
456
|
+
let queries = 0;
|
|
457
|
+
|
|
458
|
+
for (const batch of client.eavReadPlanBatches(plan)) {
|
|
459
|
+
if (done.has(batch.batchIndex)) continue;
|
|
460
|
+
let active = state.activeBatch && state.activeBatch.batchIndex === batch.batchIndex
|
|
461
|
+
? state.activeBatch
|
|
462
|
+
: { batchIndex: batch.batchIndex, cursor: null, hasNext: true, rowsRead: 0, snapshotTotal: null };
|
|
463
|
+
const request = { ...batch.structureRequest, table };
|
|
464
|
+
while (active.hasNext) {
|
|
465
|
+
let page;
|
|
466
|
+
try {
|
|
467
|
+
// eslint-disable-next-line no-await-in-loop
|
|
468
|
+
page = await fetchPage(transport, request, active.cursor);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
return deferred(variant, 'page-error', `table ${table} batch ${batch.batchIndex}: ${error.message}`, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
471
|
+
}
|
|
472
|
+
queries += 1;
|
|
473
|
+
redactions.add(page.redactionMetadata);
|
|
474
|
+
const { hasNext, total, cursors } = page.pagingMetadata;
|
|
475
|
+
const stalled = hasNext ? stalledCursorReason(`${table} batch ${batch.batchIndex}`, active.cursor, cursors.next) : null;
|
|
476
|
+
if (stalled) return deferred(variant, 'stalled-cursor', stalled, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
477
|
+
// eslint-disable-next-line no-await-in-loop
|
|
478
|
+
await appendRows(paths.tmpPath, page.rows);
|
|
479
|
+
active = {
|
|
480
|
+
batchIndex: batch.batchIndex,
|
|
481
|
+
cursor: hasNext ? cursors.next : null,
|
|
482
|
+
hasNext,
|
|
483
|
+
rowsRead: active.rowsRead + page.rows.length,
|
|
484
|
+
snapshotTotal: active.snapshotTotal === null ? total : active.snapshotTotal,
|
|
485
|
+
};
|
|
486
|
+
const rowsWritten = state.rowsWritten + page.rows.length;
|
|
487
|
+
state = hasNext
|
|
488
|
+
? { ...state, activeBatch: active, rowsWritten }
|
|
489
|
+
: {
|
|
490
|
+
...state,
|
|
491
|
+
activeBatch: null,
|
|
492
|
+
completedBatches: [...state.completedBatches, { batchIndex: active.batchIndex, rowsRead: active.rowsRead, snapshotTotal: active.snapshotTotal }],
|
|
493
|
+
rowsWritten,
|
|
494
|
+
};
|
|
495
|
+
// eslint-disable-next-line no-await-in-loop
|
|
496
|
+
await writeJsonAtomic(paths.checkpointPath, state);
|
|
497
|
+
}
|
|
498
|
+
done.add(batch.batchIndex);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const problems = client.reconcileEavReadPlan(plan, { planHash: state.planHash, batches: state.completedBatches, rowsWritten: state.rowsWritten });
|
|
502
|
+
const { blocking, bestEffort } = splitBestEffort(plan, problems);
|
|
503
|
+
if (blocking.length > 0) {
|
|
504
|
+
return deferred(variant, 'reconcile', blocking, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const currentPlan = await readJsonIfExists(paths.planPath);
|
|
508
|
+
const currentHash = currentPlan ? client.eavReadPlanHash(currentPlan) : null;
|
|
509
|
+
if (currentHash !== state.planHash || (currentPlan && client.verifyEavReadPlanIntegrity(currentPlan))) {
|
|
510
|
+
return deferred(variant, 'plan-replaced', `table ${table}: the read plan changed during this read (${state.planHash} -> ${currentHash || 'missing'}) -- not promoting; the next run restarts under the current plan`, { entity, rowsWritten: state.rowsWritten, queries, resumeNotes });
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
await fsp.rename(paths.tmpPath, paths.finalPath);
|
|
514
|
+
await removeIfExists(paths.checkpointPath);
|
|
515
|
+
const stats = await fileStats(paths.finalPath);
|
|
516
|
+
// acknowledgedDroppedColumns is forced false: this reader never promotes under it.
|
|
517
|
+
const manifest = client.eavExtractionManifest({ ...plan, acknowledgedDroppedColumns: false }, {
|
|
518
|
+
planFile: paths.planFile,
|
|
519
|
+
completedBatches: state.completedBatches,
|
|
520
|
+
rowsWritten: state.rowsWritten,
|
|
521
|
+
extractedAt: new Date(now()).toISOString(),
|
|
522
|
+
ndjsonDigest: stats.digest,
|
|
523
|
+
});
|
|
524
|
+
await writeJsonAtomic(paths.manifestPath, manifest);
|
|
525
|
+
return {
|
|
526
|
+
status: 'promoted',
|
|
527
|
+
variant,
|
|
528
|
+
entity,
|
|
529
|
+
rowsWritten: state.rowsWritten,
|
|
530
|
+
queries,
|
|
531
|
+
bestEffort: bestEffort.length > 0,
|
|
532
|
+
reasons: bestEffort,
|
|
533
|
+
manifestPath: paths.manifestPath,
|
|
534
|
+
finalPath: paths.finalPath,
|
|
535
|
+
resumeNotes,
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// --- durable deferred-extraction record ----------------------------------------------------------
|
|
540
|
+
//
|
|
541
|
+
// A deferred result in memory dies with the session, and the person who later completes the
|
|
542
|
+
// cutover is usually a different operator on a different machine. So every deferral is also
|
|
543
|
+
// written to `data/source-extract/<entity>.deferred.json`: what was not extracted, why, what it
|
|
544
|
+
// costs, what is kept for resuming, and the exact next step. Recovery needs the original
|
|
545
|
+
// migration environment (the source site's plugin credential and these in-progress files), so
|
|
546
|
+
// the owner is always the Import Operator and the record carries a copyable prompt for them.
|
|
547
|
+
// A later promotion or reuse marks the same record `resolved`, with the manifest as evidence.
|
|
548
|
+
//
|
|
549
|
+
// deferredExtractionReportInput() turns open records into completion-report `operatorActions`
|
|
550
|
+
// rows, which the migration handoff renders above the counts. That is the integration used until
|
|
551
|
+
// the shared Cutover integration exists; this file names no CMS output yet.
|
|
552
|
+
|
|
553
|
+
const DEFERRED_RECORD_KIND = 'structure-bridge-deferred-extraction';
|
|
554
|
+
const DEFERRED_ACTION_CODE = 'structure-bridge-extraction-deferred';
|
|
555
|
+
const PRODUCER = 'rp-source-wordpress/lib/structure-bridge-reader.js';
|
|
556
|
+
const SOURCE_PREREQUISITES = [
|
|
557
|
+
'the source WordPress site is still online with the Wix WordPress plugin (wix-wp-plugin-v2) active',
|
|
558
|
+
'the plugin credential this migration was configured with (Application Password or WMH2_MIGRATION_KEY) is still valid in the original migration environment',
|
|
559
|
+
];
|
|
560
|
+
const DISCOVERY_PREREQUISITES = [...SOURCE_PREREQUISITES, 'WordPress discovery can be re-run from the original migration project'];
|
|
561
|
+
|
|
562
|
+
/** What resolves each deferral code. `restart` means the rerun must pass `restart: true`. */
|
|
563
|
+
function recoveryFor(code, { entity, table, paths }) {
|
|
564
|
+
const rerun = `re-run the extraction for ${entity}`;
|
|
565
|
+
const byCode = {
|
|
566
|
+
'missing-artifact': { action: `inspect the missing artifact and its discovery producer for ${entity}; regenerate it only if that producer supports this entity, otherwise prepare a validated entity read plan through the generic bridge, then ${rerun}`, prerequisites: DISCOVERY_PREREQUISITES },
|
|
567
|
+
'plan-integrity': { action: `re-run WordPress discovery for table ${table} to regenerate ${paths.planFile} (it was edited after discovery wrote it), then ${rerun}`, prerequisites: DISCOVERY_PREREQUISITES },
|
|
568
|
+
'keys-truncated': { action: `re-run key discovery for table ${table} with a key cap high enough for the whole key list, regenerate ${paths.planFile}, then ${rerun}`, prerequisites: DISCOVERY_PREREQUISITES },
|
|
569
|
+
'dropped-identity-columns': { action: `narrow the mapping for ${entity} (or redesign its read plan) so every identity column fits each batch, regenerate ${paths.planFile}, get the mapping change reviewed, then ${rerun}`, prerequisites: [...DISCOVERY_PREREQUISITES, 'the mapping review is re-opened for this entity'] },
|
|
570
|
+
'page-error': { action: `check that the source site and its plugin credential work, then ${rerun}; it resumes from the saved checkpoint`, prerequisites: SOURCE_PREREQUISITES },
|
|
571
|
+
'stalled-cursor': { action: `check the Wix WordPress plugin version on the source site (it returned an invalid next-page cursor), then ${rerun}; it resumes from the saved checkpoint`, prerequisites: SOURCE_PREREQUISITES },
|
|
572
|
+
'count-mismatch': { action: `the table changed while it was read; ${rerun} with restart: true so it reads the whole table again`, prerequisites: SOURCE_PREREQUISITES, restart: true },
|
|
573
|
+
reconcile: { action: `a batch did not read every row its snapshot promised; ${rerun} with restart: true so it reads the whole table again`, prerequisites: SOURCE_PREREQUISITES, restart: true },
|
|
574
|
+
'request-replaced': { action: `the approved query changed during the read; ${rerun} -- it restarts under the current query`, prerequisites: SOURCE_PREREQUISITES },
|
|
575
|
+
'plan-replaced': { action: `the read plan changed during the read; ${rerun} -- it restarts under the current plan`, prerequisites: SOURCE_PREREQUISITES },
|
|
576
|
+
};
|
|
577
|
+
return { restart: false, ...(byCode[code] || { action: `fix the local cause named in the reason, then ${rerun}; in-progress files are kept`, prerequisites: SOURCE_PREREQUISITES }) };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function projectRelative(dataDir, filePath) {
|
|
581
|
+
return path.relative(path.dirname(path.resolve(dataDir)), path.resolve(filePath)).split(path.sep).join('/');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function buildDeferredRecord({ dataDir, entity, table, paths, result, previous, at }) {
|
|
585
|
+
const rel = (p) => projectRelative(dataDir, p);
|
|
586
|
+
const recovery = recoveryFor(result.code, { entity, table, paths });
|
|
587
|
+
let checkpoint = null;
|
|
588
|
+
try { checkpoint = await readJsonIfExists(paths.checkpointPath); } catch { checkpoint = null; }
|
|
589
|
+
const expectedTotal = checkpoint && isCount(checkpoint.snapshotTotal) ? checkpoint.snapshotTotal : null;
|
|
590
|
+
const [tmpPresent, checkpointPresent] = await Promise.all([exists(paths.tmpPath), exists(paths.checkpointPath)]);
|
|
591
|
+
const reason = result.reasons[0] || result.code;
|
|
592
|
+
const resolutionCheck = `${rel(paths.manifestPath)} exists, a rerun of extractStructureBridgeEntity for ${entity} returns status promoted or reused, and this record then reads status: resolved`;
|
|
593
|
+
const copyPrompt = `In the original migration project for this Wix site, the ${entity} data (WordPress table ${table}) was not extracted, so none of it was imported: ${result.code} -- ${reason} `
|
|
594
|
+
+ `Read ${rel(paths.deferredPath)}, then ${recovery.action}. `
|
|
595
|
+
+ 'Use the plugin credential already configured in this environment; never paste a credential into chat, a report or this prompt. '
|
|
596
|
+
+ `Done when ${resolutionCheck}. Then import ${entity} to Wix and regenerate the completion report and handoff.`;
|
|
597
|
+
return {
|
|
598
|
+
schemaVersion: 1,
|
|
599
|
+
kind: DEFERRED_RECORD_KIND,
|
|
600
|
+
status: 'open',
|
|
601
|
+
producer: PRODUCER,
|
|
602
|
+
entity,
|
|
603
|
+
table,
|
|
604
|
+
variant: result.variant,
|
|
605
|
+
code: result.code,
|
|
606
|
+
reasons: result.reasons,
|
|
607
|
+
firstDeferredAt: previous && previous.status === 'open' && previous.firstDeferredAt ? previous.firstDeferredAt : at,
|
|
608
|
+
lastDeferredAt: at,
|
|
609
|
+
attempts: (previous && previous.status === 'open' && isCount(previous.attempts) ? previous.attempts : 0) + 1,
|
|
610
|
+
affectedRecords: { entity, extractedSoFar: isCount(result.rowsWritten) ? result.rowsWritten : 0, expectedTotal, imported: 0 },
|
|
611
|
+
consequence: `No ${entity} record is imported to Wix until this extraction is promoted.`,
|
|
612
|
+
resumable: {
|
|
613
|
+
tmpPath: rel(paths.tmpPath),
|
|
614
|
+
tmpPresent,
|
|
615
|
+
checkpointPath: rel(paths.checkpointPath),
|
|
616
|
+
checkpointPresent,
|
|
617
|
+
restartRequired: recovery.restart,
|
|
618
|
+
},
|
|
619
|
+
owner: 'Import Operator',
|
|
620
|
+
requiresImportOperator: true,
|
|
621
|
+
action: recovery.action,
|
|
622
|
+
prerequisites: recovery.prerequisites,
|
|
623
|
+
timing: 'before cutover, while the source WordPress site and this migration\'s plugin credential are still available',
|
|
624
|
+
resolutionCheck,
|
|
625
|
+
copyPromptForImportOperator: copyPrompt,
|
|
626
|
+
cutoverIntegration: 'pending: the shared Cutover integration is not defined yet; until it is, this record reaches the Cutover Operator through the completion report\'s operatorActions and the migration handoff',
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** Write, refresh or resolve the entity's durable record. Never fails the extraction itself. */
|
|
631
|
+
async function recordOutcome({ dataDir, entity, table, paths, result, now }) {
|
|
632
|
+
try {
|
|
633
|
+
const at = new Date(now()).toISOString();
|
|
634
|
+
let previous = null;
|
|
635
|
+
try { previous = await readJsonIfExists(paths.deferredPath); } catch { previous = null; }
|
|
636
|
+
if (result.status === 'deferred') {
|
|
637
|
+
await fsp.mkdir(paths.extractDir, { recursive: true });
|
|
638
|
+
await writeJsonAtomic(paths.deferredPath, await buildDeferredRecord({ dataDir, entity, table, paths, result, previous, at }));
|
|
639
|
+
return { deferredRecordPath: paths.deferredPath };
|
|
640
|
+
}
|
|
641
|
+
if (previous && previous.status === 'open') {
|
|
642
|
+
await writeJsonAtomic(paths.deferredPath, {
|
|
643
|
+
...previous,
|
|
644
|
+
status: 'resolved',
|
|
645
|
+
resolvedAt: at,
|
|
646
|
+
resolution: { status: result.status, rowsWritten: result.rowsWritten, manifestPath: projectRelative(dataDir, paths.manifestPath) },
|
|
647
|
+
});
|
|
648
|
+
return { deferredRecordPath: paths.deferredPath };
|
|
649
|
+
}
|
|
650
|
+
return {};
|
|
651
|
+
} catch (error) {
|
|
652
|
+
return { deferredRecordError: `could not write ${paths.deferredPath}: ${error.message}` };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** Every open deferred-extraction record under `data/source-extract/`. */
|
|
657
|
+
async function collectDeferredExtractions(dataDir) {
|
|
658
|
+
const extractDir = path.join(dataDir, 'source-extract');
|
|
659
|
+
let names;
|
|
660
|
+
try { names = await fsp.readdir(extractDir); } catch (error) { if (error.code === 'ENOENT') return []; throw error; }
|
|
661
|
+
const records = [];
|
|
662
|
+
for (const name of names.filter((n) => n.endsWith('.deferred.json')).sort()) {
|
|
663
|
+
// eslint-disable-next-line no-await-in-loop
|
|
664
|
+
const record = await readJsonIfExists(path.join(extractDir, name));
|
|
665
|
+
if (record && record.kind === DEFERRED_RECORD_KIND && record.status === 'open') records.push(record);
|
|
666
|
+
}
|
|
667
|
+
return records;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Completion-report input for open deferred extractions: one high-severity `operatorActions`
|
|
672
|
+
* row per entity (rendered above the counts in the report and the handoff), and the
|
|
673
|
+
* `complete_with_deferred_records` status so a deferred table never reads as a clean run.
|
|
674
|
+
*/
|
|
675
|
+
function deferredExtractionReportInput(records = []) {
|
|
676
|
+
const operatorActions = records.map((record) => ({
|
|
677
|
+
code: DEFERRED_ACTION_CODE,
|
|
678
|
+
severity: 'high',
|
|
679
|
+
entity: record.entity,
|
|
680
|
+
affectedCount: isCount(record.affectedRecords && record.affectedRecords.expectedTotal) ? record.affectedRecords.expectedTotal : null,
|
|
681
|
+
action: `Requires Import Operator: ${record.entity}${record.table ? ` (table ${record.table})` : ''} extraction is unresolved (${record.code}); affected new writes are held`
|
|
682
|
+
+ `${record.affectedRecords && record.affectedRecords.expectedTotal === null ? ' (row count unknown)' : ''}. ${record.action}. `
|
|
683
|
+
+ `Details: data/source-extract/${record.entity}.deferred.json. Copy prompt for Import Operator: "${record.copyPromptForImportOperator}"`,
|
|
684
|
+
deadlineReason: 'the missing rows can only be read from the source WordPress site with this migration\'s plugin credential, which may be gone after cutover',
|
|
685
|
+
}));
|
|
686
|
+
return { operatorActions, statuses: operatorActions.length ? ['complete_with_deferred_records'] : [] };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Extract one structure-bridge-plugin entity completely, resuming any interrupted read.
|
|
691
|
+
*
|
|
692
|
+
* @param options.dataDir the project's `data/` directory (holds wp-discovery/ and source-extract/)
|
|
693
|
+
* @param options.entity source entity name, e.g. `discount-rule-definition`
|
|
694
|
+
* @param options.table the fulfillment's bare table name, e.g. `wdr_rules`
|
|
695
|
+
* @param options.transport { httpClient, siteBaseUrl, auth, onSourceRead } -- the same bridge
|
|
696
|
+
* credential selection discovery used (see rp-import-codegen)
|
|
697
|
+
* @param options.expectEav true when discovery's schema reported an eavPair; with no read plan
|
|
698
|
+
* on disk that means key discovery failed, and the entity is deferred
|
|
699
|
+
* rather than read through the key-names-only request
|
|
700
|
+
* @param options.restart discard the in-progress `.tmp` and checkpoint first; the recovery for
|
|
701
|
+
* a `count-mismatch` or `reconcile` deferral, whose checkpoint would
|
|
702
|
+
* otherwise reconcile the same way again
|
|
703
|
+
*/
|
|
704
|
+
async function extractStructureBridgeEntity({ dataDir, entity, table, transport, expectEav = false, restart = false, readArtifacts = null, recordDeferred = true, now = () => Date.now() } = {}) {
|
|
705
|
+
if (!transport || !transport.siteBaseUrl) throw new Error('extractStructureBridgeEntity needs transport.siteBaseUrl');
|
|
706
|
+
const paths = structureBridgePaths({ dataDir, entity, table });
|
|
707
|
+
if (readArtifacts) {
|
|
708
|
+
// Bound callers supply explicit files; invalid references never select legacy sidecars.
|
|
709
|
+
const { checkedFile } = require('./plugin-entity-read-plan.js');
|
|
710
|
+
const projectDir = fs.realpathSync(path.dirname(path.resolve(dataDir)));
|
|
711
|
+
for (const [key, ref] of Object.entries(readArtifacts)) {
|
|
712
|
+
if (!['structureRequest', 'eavReadPlan'].includes(key)) throw new Error('unknown bridge read artifact');
|
|
713
|
+
const file = checkedFile(projectDir, ref, ['data/wp-discovery']);
|
|
714
|
+
const request = JSON.parse(await fsp.readFile(file, 'utf8'));
|
|
715
|
+
if (request.table !== table) throw new Error('read artifact physical table mismatch');
|
|
716
|
+
if (key === 'eavReadPlan' && client.eavReadPlanBatches(request).some((b) => b.structureRequest.table !== table)) throw new Error('EAV batch physical table mismatch');
|
|
717
|
+
const prefix = key === 'structureRequest' ? 'request' : 'plan';
|
|
718
|
+
paths[`${prefix}Path`] = file;
|
|
719
|
+
paths[`${prefix}File`] = path.relative(projectDir, file).split(path.sep).join('/');
|
|
720
|
+
}
|
|
721
|
+
if (expectEav && !readArtifacts.eavReadPlan) throw new Error('bound EAV component needs value plan');
|
|
722
|
+
if (!expectEav && !readArtifacts.structureRequest) throw new Error('bound component needs request');
|
|
723
|
+
if (!readArtifacts.eavReadPlan) paths.planPath = path.join(paths.extractDir, `${entity}.no-eav-plan`);
|
|
724
|
+
}
|
|
725
|
+
const redactions = makeRedactionTally();
|
|
726
|
+
const hasPlan = await exists(paths.planPath);
|
|
727
|
+
let result;
|
|
728
|
+
try {
|
|
729
|
+
if (restart) await discardInProgress(paths);
|
|
730
|
+
if (hasPlan) {
|
|
731
|
+
result = await extractEav({ entity, table, paths, transport, now, redactions });
|
|
732
|
+
} else if (expectEav) {
|
|
733
|
+
result = deferred('eav', 'missing-artifact', `table ${table}: the schema reports a key/value pair but ${paths.planFile} does not exist -- key discovery failed, so values cannot be extracted; the key-names request is not an extraction`);
|
|
734
|
+
} else {
|
|
735
|
+
result = await extractSingleRequest({ entity, table, paths, transport, now, redactions });
|
|
736
|
+
}
|
|
737
|
+
} catch (error) {
|
|
738
|
+
// A local I/O or artifact-parse failure: in-progress files stay for the next attempt.
|
|
739
|
+
result = deferred(hasPlan ? 'eav' : 'single-request', 'unexpected-error', `table ${table}: ${error.message}`);
|
|
740
|
+
}
|
|
741
|
+
const recorded = recordDeferred ? await recordOutcome({ dataDir, entity, table, paths, result, now }) : {};
|
|
742
|
+
const recovery = result.status === 'deferred' ? recoveryFor(result.code, { entity, table, paths }) : null;
|
|
743
|
+
return { entity, table, ...result, ...recorded, recovery, redactions: redactions.report() };
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
module.exports = {
|
|
747
|
+
PAGE_LIMIT,
|
|
748
|
+
fileStats,
|
|
749
|
+
DEFERRED_ACTION_CODE,
|
|
750
|
+
collectDeferredExtractions,
|
|
751
|
+
deferredExtractionReportInput,
|
|
752
|
+
extractStructureBridgeEntity,
|
|
753
|
+
structureBridgePaths,
|
|
754
|
+
structureRequestHash,
|
|
755
|
+
};
|