@wix/agent-skills 1.35.1 → 1.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (820) hide show
  1. package/package.json +8 -3
  2. package/skills/wix-app/references/CODE_QUALITY.md +1 -1
  3. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +27 -29
  4. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +1 -1
  5. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +26 -25
  6. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +12 -42
  7. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +9 -20
  8. package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +5 -4
  9. package/skills/wix-app/references/editor-react-component/PARTS.md +1 -1
  10. package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +1 -1
  11. package/skills/wix-app/references/editor-react-component/VECTOR-ART.md +95 -0
  12. package/skills/wix-app/references/editor-react-component/VISUAL-QUALITY.md +39 -0
  13. package/skills/wix-docs/references/CALLING.md +1 -1
  14. package/skills/wix-headless-kit/guides/api-run.md +1 -1
  15. package/skills/wix-headless-kit/install/pins.json +1 -1
  16. package/skills/wix-headless-templates/blog/seed/seed-blog.mjs +1 -1
  17. package/skills/wix-headless-templates/donations/app/wix/donations/campaigns.ts +0 -1
  18. package/skills/wix-headless-templates/donations/app/wix/donations/donate.ts +1 -1
  19. package/skills/wix-headless-templates/donations/project/src/wix/donations/campaigns.ts +0 -1
  20. package/skills/wix-headless-templates/donations/project/src/wix/donations/donate.ts +1 -1
  21. package/skills/wix-headless-templates/donations/rest/campaigns.ts +0 -1
  22. package/skills/wix-headless-templates/donations/rest/donate.ts +1 -1
  23. package/skills/wix-headless-templates/events/seed/seed-events.mjs +1 -1
  24. package/skills/wix-headless-templates/faq/INSTRUCTIONS.md +1 -1
  25. package/skills/wix-headless-templates/faq/app/wix/faq/faq.ts +2 -2
  26. package/skills/wix-headless-templates/faq/project/src/wix/faq/faq.ts +2 -2
  27. package/skills/wix-headless-templates/faq/rest/faq.ts +2 -2
  28. package/skills/wix-headless-templates/faq/seed/SEED.md +1 -1
  29. package/skills/wix-headless-templates/faq/seed/read-site.mjs +1 -1
  30. package/skills/wix-headless-templates/faq/seed/seed-faq.mjs +5 -5
  31. package/skills/wix-headless-templates/forms/app/wix/forms/forms.ts +2 -2
  32. package/skills/wix-headless-templates/forms/app/wix/forms/submissions.ts +2 -2
  33. package/skills/wix-headless-templates/forms/project/src/wix/forms/forms.ts +2 -2
  34. package/skills/wix-headless-templates/forms/project/src/wix/forms/submissions.ts +2 -2
  35. package/skills/wix-headless-templates/members/seed/read-site.mjs +1 -1
  36. package/skills/wix-headless-templates/pricing-plans/app/wix/pricing-plans/plan-access.ts +1 -1
  37. package/skills/wix-headless-templates/pricing-plans/project/src/wix/pricing-plans/plan-access.ts +1 -1
  38. package/skills/wix-headless-templates/pricing-plans/rest/plan-access.ts +1 -1
  39. package/skills/wix-headless-templates/restaurants/app/wix/restaurants/menu.ts +0 -1
  40. package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/menu.ts +0 -1
  41. package/skills/wix-headless-templates/restaurants/rest/menu.ts +0 -1
  42. package/skills/wix-headless-templates/shared/rest/client.ts +1 -1
  43. package/skills/wix-headless-templates/shared/seed/site.mjs +2 -1
  44. package/skills/wix-manage/SKILL.md +3 -3
  45. package/skills/wix-manage/references/cms/cms-data-items-crud.md +78 -1
  46. package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +11 -11
  47. package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +0 -1
  48. package/skills/wix-manage/references/google-ads/install-and-create-account.md +0 -1
  49. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +20 -51
  50. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +4 -4
  51. package/skills/wix-manage/references/rich-content/ricos-converter-service.md +1 -1
  52. package/skills/wix-base44-connector/SKILL.md +0 -437
  53. package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +0 -78
  54. package/skills/wix-base44-connector/scripts/utils.cjs +0 -466
  55. package/skills/wix-export/SKILL.md +0 -83
  56. package/skills/wix-harmony-editor/.publish-manifest.json +0 -8
  57. package/skills/wix-harmony-editor/COPY-SITE.md +0 -494
  58. package/skills/wix-harmony-editor/SKILL.md +0 -317
  59. package/skills/wix-harmony-editor/scripts/copy/build-description.mjs +0 -249
  60. package/skills/wix-harmony-editor/scripts/copy/check-description.mjs +0 -225
  61. package/skills/wix-harmony-editor/scripts/copy/check-mirror.mjs +0 -89
  62. package/skills/wix-harmony-editor/scripts/copy/compare-preview.mjs +0 -76
  63. package/skills/wix-harmony-editor/scripts/copy/deploy-description.mjs +0 -212
  64. package/skills/wix-harmony-editor/scripts/copy/desc-hash.mjs +0 -44
  65. package/skills/wix-harmony-editor/scripts/copy/editor-run.mjs +0 -45
  66. package/skills/wix-harmony-editor/scripts/copy/export-page.mjs +0 -53
  67. package/skills/wix-harmony-editor/scripts/copy/find-interactions.mjs +0 -76
  68. package/skills/wix-harmony-editor/scripts/copy/hide-scrollbars.mjs +0 -16
  69. package/skills/wix-harmony-editor/scripts/copy/import-media.mjs +0 -150
  70. package/skills/wix-harmony-editor/scripts/copy/link-url.mjs +0 -27
  71. package/skills/wix-harmony-editor/scripts/copy/lint-api.mjs +0 -56
  72. package/skills/wix-harmony-editor/scripts/copy/measure-source.mjs +0 -211
  73. package/skills/wix-harmony-editor/scripts/copy/merge-mirrors.mjs +0 -29
  74. package/skills/wix-harmony-editor/scripts/copy/mirror-site.mjs +0 -92
  75. package/skills/wix-harmony-editor/scripts/copy/mobile-stage.mjs +0 -56
  76. package/skills/wix-harmony-editor/scripts/copy/preview-widths.mjs +0 -73
  77. package/skills/wix-harmony-editor/scripts/copy/render-plate.mjs +0 -67
  78. package/skills/wix-harmony-editor/scripts/copy/serve-mirror.mjs +0 -102
  79. package/skills/wix-harmony-editor/scripts/copy/snapshot-capabilities.mjs +0 -40
  80. package/skills/wix-harmony-editor/scripts/copy/split-sections.mjs +0 -191
  81. package/skills/wix-harmony-editor/scripts/copy/upload-media-manager.mjs +0 -61
  82. package/skills/wix-harmony-editor/scripts/launch.sh +0 -359
  83. package/skills/wix-headless/SKILL.md +0 -151
  84. package/skills/wix-headless/references/AI_FEATURES.md +0 -178
  85. package/skills/wix-headless/references/CAPABILITIES.md +0 -117
  86. package/skills/wix-headless/references/CONTENT.md +0 -36
  87. package/skills/wix-headless/references/DESIGN.md +0 -55
  88. package/skills/wix-headless/references/DISCOVERY.md +0 -80
  89. package/skills/wix-headless/references/DOC_DISCOVERY.md +0 -43
  90. package/skills/wix-headless/references/FEEDBACK.md +0 -129
  91. package/skills/wix-headless/references/IMAGE_GENERATION.md +0 -80
  92. package/skills/wix-headless/references/SDK_HANDOFF.md +0 -108
  93. package/skills/wix-headless/references/SEED.md +0 -74
  94. package/skills/wix-headless/references/SETUP.md +0 -60
  95. package/skills/wix-headless/references/astro.md +0 -98
  96. package/skills/wix-headless/references/inline-recipes/experience-donations.md +0 -102
  97. package/skills/wix-headless/references/inline-recipes/experience-store.md +0 -49
  98. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +0 -196
  99. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +0 -590
  100. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +0 -279
  101. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +0 -163
  102. package/skills/wix-headless/references/inline-recipes/how-to-code-donations.md +0 -146
  103. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +0 -203
  104. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +0 -696
  105. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +0 -124
  106. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +0 -160
  107. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +0 -118
  108. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +0 -132
  109. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +0 -126
  110. package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +0 -254
  111. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +0 -191
  112. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +0 -213
  113. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +0 -161
  114. package/skills/wix-headless/references/inline-recipes/setup-blog.md +0 -175
  115. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +0 -232
  116. package/skills/wix-headless/references/inline-recipes/setup-cms.md +0 -200
  117. package/skills/wix-headless/references/inline-recipes/setup-donations.md +0 -15
  118. package/skills/wix-headless/references/inline-recipes/setup-events.md +0 -196
  119. package/skills/wix-headless/references/inline-recipes/setup-forms.md +0 -288
  120. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +0 -214
  121. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +0 -155
  122. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +0 -229
  123. package/skills/wix-headless/references/inline-recipes/setup-rentals.md +0 -254
  124. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +0 -73
  125. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +0 -197
  126. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +0 -136
  127. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +0 -152
  128. package/skills/wix-headless/references/managed/AUTHENTICATION.md +0 -71
  129. package/skills/wix-headless/references/managed/CONNECT.md +0 -74
  130. package/skills/wix-headless/references/managed/CREATE.md +0 -149
  131. package/skills/wix-headless/references/managed/DEPLOYMENT.md +0 -104
  132. package/skills/wix-headless/references/non-astro.md +0 -59
  133. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +0 -22
  134. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +0 -15
  135. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +0 -55
  136. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +0 -71
  137. package/skills/wix-headless/scripts/bootstrap.mjs +0 -156
  138. package/skills/wix-headless-replatform/.publish-manifest.json +0 -8
  139. package/skills/wix-headless-replatform/SKILL.md +0 -116
  140. package/skills/wix-headless-replatform/agents/openai.yaml +0 -6
  141. package/skills/wix-headless-replatform/references/control-and-visual-fidelity-contract.md +0 -43
  142. package/skills/wix-headless-replatform/references/extraction-schema.md +0 -187
  143. package/skills/wix-headless-replatform/references/interaction-runtime-contract.md +0 -137
  144. package/skills/wix-headless-replatform/references/layout-blueprint-contract.md +0 -41
  145. package/skills/wix-headless-replatform/references/output-contract.md +0 -93
  146. package/skills/wix-headless-replatform/references/post-build-gap-contract.md +0 -125
  147. package/skills/wix-headless-replatform/references/qa-checklist.md +0 -83
  148. package/skills/wix-headless-replatform/references/safety.md +0 -19
  149. package/skills/wix-headless-replatform/references/ui-normalization-contract.md +0 -70
  150. package/skills/wix-headless-replatform/references/workflow.md +0 -157
  151. package/skills/wix-headless-replatform/registry/README.md +0 -20
  152. package/skills/wix-headless-replatform/registry/approvals/button/shadcn-default-ac60ef5.json +0 -28
  153. package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/button.tsx +0 -54
  154. package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/capabilities.json +0 -60
  155. package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/contract.md +0 -48
  156. package/skills/wix-headless-replatform/registry/licenses/shadcn-ui-ac60ef5-MIT.txt +0 -21
  157. package/skills/wix-headless-replatform/registry/registry.json +0 -36
  158. package/skills/wix-headless-replatform/resources/rp-facelift/SKILL.md +0 -32
  159. package/skills/wix-headless-replatform/resources/rp-project-context/SKILL.md +0 -62
  160. package/skills/wix-headless-replatform/resources/rp-qa-gap-loop/SKILL.md +0 -64
  161. package/skills/wix-headless-replatform/resources/rp-source-evidence/SKILL.md +0 -42
  162. package/skills/wix-headless-replatform/resources/rp-ui-implementation/SKILL.md +0 -73
  163. package/skills/wix-headless-replatform/schemas/build-plan.schema.json +0 -10
  164. package/skills/wix-headless-replatform/schemas/candidate.schema.json +0 -19
  165. package/skills/wix-headless-replatform/schemas/common.schema.json +0 -33
  166. package/skills/wix-headless-replatform/schemas/decision-patch.schema.json +0 -10
  167. package/skills/wix-headless-replatform/schemas/decision-request.schema.json +0 -10
  168. package/skills/wix-headless-replatform/schemas/extraction-gap.schema.json +0 -28
  169. package/skills/wix-headless-replatform/schemas/extraction-manifest.schema.json +0 -34
  170. package/skills/wix-headless-replatform/schemas/final-report.schema.json +0 -10
  171. package/skills/wix-headless-replatform/schemas/foundation.schema.json +0 -11
  172. package/skills/wix-headless-replatform/schemas/metadata.schema.json +0 -11
  173. package/skills/wix-headless-replatform/schemas/observation-packet.schema.json +0 -17
  174. package/skills/wix-headless-replatform/schemas/page-capture.schema.json +0 -11
  175. package/skills/wix-headless-replatform/schemas/page-resolution.schema.json +0 -33
  176. package/skills/wix-headless-replatform/schemas/shared-chrome.schema.json +0 -11
  177. package/skills/wix-headless-replatform/schemas/unit.schema.json +0 -40
  178. package/skills/wix-headless-replatform/scripts/assemble-extraction.mjs +0 -844
  179. package/skills/wix-headless-replatform/scripts/benchmark-extraction.mjs +0 -163
  180. package/skills/wix-headless-replatform/scripts/browser-extraction-preflight.mjs +0 -42
  181. package/skills/wix-headless-replatform/scripts/build-extracted-homepage.mjs +0 -228
  182. package/skills/wix-headless-replatform/scripts/clear-wix-starter.mjs +0 -87
  183. package/skills/wix-headless-replatform/scripts/create-wix-project.mjs +0 -505
  184. package/skills/wix-headless-replatform/scripts/discover-urls.mjs +0 -142
  185. package/skills/wix-headless-replatform/scripts/extract-assets.mjs +0 -140
  186. package/skills/wix-headless-replatform/scripts/extract-design-system.mjs +0 -637
  187. package/skills/wix-headless-replatform/scripts/extract-interactions.mjs +0 -1903
  188. package/skills/wix-headless-replatform/scripts/extract-page.mjs +0 -2461
  189. package/skills/wix-headless-replatform/scripts/extract-seo.mjs +0 -25
  190. package/skills/wix-headless-replatform/scripts/extract-tokens.mjs +0 -63
  191. package/skills/wix-headless-replatform/scripts/finalize-extraction-report.mjs +0 -126
  192. package/skills/wix-headless-replatform/scripts/finalize-gap-review.mjs +0 -180
  193. package/skills/wix-headless-replatform/scripts/finalize-gap-review.test.mjs +0 -98
  194. package/skills/wix-headless-replatform/scripts/finalize-migration-website.mjs +0 -103
  195. package/skills/wix-headless-replatform/scripts/generate-control-state-contract.mjs +0 -176
  196. package/skills/wix-headless-replatform/scripts/generate-design-md.mjs +0 -261
  197. package/skills/wix-headless-replatform/scripts/generate-extraction-validators.mjs +0 -25
  198. package/skills/wix-headless-replatform/scripts/generate-invoice-download-backend.mjs +0 -342
  199. package/skills/wix-headless-replatform/scripts/generate-layout-blueprint.mjs +0 -140
  200. package/skills/wix-headless-replatform/scripts/generate-repeater-cms.mjs +0 -22
  201. package/skills/wix-headless-replatform/scripts/generate-route-plan.mjs +0 -143
  202. package/skills/wix-headless-replatform/scripts/generate-scene-contract.mjs +0 -288
  203. package/skills/wix-headless-replatform/scripts/generate-ui-normalization.mjs +0 -266
  204. package/skills/wix-headless-replatform/scripts/generate-visual-assets.mjs +0 -111
  205. package/skills/wix-headless-replatform/scripts/lib/browser-tooling.mjs +0 -570
  206. package/skills/wix-headless-replatform/scripts/lib/common.mjs +0 -329
  207. package/skills/wix-headless-replatform/scripts/lib/component-capabilities.mjs +0 -121
  208. package/skills/wix-headless-replatform/scripts/lib/component-registry.mjs +0 -282
  209. package/skills/wix-headless-replatform/scripts/lib/content-boundary.mjs +0 -61
  210. package/skills/wix-headless-replatform/scripts/lib/extraction-contract.mjs +0 -374
  211. package/skills/wix-headless-replatform/scripts/lib/extraction-detectors.mjs +0 -243
  212. package/skills/wix-headless-replatform/scripts/lib/extraction-validators.generated.mjs +0 -1139
  213. package/skills/wix-headless-replatform/scripts/lib/font-contract.mjs +0 -326
  214. package/skills/wix-headless-replatform/scripts/lib/frontend-automation-state.mjs +0 -184
  215. package/skills/wix-headless-replatform/scripts/lib/gap-analysis.mjs +0 -396
  216. package/skills/wix-headless-replatform/scripts/lib/html-extract.mjs +0 -168
  217. package/skills/wix-headless-replatform/scripts/lib/interaction-bootstrap.mjs +0 -10
  218. package/skills/wix-headless-replatform/scripts/lib/interaction-runtime.mjs +0 -194
  219. package/skills/wix-headless-replatform/scripts/lib/interaction-timeline.mjs +0 -348
  220. package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/classifiers.mjs +0 -141
  221. package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/geometry.mjs +0 -43
  222. package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/vocabulary.mjs +0 -18
  223. package/skills/wix-headless-replatform/scripts/lib/ui-normalization.css +0 -64
  224. package/skills/wix-headless-replatform/scripts/optimize-svg.mjs +0 -31
  225. package/skills/wix-headless-replatform/scripts/post-build-gap.mjs +0 -358
  226. package/skills/wix-headless-replatform/scripts/provision-repeater-cms.mjs +0 -25
  227. package/skills/wix-headless-replatform/scripts/record-gap-decision.mjs +0 -39
  228. package/skills/wix-headless-replatform/scripts/replay-extraction.mjs +0 -107
  229. package/skills/wix-headless-replatform/scripts/resolve-home-page.mjs +0 -96
  230. package/skills/wix-headless-replatform/scripts/site-clone.mjs +0 -658
  231. package/skills/wix-headless-replatform/scripts/validate-font-contract.mjs +0 -29
  232. package/skills/wix-headless-replatform/scripts/verify-interactions.mjs +0 -669
  233. package/skills/wix-headless-replatform/scripts/visual-qa.mjs +0 -370
  234. package/skills/wix-import/SKILL.md +0 -90
  235. package/skills/wix-replatform/.publish-manifest.json +0 -8
  236. package/skills/wix-replatform/CLI.md +0 -162
  237. package/skills/wix-replatform/README.md +0 -52
  238. package/skills/wix-replatform/SKILL.md +0 -238
  239. package/skills/wix-replatform/console/account.css +0 -1
  240. package/skills/wix-replatform/console/app.css +0 -1
  241. package/skills/wix-replatform/console/app.js +0 -117
  242. package/skills/wix-replatform/console/index.html +0 -4
  243. package/skills/wix-replatform/console/json.css +0 -11
  244. package/skills/wix-replatform/lib/agent-adapters/index.js +0 -71
  245. package/skills/wix-replatform/lib/app-page-routing.js +0 -75
  246. package/skills/wix-replatform/lib/artifact-freshness.js +0 -225
  247. package/skills/wix-replatform/lib/blocked-data-requests.js +0 -427
  248. package/skills/wix-replatform/lib/census-reconciliation.js +0 -426
  249. package/skills/wix-replatform/lib/completion-report.js +0 -1580
  250. package/skills/wix-replatform/lib/config-env.js +0 -133
  251. package/skills/wix-replatform/lib/confirmed-pictures.js +0 -212
  252. package/skills/wix-replatform/lib/dependency-readiness.js +0 -423
  253. package/skills/wix-replatform/lib/dependency-recovery.js +0 -21
  254. package/skills/wix-replatform/lib/destination-choice.js +0 -163
  255. package/skills/wix-replatform/lib/destination-handoff.js +0 -62
  256. package/skills/wix-replatform/lib/destination-http.js +0 -81
  257. package/skills/wix-replatform/lib/destination-route.js +0 -98
  258. package/skills/wix-replatform/lib/destination.js +0 -919
  259. package/skills/wix-replatform/lib/discovered-census.js +0 -231
  260. package/skills/wix-replatform/lib/discovered-set-status.js +0 -255
  261. package/skills/wix-replatform/lib/document-url-guard.js +0 -203
  262. package/skills/wix-replatform/lib/entity-qa-attestation.js +0 -339
  263. package/skills/wix-replatform/lib/entity-verification.js +0 -18
  264. package/skills/wix-replatform/lib/execution-capability.js +0 -120
  265. package/skills/wix-replatform/lib/execution-log.js +0 -170
  266. package/skills/wix-replatform/lib/execution-manifest.js +0 -203
  267. package/skills/wix-replatform/lib/execution-state.js +0 -195
  268. package/skills/wix-replatform/lib/expectations-ledger.js +0 -96
  269. package/skills/wix-replatform/lib/generated-plugin-handoff.js +0 -140
  270. package/skills/wix-replatform/lib/import-readiness-contract.js +0 -23
  271. package/skills/wix-replatform/lib/import-recovery.js +0 -1107
  272. package/skills/wix-replatform/lib/imported-redirect-targets.js +0 -608
  273. package/skills/wix-replatform/lib/internal-link-reconciliation.js +0 -166
  274. package/skills/wix-replatform/lib/link-audit.js +0 -173
  275. package/skills/wix-replatform/lib/link-census.js +0 -194
  276. package/skills/wix-replatform/lib/link-plan.js +0 -148
  277. package/skills/wix-replatform/lib/link-verification.js +0 -274
  278. package/skills/wix-replatform/lib/link-write-journal.js +0 -42
  279. package/skills/wix-replatform/lib/local-state.js +0 -1481
  280. package/skills/wix-replatform/lib/member-completion.js +0 -658
  281. package/skills/wix-replatform/lib/member-crosswalk.js +0 -69
  282. package/skills/wix-replatform/lib/member-outcome-registry.js +0 -265
  283. package/skills/wix-replatform/lib/migration-completion.js +0 -311
  284. package/skills/wix-replatform/lib/operator-identity.js +0 -47
  285. package/skills/wix-replatform/lib/orchestration-decisions.js +0 -248
  286. package/skills/wix-replatform/lib/orchestration-preflight.js +0 -525
  287. package/skills/wix-replatform/lib/orchestration-router.js +0 -698
  288. package/skills/wix-replatform/lib/orchestration-state.js +0 -631
  289. package/skills/wix-replatform/lib/order-reference-readiness.js +0 -30
  290. package/skills/wix-replatform/lib/payments-outcome-registry.js +0 -200
  291. package/skills/wix-replatform/lib/plugin-cms-fallback.js +0 -618
  292. package/skills/wix-replatform/lib/preservation-approval-stamp.js +0 -54
  293. package/skills/wix-replatform/lib/product-media-deferral.js +0 -96
  294. package/skills/wix-replatform/lib/progress-log.js +0 -240
  295. package/skills/wix-replatform/lib/progress-log.sh +0 -140
  296. package/skills/wix-replatform/lib/read-back-approval.js +0 -184
  297. package/skills/wix-replatform/lib/read-back-comparison.js +0 -136
  298. package/skills/wix-replatform/lib/read-back-contract.js +0 -243
  299. package/skills/wix-replatform/lib/read-back-extract.js +0 -118
  300. package/skills/wix-replatform/lib/read-back-gate.js +0 -734
  301. package/skills/wix-replatform/lib/read-back-reads.js +0 -94
  302. package/skills/wix-replatform/lib/read-back-state.js +0 -107
  303. package/skills/wix-replatform/lib/read-back-summary.js +0 -81
  304. package/skills/wix-replatform/lib/rerun-conflicts.js +0 -334
  305. package/skills/wix-replatform/lib/run-controller.js +0 -401
  306. package/skills/wix-replatform/lib/seo-outcome-registry.js +0 -45
  307. package/skills/wix-replatform/lib/setup-verification.js +0 -148
  308. package/skills/wix-replatform/lib/source-access.js +0 -231
  309. package/skills/wix-replatform/lib/source-coverage.js +0 -1130
  310. package/skills/wix-replatform/lib/source-secrets.js +0 -277
  311. package/skills/wix-replatform/lib/structured-link-patch.js +0 -59
  312. package/skills/wix-replatform/lib/task-state.js +0 -110
  313. package/skills/wix-replatform/lib/url-preservation-report.js +0 -319
  314. package/skills/wix-replatform/lib/url-preservation-state.js +0 -739
  315. package/skills/wix-replatform/lib/url-redirect-plan.js +0 -728
  316. package/skills/wix-replatform/lib/url-verification.js +0 -315
  317. package/skills/wix-replatform/lib/user-action-effects.js +0 -42
  318. package/skills/wix-replatform/lib/user-actions.js +0 -264
  319. package/skills/wix-replatform/lib/website-handoff.js +0 -1220
  320. package/skills/wix-replatform/lib/wix-client-callers.js +0 -54
  321. package/skills/wix-replatform/lib/wix-dashboard-url.js +0 -35
  322. package/skills/wix-replatform/lib/write-verification-state.js +0 -92
  323. package/skills/wix-replatform/lib/write-verification.js +0 -274
  324. package/skills/wix-replatform/resources/rp-destination/SKILL.md +0 -137
  325. package/skills/wix-replatform/resources/rp-discovery/SKILL.md +0 -357
  326. package/skills/wix-replatform/resources/rp-discovery/scripts/promote-source-coverage.js +0 -47
  327. package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +0 -1369
  328. package/skills/wix-replatform/resources/rp-execute-import/scripts/shipping-estimate-audit.js +0 -70
  329. package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +0 -482
  330. package/skills/wix-replatform/resources/rp-execute-setup/scripts/execute-setup.js +0 -442
  331. package/skills/wix-replatform/resources/rp-execute-setup/scripts/mint-token.sh +0 -64
  332. package/skills/wix-replatform/resources/rp-execution-policy/SKILL.md +0 -112
  333. package/skills/wix-replatform/resources/rp-execution-policy/lib/coupon-currency-plan-report.js +0 -143
  334. package/skills/wix-replatform/resources/rp-execution-policy/lib/payments-report.js +0 -244
  335. package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +0 -3158
  336. package/skills/wix-replatform/resources/rp-mapper/SKILL.md +0 -947
  337. package/skills/wix-replatform/resources/rp-mapper/lib/mapping-resolve.js +0 -217
  338. package/skills/wix-replatform/resources/rp-mapper/lib/structure-bridge-planning.js +0 -303
  339. package/skills/wix-replatform/resources/rp-mapper/scripts/coverage-reconcile.js +0 -88
  340. package/skills/wix-replatform/resources/rp-mapper/scripts/resolve-mapping.js +0 -148
  341. package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +0 -378
  342. package/skills/wix-replatform/resources/rp-preflight/SKILL.md +0 -40
  343. package/skills/wix-replatform/resources/rp-project-intake/SKILL.md +0 -55
  344. package/skills/wix-replatform/resources/rp-quick-runtime/SKILL.md +0 -60
  345. package/skills/wix-replatform/resources/rp-quick-runtime/lib/import-progress.js +0 -89
  346. package/skills/wix-replatform/resources/rp-quick-runtime/lib/limit-recovery.js +0 -36
  347. package/skills/wix-replatform/resources/rp-quick-shopify/SKILL.md +0 -123
  348. package/skills/wix-replatform/resources/rp-quick-shopify/quick-mode.json +0 -124
  349. package/skills/wix-replatform/resources/rp-quick-shopify/scripts/quick-mode.js +0 -522
  350. package/skills/wix-replatform/resources/rp-quick-woocommerce/SKILL.md +0 -56
  351. package/skills/wix-replatform/resources/rp-quick-woocommerce/quick-mode.json +0 -91
  352. package/skills/wix-replatform/resources/rp-quick-woocommerce/scripts/quick-mode.js +0 -255
  353. package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +0 -253
  354. package/skills/wix-replatform/resources/rp-setup-discovery/scripts/quick-setup-plan.js +0 -44
  355. package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +0 -481
  356. package/skills/wix-replatform/resources/rp-source-csv/lib/canonical-record.js +0 -136
  357. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
  358. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fingerprint.js +0 -310
  359. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-layout.js +0 -1081
  360. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-parse.js +0 -618
  361. package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +0 -969
  362. package/skills/wix-replatform/resources/rp-source-csv/source-schema.example.json +0 -196
  363. package/skills/wix-replatform/resources/rp-source-csv/vendors/bigcommerce.json +0 -66
  364. package/skills/wix-replatform/resources/rp-source-csv/vendors/magento.json +0 -60
  365. package/skills/wix-replatform/resources/rp-source-csv/vendors/shopify.json +0 -95
  366. package/skills/wix-replatform/resources/rp-source-csv/vendors/woocommerce.json +0 -82
  367. package/skills/wix-replatform/resources/rp-source-inputs/SKILL.md +0 -66
  368. package/skills/wix-replatform/resources/rp-source-sfcc/SKILL.md +0 -153
  369. package/skills/wix-replatform/resources/rp-source-shopify/SKILL.md +0 -394
  370. package/skills/wix-replatform/resources/rp-source-shopify/lib/discovery-reads.js +0 -200
  371. package/skills/wix-replatform/resources/rp-source-shopify/lib/import-findings.js +0 -34
  372. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk-sqlite-worker.js +0 -44
  373. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk.js +0 -1470
  374. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-cli.js +0 -446
  375. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-gift-cards.js +0 -474
  376. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-protected-fields.js +0 -879
  377. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-session.js +0 -426
  378. package/skills/wix-replatform/resources/rp-source-shopify/queries/articles.graphql +0 -24
  379. package/skills/wix-replatform/resources/rp-source-shopify/queries/blogs.graphql +0 -18
  380. package/skills/wix-replatform/resources/rp-source-shopify/queries/collections.graphql +0 -40
  381. package/skills/wix-replatform/resources/rp-source-shopify/queries/customers.graphql +0 -76
  382. package/skills/wix-replatform/resources/rp-source-shopify/queries/delivery-profiles.graphql +0 -51
  383. package/skills/wix-replatform/resources/rp-source-shopify/queries/discounts.graphql +0 -35
  384. package/skills/wix-replatform/resources/rp-source-shopify/queries/files.graphql +0 -20
  385. package/skills/wix-replatform/resources/rp-source-shopify/queries/gift-cards.graphql +0 -18
  386. package/skills/wix-replatform/resources/rp-source-shopify/queries/inventory.graphql +0 -31
  387. package/skills/wix-replatform/resources/rp-source-shopify/queries/locations.graphql +0 -21
  388. package/skills/wix-replatform/resources/rp-source-shopify/queries/menus.graphql +0 -21
  389. package/skills/wix-replatform/resources/rp-source-shopify/queries/metaobjects.graphql +0 -30
  390. package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-core.graphql +0 -150
  391. package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-fulfillments.graphql +0 -36
  392. package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-refunds.graphql +0 -48
  393. package/skills/wix-replatform/resources/rp-source-shopify/queries/pages.graphql +0 -20
  394. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-address.graphql +0 -8
  395. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-email.graphql +0 -6
  396. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-name.graphql +0 -8
  397. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-phone.graphql +0 -8
  398. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-address.graphql +0 -12
  399. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-email.graphql +0 -6
  400. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-name.graphql +0 -13
  401. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-phone.graphql +0 -13
  402. package/skills/wix-replatform/resources/rp-source-shopify/queries/products.graphql +0 -73
  403. package/skills/wix-replatform/resources/rp-source-shopify/queries/url-redirects.graphql +0 -13
  404. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-discovery.js +0 -693
  405. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-extract.js +0 -283
  406. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-gift-card-remedy.js +0 -191
  407. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-session-check.js +0 -115
  408. package/skills/wix-replatform/resources/rp-source-shopify/source-schema.example.json +0 -201
  409. package/skills/wix-replatform/resources/rp-source-wordpress/PLUGIN-READ-PLANS.md +0 -146
  410. package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +0 -970
  411. package/skills/wix-replatform/resources/rp-source-wordpress/lib/blocked-data-handlers.js +0 -73
  412. package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.js +0 -115
  413. package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.test-fixture.js +0 -91
  414. package/skills/wix-replatform/resources/rp-source-wordpress/lib/coupon-discovery.js +0 -440
  415. package/skills/wix-replatform/resources/rp-source-wordpress/lib/currency-gate.js +0 -125
  416. package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.js +0 -252
  417. package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.json +0 -183
  418. package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-summary.mjs +0 -333
  419. package/skills/wix-replatform/resources/rp-source-wordpress/lib/invoice-discovery.js +0 -426
  420. package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-disposition.js +0 -400
  421. package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-entity-read-plan.js +0 -633
  422. package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-knowledge.js +0 -1127
  423. package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.js +0 -194
  424. package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.json +0 -100
  425. package/skills/wix-replatform/resources/rp-source-wordpress/lib/sampled-ids-batch.js +0 -157
  426. package/skills/wix-replatform/resources/rp-source-wordpress/lib/secret-value-guard.js +0 -234
  427. package/skills/wix-replatform/resources/rp-source-wordpress/lib/store-currency.js +0 -101
  428. package/skills/wix-replatform/resources/rp-source-wordpress/lib/structure-bridge-reader.js +0 -755
  429. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.js +0 -1074
  430. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.test-fixture.js +0 -149
  431. package/skills/wix-replatform/resources/rp-source-wordpress/lib/woo-inventory.js +0 -25
  432. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +0 -444
  433. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-image-url.js +0 -186
  434. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-plugin-detect.js +0 -1422
  435. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-redirect-sources.js +0 -343
  436. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +0 -680
  437. package/skills/wix-replatform/resources/rp-source-wordpress/lib/yoast-product-seo-evidence.js +0 -272
  438. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/README.md +0 -373
  439. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/advanced-custom-fields.json +0 -101
  440. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/back-in-stock-notifier-for-woocommerce.json +0 -81
  441. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/capabilities-pending-decision.json +0 -128
  442. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/cms-fallback-eligible.json +0 -18
  443. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/contact-form-7.json +0 -104
  444. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/facebook-for-woocommerce.json +0 -44
  445. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/fingerprint-aliases.json +0 -49
  446. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/free-gifts-for-woocommerce.json +0 -94
  447. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/index.json +0 -863
  448. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mailpoet.json +0 -52
  449. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/membership-for-woocommerce.json +0 -161
  450. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mwb-bookings-for-woocommerce.json +0 -180
  451. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/no-migration-needed.json +0 -286
  452. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pricing-deals-for-woocommerce.json +0 -79
  453. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/product-extras-for-woocommerce.json +0 -120
  454. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pw-woocommerce-gift-cards.json +0 -128
  455. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/requires-development.json +0 -88
  456. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seo-by-rank-math.json +0 -80
  457. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seriously-simple-podcasting.json +0 -46
  458. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/strong-testimonials.json +0 -55
  459. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/subscriptions-for-woocommerce.json +0 -99
  460. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/tablepress.json +0 -55
  461. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/the-events-calendar.json +0 -160
  462. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/ti-woocommerce-wishlist.json +0 -81
  463. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-checkout-field-editor-pro.json +0 -60
  464. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-discount-rules.json +0 -74
  465. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-product-bundle.json +0 -75
  466. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-gift-cards.json +0 -60
  467. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-other-payment-gateway.json +0 -44
  468. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-pdf-invoices-packing-slips.json +0 -57
  469. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-product-addon.json +0 -141
  470. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-progressive-discounts.json +0 -54
  471. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wordpress-seo.json +0 -158
  472. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpc-composite-products.json +0 -97
  473. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpforms-lite.json +0 -86
  474. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/capture-store-settings.js +0 -90
  475. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge-validate.js +0 -51
  476. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge.js +0 -103
  477. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +0 -2409
  478. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-plugin-inventory.js +0 -273
  479. package/skills/wix-replatform/resources/rp-source-wordpress/secret-value-patterns.v1.json +0 -11
  480. package/skills/wix-replatform/resources/rp-source-wordpress/source-schema.example.json +0 -71
  481. package/skills/wix-replatform/resources/rp-source-wordpress/sub-parts.json +0 -13
  482. package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +0 -1046
  483. package/skills/wix-replatform/resources/rp-target-wix/app-page-routing-capabilities.json +0 -36
  484. package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +0 -57
  485. package/skills/wix-replatform/resources/rp-target-wix/domains/SERVICE-PLUGINS.md +0 -322
  486. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/docs-survey.json +0 -70
  487. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +0 -29
  488. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +0 -71
  489. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +0 -83
  490. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +0 -95
  491. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +0 -66
  492. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/docs-survey.json +0 -332
  493. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/domain.json +0 -75
  494. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/booking.json +0 -181
  495. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service-add-on.json +0 -130
  496. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service.json +0 -180
  497. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/docs-survey.json +0 -161
  498. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +0 -25
  499. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +0 -67
  500. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +0 -75
  501. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +0 -41
  502. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/docs-survey.json +0 -204
  503. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +0 -27
  504. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +0 -275
  505. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +0 -105
  506. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +0 -77
  507. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/docs-survey.json +0 -306
  508. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +0 -44
  509. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/delivery-profile.json +0 -171
  510. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/discount-rule.json +0 -182
  511. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-applied-discount.json +0 -129
  512. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-invoice-document.json +0 -192
  513. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item-options.json +0 -123
  514. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +0 -85
  515. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-merchant-note.json +0 -112
  516. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-payment.json +0 -191
  517. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +0 -164
  518. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +0 -462
  519. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/pickup-location.json +0 -122
  520. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/shipping-option.json +0 -98
  521. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/wishlist-item.json +0 -86
  522. package/skills/wix-replatform/resources/rp-target-wix/domains/events/docs-survey.json +0 -128
  523. package/skills/wix-replatform/resources/rp-target-wix/domains/events/domain.json +0 -38
  524. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/category.json +0 -73
  525. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/event.json +0 -107
  526. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/registration-form.json +0 -89
  527. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/rsvp.json +0 -118
  528. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/ticket-definition.json +0 -93
  529. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/docs-survey.json +0 -86
  530. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/domain.json +0 -56
  531. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form-submission.json +0 -121
  532. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form.json +0 -134
  533. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/docs-survey.json +0 -41
  534. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/domain.json +0 -50
  535. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card-product.json +0 -87
  536. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card.json +0 -130
  537. package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +0 -1418
  538. package/skills/wix-replatform/resources/rp-target-wix/domains/media/docs-survey.json +0 -41
  539. package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +0 -24
  540. package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +0 -75
  541. package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +0 -71
  542. package/skills/wix-replatform/resources/rp-target-wix/domains/members/docs-survey.json +0 -119
  543. package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +0 -23
  544. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +0 -71
  545. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/custom-field.json +0 -82
  546. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member-about.json +0 -77
  547. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +0 -205
  548. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/docs-survey.json +0 -41
  549. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/domain.json +0 -46
  550. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan-order.json +0 -137
  551. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan.json +0 -133
  552. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/docs-survey.json +0 -7
  553. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/domain.json +0 -36
  554. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/amazon-shop-connect.json +0 -71
  555. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/ebay-shop-connect.json +0 -67
  556. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/google-youtube-connect.json +0 -72
  557. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/meta-catalog-connect.json +0 -74
  558. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/pinterest-catalog-connect.json +0 -69
  559. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/tiktok-catalog-connect.json +0 -61
  560. package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +0 -111
  561. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/docs-survey.json +0 -109
  562. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/domain.json +0 -73
  563. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/item-seo-tags.json +0 -148
  564. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/seo-pattern.json +0 -91
  565. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/site-seo-tags.json +0 -79
  566. package/skills/wix-replatform/resources/rp-target-wix/domains/site/docs-survey.json +0 -32
  567. package/skills/wix-replatform/resources/rp-target-wix/domains/site/domain.json +0 -49
  568. package/skills/wix-replatform/resources/rp-target-wix/domains/site/entities/business-contact.json +0 -103
  569. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/docs-survey.json +0 -203
  570. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +0 -42
  571. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/back-in-stock-request.json +0 -119
  572. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/brand.json +0 -111
  573. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +0 -81
  574. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +0 -241
  575. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +0 -118
  576. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-customization.json +0 -111
  577. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +0 -79
  578. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +0 -221
  579. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/promotion.json +0 -84
  580. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/review.json +0 -178
  581. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/subscription-product.json +0 -87
  582. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/docs-survey.json +0 -106
  583. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/domain.json +0 -64
  584. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/manual-tax-mapping.json +0 -119
  585. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-group.json +0 -94
  586. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-region.json +0 -113
  587. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-settings.json +0 -65
  588. package/skills/wix-replatform/resources/rp-target-wix/lib/audit-summary.js +0 -64
  589. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-author-attribution.js +0 -165
  590. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-client.js +0 -60
  591. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-duplicates.js +0 -201
  592. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-ledger.js +0 -306
  593. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-report.js +0 -229
  594. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-stage.js +0 -55
  595. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build.js +0 -715
  596. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-media.js +0 -567
  597. package/skills/wix-replatform/resources/rp-target-wix/lib/bulk-results.js +0 -73
  598. package/skills/wix-replatform/resources/rp-target-wix/lib/contact-extended-fields.js +0 -194
  599. package/skills/wix-replatform/resources/rp-target-wix/lib/contact-import.js +0 -133
  600. package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +0 -150
  601. package/skills/wix-replatform/resources/rp-target-wix/lib/coupon-redemption-drift.js +0 -491
  602. package/skills/wix-replatform/resources/rp-target-wix/lib/customer-member.js +0 -104
  603. package/skills/wix-replatform/resources/rp-target-wix/lib/data-extension-schema.js +0 -673
  604. package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +0 -835
  605. package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-balance-reconciliation.js +0 -119
  606. package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-build.js +0 -566
  607. package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-redemption-build.js +0 -249
  608. package/skills/wix-replatform/resources/rp-target-wix/lib/ndjson.js +0 -260
  609. package/skills/wix-replatform/resources/rp-target-wix/lib/order-applied-discount-build.js +0 -63
  610. package/skills/wix-replatform/resources/rp-target-wix/lib/order-buyer.js +0 -187
  611. package/skills/wix-replatform/resources/rp-target-wix/lib/order-channel-build.js +0 -64
  612. package/skills/wix-replatform/resources/rp-target-wix/lib/order-history.js +0 -1027
  613. package/skills/wix-replatform/resources/rp-target-wix/lib/order-invoice-contract.js +0 -90
  614. package/skills/wix-replatform/resources/rp-target-wix/lib/order-payment-mapping.js +0 -420
  615. package/skills/wix-replatform/resources/rp-target-wix/lib/pricing-plan-definition.js +0 -177
  616. package/skills/wix-replatform/resources/rp-target-wix/lib/product-import.js +0 -151
  617. package/skills/wix-replatform/resources/rp-target-wix/lib/product-seo.js +0 -723
  618. package/skills/wix-replatform/resources/rp-target-wix/lib/seo-redirects.js +0 -353
  619. package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-build.js +0 -1437
  620. package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-option-stage.js +0 -354
  621. package/skills/wix-replatform/resources/rp-target-wix/lib/store-config-build.js +0 -134
  622. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-category-attach.js +0 -221
  623. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-inventory.js +0 -248
  624. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +0 -321
  625. package/skills/wix-replatform/resources/rp-target-wix/lib/tax-build.js +0 -231
  626. package/skills/wix-replatform/resources/rp-target-wix/lib/url-mapper-transport.js +0 -28
  627. package/skills/wix-replatform/resources/rp-target-wix/lib/value-utils.js +0 -14
  628. package/skills/wix-replatform/resources/rp-target-wix/lib/variant-correlation.js +0 -34
  629. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +0 -388
  630. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +0 -255
  631. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +0 -6041
  632. package/skills/wix-replatform/resources/rp-target-wix/lib/write-verification.js +0 -3
  633. package/skills/wix-replatform/resources/rp-target-wix/scripts/docs-survey-sync.js +0 -150
  634. package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +0 -69
  635. package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge.js +0 -100
  636. package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +0 -112
  637. package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +0 -484
  638. package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +0 -318
  639. package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +0 -2831
  640. package/skills/wix-replatform/resources/rp-telemetry/lib/transcript-digest.js +0 -318
  641. package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +0 -83
  642. package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +0 -243
  643. package/skills/wix-replatform/resources/rp-website-continuation/SKILL.md +0 -94
  644. package/skills/wix-replatform/scripts/artifact-freshness.js +0 -76
  645. package/skills/wix-replatform/scripts/census-reconciliation.js +0 -25
  646. package/skills/wix-replatform/scripts/cms-fallback.js +0 -89
  647. package/skills/wix-replatform/scripts/discovered-census.js +0 -26
  648. package/skills/wix-replatform/scripts/discovered-set-status.js +0 -33
  649. package/skills/wix-replatform/scripts/entity-qa-attestation.js +0 -130
  650. package/skills/wix-replatform/scripts/execution-health-check.js +0 -415
  651. package/skills/wix-replatform/scripts/execution-manifest-validate.js +0 -43
  652. package/skills/wix-replatform/scripts/execution-state-prepare.js +0 -128
  653. package/skills/wix-replatform/scripts/expectations-ledger.js +0 -44
  654. package/skills/wix-replatform/scripts/import-contract-verify.js +0 -183
  655. package/skills/wix-replatform/scripts/internal-link-audit.js +0 -90
  656. package/skills/wix-replatform/scripts/item-seo-tags-verify.js +0 -205
  657. package/skills/wix-replatform/scripts/migration-completion.js +0 -76
  658. package/skills/wix-replatform/scripts/orchestration-preflight.js +0 -67
  659. package/skills/wix-replatform/scripts/orchestration-route.js +0 -77
  660. package/skills/wix-replatform/scripts/orchestration-state.js +0 -381
  661. package/skills/wix-replatform/scripts/read-back-gate.js +0 -175
  662. package/skills/wix-replatform/scripts/replatform-console.js +0 -146
  663. package/skills/wix-replatform/scripts/replatform.js +0 -436
  664. package/skills/wix-replatform/scripts/source-secrets.js +0 -77
  665. package/skills/wix-replatform/scripts/url-redirects.js +0 -286
  666. package/skills/wix-replatform/scripts/user-action.js +0 -58
  667. package/skills/wix-replatform/scripts/website-handoff-generate.js +0 -64
  668. package/skills/wix-replatform/scripts/wix-dashboard-url.js +0 -54
  669. package/skills/wix-replatform/scripts/wix-destination.js +0 -143
  670. package/skills/wix-replatform/scripts/wix-headless-scaffold.js +0 -762
  671. package/skills/wix-vibe-headless/SKILL.md +0 -183
  672. package/skills/wix-vibe-headless/install/deploy.cjs +0 -221
  673. package/skills/wix-vibe-headless/install/pin-agents-md.cjs +0 -35
  674. package/skills/wix-vibe-headless/platforms/README.md +0 -26
  675. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +0 -127
  676. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +0 -121
  677. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +0 -87
  678. package/skills/wix-vibe-headless/platforms/base44.md +0 -148
  679. package/skills/wix-vibe-headless/platforms/generic.md +0 -132
  680. package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +0 -30
  681. package/skills/wix-vibe-headless/references/_shared/routing.md +0 -86
  682. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +0 -270
  683. package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +0 -43
  684. package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +0 -25
  685. package/skills/wix-vibe-headless/references/blog/app/components/PostGrid.jsx +0 -15
  686. package/skills/wix-vibe-headless/references/blog/app/context/TaxonomyContext.jsx +0 -35
  687. package/skills/wix-vibe-headless/references/blog/app/hooks/usePostDetail.js +0 -38
  688. package/skills/wix-vibe-headless/references/blog/app/pages/Blog.jsx +0 -40
  689. package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +0 -54
  690. package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +0 -65
  691. package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +0 -50
  692. package/skills/wix-vibe-headless/references/blog/app/rest/wix-blog.js +0 -217
  693. package/skills/wix-vibe-headless/references/blog/seed/SEED.md +0 -99
  694. package/skills/wix-vibe-headless/references/blog/seed/seed-blog.cjs +0 -269
  695. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +0 -342
  696. package/skills/wix-vibe-headless/references/bookings/app/components/BookingForm.jsx +0 -65
  697. package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +0 -67
  698. package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +0 -47
  699. package/skills/wix-vibe-headless/references/bookings/app/components/ServiceGrid.jsx +0 -13
  700. package/skills/wix-vibe-headless/references/bookings/app/components/SlotPicker.jsx +0 -126
  701. package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +0 -128
  702. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +0 -110
  703. package/skills/wix-vibe-headless/references/bookings/app/hooks/useServices.js +0 -58
  704. package/skills/wix-vibe-headless/references/bookings/app/lib/serviceFacts.js +0 -95
  705. package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +0 -94
  706. package/skills/wix-vibe-headless/references/bookings/app/pages/Services.jsx +0 -89
  707. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-checkout.js +0 -148
  708. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +0 -250
  709. package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +0 -300
  710. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +0 -154
  711. package/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.cjs +0 -333
  712. package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.cjs +0 -267
  713. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +0 -178
  714. package/skills/wix-vibe-headless/references/cms/app/collection.config.js +0 -31
  715. package/skills/wix-vibe-headless/references/cms/app/lib/wixImage.js +0 -18
  716. package/skills/wix-vibe-headless/references/cms/app/rest/wix-cms.js +0 -231
  717. package/skills/wix-vibe-headless/references/cms/seed/SEED.md +0 -145
  718. package/skills/wix-vibe-headless/references/donations/INSTRUCTIONS.md +0 -88
  719. package/skills/wix-vibe-headless/references/donations/app/rest/wix-donations.js +0 -112
  720. package/skills/wix-vibe-headless/references/donations/seed/SEED.md +0 -11
  721. package/skills/wix-vibe-headless/references/donations/tests/donations.test.mjs +0 -258
  722. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +0 -267
  723. package/skills/wix-vibe-headless/references/events/app/components/CategoryFilter.jsx +0 -23
  724. package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +0 -43
  725. package/skills/wix-vibe-headless/references/events/app/components/EventGrid.jsx +0 -13
  726. package/skills/wix-vibe-headless/references/events/app/components/EventRegistration.jsx +0 -29
  727. package/skills/wix-vibe-headless/references/events/app/components/RsvpForm.jsx +0 -57
  728. package/skills/wix-vibe-headless/references/events/app/components/TicketPicker.jsx +0 -78
  729. package/skills/wix-vibe-headless/references/events/app/hooks/useEventDetail.js +0 -27
  730. package/skills/wix-vibe-headless/references/events/app/hooks/useEventsList.js +0 -54
  731. package/skills/wix-vibe-headless/references/events/app/hooks/useRsvpForm.js +0 -40
  732. package/skills/wix-vibe-headless/references/events/app/hooks/useTicketing.js +0 -81
  733. package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +0 -48
  734. package/skills/wix-vibe-headless/references/events/app/pages/Events.jsx +0 -36
  735. package/skills/wix-vibe-headless/references/events/app/rest/wix-events-browse.js +0 -136
  736. package/skills/wix-vibe-headless/references/events/app/rest/wix-events-registration.js +0 -187
  737. package/skills/wix-vibe-headless/references/events/seed/SEED.md +0 -103
  738. package/skills/wix-vibe-headless/references/events/seed/seed-events.cjs +0 -251
  739. package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +0 -487
  740. package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +0 -425
  741. package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +0 -187
  742. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +0 -238
  743. package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +0 -108
  744. package/skills/wix-vibe-headless/references/forms/seed/SEED.md +0 -1836
  745. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +0 -328
  746. package/skills/wix-vibe-headless/references/members/app/components/LoginForm.jsx +0 -101
  747. package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +0 -33
  748. package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +0 -21
  749. package/skills/wix-vibe-headless/references/members/app/components/SocialButtons.jsx +0 -29
  750. package/skills/wix-vibe-headless/references/members/app/context/MemberContext.jsx +0 -54
  751. package/skills/wix-vibe-headless/references/members/app/hooks/useLoginForm.js +0 -77
  752. package/skills/wix-vibe-headless/references/members/app/pages/Account.jsx +0 -36
  753. package/skills/wix-vibe-headless/references/members/app/pages/Callback.jsx +0 -32
  754. package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +0 -35
  755. package/skills/wix-vibe-headless/references/members/app/rest/wix-members-auth.js +0 -390
  756. package/skills/wix-vibe-headless/references/members/install/Register.jsx +0 -22
  757. package/skills/wix-vibe-headless/references/members/seed/SEED.md +0 -21
  758. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +0 -270
  759. package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +0 -31
  760. package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionGrid.jsx +0 -15
  761. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +0 -39
  762. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectGrid.jsx +0 -15
  763. package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectMedia.jsx +0 -29
  764. package/skills/wix-vibe-headless/references/portfolio/app/hooks/useCollectionProjects.js +0 -35
  765. package/skills/wix-vibe-headless/references/portfolio/app/hooks/usePortfolioGallery.js +0 -27
  766. package/skills/wix-vibe-headless/references/portfolio/app/hooks/useProjectDetail.js +0 -25
  767. package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +0 -39
  768. package/skills/wix-vibe-headless/references/portfolio/app/pages/Portfolio.jsx +0 -27
  769. package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +0 -60
  770. package/skills/wix-vibe-headless/references/portfolio/app/rest/wix-portfolio.js +0 -223
  771. package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +0 -90
  772. package/skills/wix-vibe-headless/references/portfolio/seed/seed-portfolio.cjs +0 -236
  773. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +0 -280
  774. package/skills/wix-vibe-headless/references/pricing-plans/app/components/OrderCard.jsx +0 -35
  775. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +0 -48
  776. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanGrid.jsx +0 -16
  777. package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanPrice.jsx +0 -35
  778. package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/useMyPlans.js +0 -25
  779. package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlanDetail.js +0 -28
  780. package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlans.js +0 -30
  781. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/MyPlans.jsx +0 -30
  782. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +0 -53
  783. package/skills/wix-vibe-headless/references/pricing-plans/app/pages/Plans.jsx +0 -28
  784. package/skills/wix-vibe-headless/references/pricing-plans/app/rest/wix-pricing-plans.js +0 -157
  785. package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +0 -82
  786. package/skills/wix-vibe-headless/references/pricing-plans/seed/seed-pricing-plans.cjs +0 -251
  787. package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +0 -13
  788. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +0 -290
  789. package/skills/wix-vibe-headless/references/restaurants/app/components/ItemDialog.jsx +0 -81
  790. package/skills/wix-vibe-headless/references/restaurants/app/components/MenuItemCard.jsx +0 -71
  791. package/skills/wix-vibe-headless/references/restaurants/app/components/MenuList.jsx +0 -43
  792. package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartButton.jsx +0 -21
  793. package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +0 -72
  794. package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +0 -66
  795. package/skills/wix-vibe-headless/references/restaurants/app/hooks/useItemOrder.js +0 -32
  796. package/skills/wix-vibe-headless/references/restaurants/app/hooks/useReservation.js +0 -73
  797. package/skills/wix-vibe-headless/references/restaurants/app/pages/Menu.jsx +0 -28
  798. package/skills/wix-vibe-headless/references/restaurants/app/pages/Reservations.jsx +0 -102
  799. package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-menu.js +0 -224
  800. package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-ordering.js +0 -218
  801. package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-reservations.js +0 -107
  802. package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +0 -176
  803. package/skills/wix-vibe-headless/references/restaurants/seed/seed-restaurants.cjs +0 -452
  804. package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +0 -56
  805. package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +0 -9
  806. package/skills/wix-vibe-headless/references/shared/app/rest/wix-client.js +0 -185
  807. package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +0 -8
  808. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -471
  809. package/skills/wix-vibe-headless/references/storefront/app/components/CartButton.jsx +0 -20
  810. package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +0 -167
  811. package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +0 -83
  812. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +0 -104
  813. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +0 -163
  814. package/skills/wix-vibe-headless/references/storefront/app/hooks/useShop.js +0 -83
  815. package/skills/wix-vibe-headless/references/storefront/app/hooks/useVariantOptions.js +0 -76
  816. package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +0 -64
  817. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +0 -176
  818. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +0 -218
  819. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +0 -174
  820. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +0 -546
@@ -1,2461 +0,0 @@
1
- #!/usr/bin/env node
2
- import path from "node:path";
3
- import {
4
- DEFAULT_VIEWPORTS,
5
- classifyUrl,
6
- docsDir,
7
- ensureDir,
8
- extractLinks,
9
- fetchText,
10
- normalizeUrl,
11
- parseArgs,
12
- resolveOutputDir,
13
- slugForUrl,
14
- writeJson,
15
- } from "./lib/common.mjs";
16
- import { loadPlaywrightFromContext, resolveBrowserToolingContext } from "./lib/browser-tooling.mjs";
17
- import { extractSeo, extractSectionsFromHtml, inferTokensFromHtml } from "./lib/html-extract.mjs";
18
-
19
- async function main() {
20
- const args = parseArgs();
21
- const url = normalizeUrl(args._[0] || args.url).toString();
22
- const outputDir = resolveOutputDir(url, args.out);
23
- const page = await extractPage(url, { outputDir, screenshots: args.screenshots !== "false" });
24
- const pageDir = path.join(docsDir(outputDir), "pages");
25
- await writeJson(path.join(pageDir, `${slugForUrl(url)}.json`), page);
26
- if (args.json) process.stdout.write(`${JSON.stringify(page, null, 2)}\n`);
27
- }
28
-
29
- export async function extractPage(url, options = {}) {
30
- try {
31
- return await extractWithPlaywright(url, options);
32
- } catch (error) {
33
- throw new Error(
34
- `Browser extraction failed for ${url}. The site clone skill now requires Playwright-based extraction instead of HTML fallback because shared chrome, first-viewport evidence, and repeater detection depend on a real browser. Details: ${error.message}`,
35
- );
36
- }
37
- }
38
-
39
- async function extractWithPlaywright(url, { outputDir, screenshots = true, browserTooling, screenshotDir: suppliedScreenshotDir, screenshotPrefix = "source" } = {}) {
40
- const toolingContext = browserTooling || await resolveBrowserToolingContext({ startDir: process.cwd() });
41
- const playwright = await loadPlaywrightFromContext(toolingContext);
42
- const browser = await playwright.chromium.launch({ headless: true });
43
- const page = await browser.newPage();
44
- const screenshotMap = {};
45
- try {
46
- const htmlByViewport = {};
47
- const navigationProfiles = [];
48
- const responsiveTextGeometry = {};
49
- for (const viewport of DEFAULT_VIEWPORTS) {
50
- await page.setViewportSize({ width: viewport.width, height: viewport.height });
51
- const navigation = await navigateWithAdaptiveProfile(page, url);
52
- navigationProfiles.push({ viewport: viewport.name, ...navigation });
53
- responsiveTextGeometry[viewport.name] = await captureImportantTextGeometry(page);
54
- if (screenshots && outputDir) {
55
- const screenshotDir = suppliedScreenshotDir || path.join(docsDir(outputDir), "screenshots");
56
- await ensureDir(screenshotDir);
57
- const screenshotPath = path.join(screenshotDir, `${screenshotPrefix}-${slugForUrl(url)}-${viewport.name}.png`);
58
- await page.screenshot({ path: screenshotPath, fullPage: true });
59
- screenshotMap[viewport.name] = screenshotPath;
60
- }
61
- htmlByViewport[viewport.name] = await page.content();
62
- }
63
- const html = htmlByViewport.desktop || Object.values(htmlByViewport)[0];
64
- const seo = extractSeo(html, url);
65
- const desktopViewport = DEFAULT_VIEWPORTS.find((viewport) => viewport.name === "desktop") || DEFAULT_VIEWPORTS[0];
66
- await page.setViewportSize({ width: desktopViewport.width, height: desktopViewport.height });
67
- const desktopNavigation = await navigateWithAdaptiveProfile(page, url);
68
- const repeaterPlan = await detectRepeaterSignals(page);
69
- const browserData = await page.evaluate(async (repeaterPlan) => {
70
- const cleanText = (value) => String(value || "").replace(/\s+/g, " ").trim();
71
- const nonContentSelector = "script,style,noscript,template";
72
- const semanticText = (node) => {
73
- if (!node || node.nodeType !== Node.ELEMENT_NODE || node.matches(nonContentSelector)) return "";
74
- const clone = node.cloneNode(true);
75
- clone.querySelectorAll(nonContentSelector).forEach((child) => child.remove());
76
- return cleanText(clone.textContent);
77
- };
78
- const renderedText = (node) => {
79
- if (!node || node.nodeType !== Node.ELEMENT_NODE || node.matches(nonContentSelector)) return "";
80
- const excludedSelector = `${nonContentSelector},[hidden],[inert],[aria-hidden='true' i]`;
81
- const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
82
- const values = [];
83
- for (let textNode = walker.nextNode(); textNode; textNode = walker.nextNode()) {
84
- if (!cleanText(textNode.nodeValue)) continue;
85
- const parent = textNode.parentElement;
86
- if (!parent || parent.closest(excludedSelector)) continue;
87
- let current = parent;
88
- let excluded = false;
89
- while (current) {
90
- const styles = getComputedStyle(current);
91
- if (styles.display === "none" || styles.visibility === "hidden" || Number(styles.opacity || 1) <= 0) {
92
- excluded = true;
93
- break;
94
- }
95
- if (current === node) break;
96
- current = current.parentElement;
97
- }
98
- if (!excluded) values.push(textNode.nodeValue);
99
- }
100
- return cleanText(values.join(" "));
101
- };
102
- const rectFor = (node) => {
103
- const rect = node.getBoundingClientRect();
104
- return { x: rect.x, y: rect.y, width: rect.width, height: rect.height, top: rect.top, bottom: rect.bottom };
105
- };
106
- const isRawVisible = (node) => {
107
- const rect = node.getBoundingClientRect();
108
- const styles = getComputedStyle(node);
109
- return rect.width > 0 && rect.height > 0 && styles.display !== "none" && styles.visibility !== "hidden" && Number(styles.opacity || 1) > 0;
110
- };
111
- const visibleCodeTexts = (node) => Array.from(node?.querySelectorAll?.("pre,code") || [])
112
- .filter((candidate) => isRawVisible(candidate))
113
- .map((candidate) => renderedText(candidate))
114
- .filter(Boolean)
115
- .filter((value, index, values) => values.indexOf(value) === index)
116
- .slice(0, 12);
117
- const consentSelectors = [
118
- "#onetrust-banner-sdk",
119
- "#onetrust-consent-sdk",
120
- "[class*='onetrust' i]",
121
- "[id*='cookie-banner' i]",
122
- "[class*='cookie-banner' i]",
123
- "[id*='consent-banner' i]",
124
- "[class*='consent-banner' i]",
125
- "[role='dialog']",
126
- ].join(",");
127
- const consentCandidates = Array.from(document.querySelectorAll(consentSelectors)).filter(isRawVisible).filter((node) => {
128
- const text = renderedText(node).toLowerCase();
129
- const identity = `${node.id || ""} ${node.className || ""} ${node.getAttribute("aria-label") || ""}`.toLowerCase();
130
- const controls = Array.from(node.querySelectorAll("button,a,[role='button']")).map((control) => renderedText(control)).join(" ").toLowerCase();
131
- const style = getComputedStyle(node);
132
- const rect = node.getBoundingClientRect();
133
- const consentSignal = /cookie|consent|privacy preferences|tracking preferences/.test(`${identity} ${text}`);
134
- const actionSignal = /accept|reject|decline|customi[sz]e|preferences|allow all/.test(controls);
135
- const overlaySignal = style.position === "fixed" || style.position === "sticky" || node.getAttribute("role") === "dialog" || rect.width >= window.innerWidth * 0.55;
136
- return /onetrust|cookiebot|quantcast/.test(identity) || consentSignal && actionSignal && overlaySignal;
137
- });
138
- const consentRoots = consentCandidates.filter((node, index, all) => !all.some((candidate, candidateIndex) => candidateIndex !== index && candidate.contains(node)));
139
- const ignoredSurfaces = consentRoots.map((node, index) => {
140
- node.setAttribute("data-rp-ignored-surface", "consent-management");
141
- const rect = node.getBoundingClientRect();
142
- const identity = `${node.id || ""} ${node.className || ""}`.toLowerCase();
143
- return {
144
- id: `ignored-surface-${String(index + 1).padStart(3, "0")}`,
145
- kind: "consent-management",
146
- provider: /onetrust|ot-sdk/.test(identity) ? "onetrust" : /cookiebot/.test(identity) ? "cookiebot" : /quantcast/.test(identity) ? "quantcast" : "unknown",
147
- creationPolicy: "ignore",
148
- reason: "Destination consent must be implemented as functional infrastructure, not cloned page content.",
149
- textFingerprint: renderedText(node).slice(0, 240),
150
- rect: { top: Math.round(rect.top), left: Math.round(rect.left), width: Math.round(rect.width), height: Math.round(rect.height) },
151
- domRef: {
152
- tag: node.tagName.toLowerCase(),
153
- id: node.id || "",
154
- classTokens: String(node.className || "").split(/\s+/).filter(Boolean).slice(0, 10),
155
- },
156
- };
157
- });
158
- const isVisible = (node) => {
159
- if (!(node instanceof Element) || node.closest("[data-rp-ignored-surface]")) return false;
160
- return isRawVisible(node);
161
- };
162
- const headerSelectors = "header,[role='banner'],#header,.header,.site-header,.main-header,.masthead,.sticky-header";
163
- const navSelectors = "nav,[role='navigation'],.menu,.nav,.navbar,.navigation";
164
- const visualAssetSelectors = "img,svg";
165
-
166
- function pickHeader() {
167
- const candidates = Array.from(document.querySelectorAll(headerSelectors))
168
- .filter(isVisible)
169
- .map((node) => ({ node, rect: node.getBoundingClientRect(), styles: getComputedStyle(node) }))
170
- .filter(({ rect }) => rect.bottom > -8 && rect.top < Math.max(window.innerHeight * 0.55, 260));
171
- candidates.sort((a, b) => {
172
- const aTop = Math.abs(a.rect.top);
173
- const bTop = Math.abs(b.rect.top);
174
- if (aTop !== bTop) return aTop - bTop;
175
- return b.rect.height - a.rect.height;
176
- });
177
- return candidates[0] || null;
178
- }
179
-
180
- function visualAssetRecord(node, context = "page") {
181
- if (!(node instanceof Element)) return null;
182
- const rect = node.getBoundingClientRect();
183
- const styles = getComputedStyle(node);
184
- const tag = node.tagName.toLowerCase();
185
- const use = tag === "svg" ? node.querySelector("use") : null;
186
- const useHref = use?.getAttribute("href") || use?.getAttribute("xlink:href") || "";
187
- const sourceUrl = tag === "img" ? (node.currentSrc || node.src || "") : useHref;
188
- const accessibleName = cleanText(node.getAttribute("aria-label") || node.getAttribute("title") || node.getAttribute("alt") || node.closest("a,button")?.getAttribute("aria-label") || "");
189
- const identity = `${node.id || ""} ${node.className?.baseVal || node.className || ""} ${sourceUrl} ${accessibleName}`.toLowerCase();
190
- if (/cookielaw|onetrust|ot-sdk|cookiebot|quantcast/.test(identity)) return null;
191
- const owner = node.closest("header,footer,button,a,[role='button']");
192
- const ownerIdentity = `${owner?.className || ""} ${owner?.id || ""} ${owner?.getAttribute?.("aria-label") || ""} ${owner ? semanticText(owner) : ""}`.toLowerCase();
193
- const homeLink = node.closest("a")?.href && new URL(node.closest("a").href, location.href).pathname.replace(/\/+$/, "") === "";
194
- const social = /linkedin|instagram|facebook|twitter|youtube|tiktok|x\.com|social/.test(`${identity} ${ownerIdentity}`);
195
- const looksLikeLogo = /logo|wordmark|brandmark|brand-mark/.test(identity)
196
- || (!social && homeLink && (rect.width >= 72 || Number(node.getAttribute("width")) >= 72))
197
- || (!social && rect.width >= 120 && rect.width >= rect.height * 1.8 && /header|footer/.test(context));
198
- const kind = looksLikeLogo ? "logo" : "icon";
199
- const fragmentId = useHref.startsWith("#") ? useHref.slice(1) : "";
200
- const symbol = fragmentId ? document.getElementById(fragmentId) : null;
201
- const variantText = `${identity} ${context}`;
202
- const variant = /footer/.test(variantText) ? "footer"
203
- : /solid/.test(variantText) ? "solid"
204
- : /dark|black/.test(variantText) ? "dark"
205
- : /light|white/.test(variantText) ? "light"
206
- : /mobile|menu|drawer/.test(variantText) ? "menu"
207
- : "default";
208
- return {
209
- kind,
210
- context,
211
- variant,
212
- sourceType: tag === "img" ? (/\.svg(?:$|[?#])/i.test(sourceUrl) ? "external-svg" : "image") : useHref ? "svg-sprite-use" : "inline-svg",
213
- sourceUrl,
214
- useHref,
215
- accessibleName,
216
- visible: isRawVisible(node),
217
- renderedSize: { width: Math.round(rect.width * 100) / 100, height: Math.round(rect.height * 100) / 100 },
218
- intrinsicSize: {
219
- width: node.getAttribute("width") || "",
220
- height: node.getAttribute("height") || "",
221
- viewBox: node.getAttribute("viewBox") || symbol?.getAttribute?.("viewBox") || "",
222
- },
223
- presentation: {
224
- color: styles.color,
225
- fill: styles.fill,
226
- stroke: styles.stroke,
227
- },
228
- svgMarkup: tag === "svg" ? node.outerHTML.slice(0, 24000) : "",
229
- symbolMarkup: symbol?.outerHTML?.slice(0, 24000) || "",
230
- domRef: {
231
- tag,
232
- id: node.id || "",
233
- classTokens: String(node.className?.baseVal || node.className || "").split(/\s+/).filter(Boolean).slice(0, 10),
234
- },
235
- };
236
- }
237
-
238
- function collectVisualAssets() {
239
- const records = [];
240
- const seen = new Set();
241
- const scopes = [
242
- ...Array.from(document.querySelectorAll("header,[role='banner']")).map((scope) => ({ scope, context: "header" })),
243
- ...Array.from(document.querySelectorAll("footer,[role='contentinfo']")).map((scope) => ({ scope, context: "footer" })),
244
- ...Array.from(document.querySelectorAll("button,[role='button'],a[href]")).map((scope) => ({ scope, context: scope.closest("footer") ? "footer-control" : scope.closest("header") ? "header-control" : "control" })),
245
- ];
246
- for (const { scope, context } of scopes) {
247
- const nodes = scope.matches?.(visualAssetSelectors) ? [scope] : Array.from(scope.querySelectorAll(visualAssetSelectors));
248
- for (const node of nodes) {
249
- if (node.closest("[data-rp-ignored-surface]")) continue;
250
- const record = visualAssetRecord(node, context);
251
- if (!record) continue;
252
- if (record.kind === "icon" && !record.visible && !record.useHref && !record.sourceUrl) continue;
253
- const key = `${record.kind}\u0000${record.sourceType}\u0000${record.sourceUrl}\u0000${record.domRef.id}\u0000${record.domRef.classTokens.join(".")}\u0000${context}`;
254
- if (seen.has(key)) continue;
255
- seen.add(key);
256
- records.push(record);
257
- if (records.length >= 120) return records;
258
- }
259
- }
260
- return records;
261
- }
262
-
263
- function headerVariant(name) {
264
- const candidate = pickHeader();
265
- if (!candidate) return { name, position: "unknown", height: 0, visibleAtScrollY: window.scrollY, visible: false };
266
- const { node, rect, styles } = candidate;
267
- const logo = Array.from(node.querySelectorAll(visualAssetSelectors))
268
- .map((asset) => ({ asset, record: visualAssetRecord(asset, `header-${name}`) }))
269
- .find(({ record }) => record?.kind === "logo") || null;
270
- const logoNode = logo?.asset || null;
271
- const logoRect = logoNode?.getBoundingClientRect();
272
- return {
273
- name,
274
- position: styles.position || "static",
275
- height: Math.round(rect.height),
276
- visibleAtScrollY: Math.round(window.scrollY),
277
- visible: true,
278
- rect: rectFor(node),
279
- logo: logoNode && logoRect ? {
280
- ...logo.record,
281
- sourceUrl: logo.record.sourceUrl,
282
- alt: logoNode.getAttribute("alt") || logo.record.accessibleName || "",
283
- renderedWidth: Math.round(logoRect.width),
284
- renderedHeight: Math.round(logoRect.height),
285
- } : null,
286
- };
287
- }
288
-
289
- function linkRecord(anchor) {
290
- return {
291
- label: semanticText(anchor).slice(0, 120),
292
- href: anchor.href || anchor.getAttribute("href") || "",
293
- };
294
- }
295
-
296
- function isSameOriginHref(href) {
297
- try {
298
- return new URL(href, window.location.href).origin === window.location.origin;
299
- } catch {
300
- return false;
301
- }
302
- }
303
-
304
- function navScore(scope, headerNode) {
305
- const anchors = Array.from(scope.querySelectorAll("a[href]")).filter(isVisible);
306
- if (!anchors.length) return Number.NEGATIVE_INFINITY;
307
- const records = anchors.map(linkRecord);
308
- const sameOriginCount = records.filter((item) => isSameOriginHref(item.href)).length;
309
- const nonEmptyLabelCount = records.filter((item) => item.label).length;
310
- const longLabelCount = records.filter((item) => item.label.length >= 2).length;
311
- const externalCount = records.length - sameOriginCount;
312
- const socialCount = anchors.filter((anchor) => {
313
- const text = semanticText(anchor).toLowerCase();
314
- const classText = `${anchor.className || ""} ${anchor.parentElement?.className || ""}`.toLowerCase();
315
- const href = String(anchor.href || "").toLowerCase();
316
- return /facebook|instagram|pinterest|youtube|tiktok|whatsapp|social|icon-/.test(`${text} ${classText} ${href}`);
317
- }).length;
318
- const topLevelListItems = Array.from(scope.querySelectorAll(":scope > ul > li, :scope > ol > li, :scope > li"))
319
- .filter(isVisible);
320
- const nestedListItems = Array.from(scope.querySelectorAll("li li")).filter(isVisible);
321
- const uniqueHrefs = new Set(records.map((item) => item.href)).size;
322
- const inHeader = Boolean(headerNode && headerNode.contains(scope));
323
- const ariaLabel = cleanText(scope.getAttribute("aria-label") || "").toLowerCase();
324
- const classText = `${scope.className || ""} ${scope.id || ""}`.toLowerCase();
325
- const textSample = semanticText(scope).toLowerCase();
326
-
327
- let score = 0;
328
- score += sameOriginCount * 10;
329
- score += nonEmptyLabelCount * 6;
330
- score += longLabelCount * 3;
331
- score += Math.min(topLevelListItems.length, 12) * 8;
332
- score += Math.min(nestedListItems.length, 24) * 3;
333
- score += Math.min(uniqueHrefs, 20) * 2;
334
- if (inHeader) score += 30;
335
- if (/main|primary|menu|navigation/.test(`${ariaLabel} ${classText}`)) score += 20;
336
- if (/social/.test(`${ariaLabel} ${classText}`)) score -= 40;
337
- if (sameOriginCount <= 1 && externalCount > 0) score -= 35;
338
- score -= externalCount * 6;
339
- score -= socialCount * 25;
340
- if (records.length <= 2 && sameOriginCount === 0) score -= 50;
341
- if (!topLevelListItems.length && records.length <= 3) score -= 15;
342
- if (/facebook|instagram/.test(textSample) && sameOriginCount === 0) score -= 50;
343
- return score;
344
- }
345
-
346
- function navigationItems() {
347
- const scopes = Array.from(document.querySelectorAll(`header ${navSelectors}, ${navSelectors}`)).filter(isVisible);
348
- const headerNode = pickHeader()?.node || null;
349
- const scoredScopes = scopes
350
- .map((scope) => ({ scope, score: navScore(scope, headerNode) }))
351
- .sort((a, b) => b.score - a.score);
352
- const scope = scoredScopes[0]?.scope;
353
- if (!scope) return [];
354
- const listItems = Array.from(scope.querySelectorAll("li")).filter((item) => {
355
- const parentLi = item.parentElement?.closest("li");
356
- return !parentLi || !scope.contains(parentLi);
357
- });
358
- const items = listItems.length ? listItems.map((item) => {
359
- const anchor = Array.from(item.children).find((child) => child.matches?.("a[href]")) || item.querySelector("a[href]");
360
- if (!anchor) return null;
361
- const children = Array.from(item.querySelectorAll("li a[href]"))
362
- .filter((childAnchor) => childAnchor !== anchor)
363
- .map(linkRecord)
364
- .filter((child) => child.label || child.href);
365
- return { ...linkRecord(anchor), children: dedupeNav(children).slice(0, 24) };
366
- }) : Array.from(scope.querySelectorAll("a[href]")).map((anchor) => ({ ...linkRecord(anchor), children: [] }));
367
- return dedupeNav(items.filter((item) => item && (item.label || item.href))).slice(0, 40);
368
- }
369
-
370
- function dedupeNav(items) {
371
- const seen = new Set();
372
- return items.filter((item) => {
373
- const key = `${item.label}\u0000${item.href}`;
374
- if (seen.has(key)) return false;
375
- seen.add(key);
376
- return true;
377
- });
378
- }
379
-
380
- function firstViewportText() {
381
- const nodes = Array.from(document.querySelectorAll("main *:not(script):not(style), body > *:not(header):not(nav):not(footer):not(script):not(style)"));
382
- const texts = [];
383
- const seen = new Set();
384
- for (const node of nodes) {
385
- if (!isVisible(node)) continue;
386
- if (node.closest("header,nav,footer")) continue;
387
- const rect = node.getBoundingClientRect();
388
- if (rect.bottom < 0 || rect.top > window.innerHeight) continue;
389
- if (Array.from(node.children).some((child) => renderedText(child) === renderedText(node))) continue;
390
- const text = renderedText(node);
391
- if (!text || text.length < 2 || seen.has(text)) continue;
392
- seen.add(text);
393
- texts.push(text.slice(0, 180));
394
- if (texts.length >= 80) break;
395
- }
396
- return texts;
397
- }
398
-
399
- const initialHeader = headerVariant("initial");
400
- const navigation = navigationItems();
401
- const visibleText = firstViewportText();
402
- const scrollTarget = Math.max(260, Math.min(document.documentElement.scrollHeight - window.innerHeight, window.innerHeight * 0.75));
403
- window.scrollTo(0, scrollTarget);
404
- await new Promise((resolve) => setTimeout(resolve, 250));
405
- const scrolledHeader = headerVariant("scrolled");
406
- window.scrollTo(0, 0);
407
-
408
- function domPath(node, stopNode) {
409
- if (!node) return "";
410
- const parts = [];
411
- let current = node;
412
- while (current && current.nodeType === Node.ELEMENT_NODE && current !== stopNode && parts.length < 8) {
413
- const tag = current.tagName.toLowerCase();
414
- const id = current.id ? `#${current.id}` : "";
415
- const classTokens = String(current.className || "")
416
- .split(/\s+/)
417
- .filter(Boolean)
418
- .slice(0, 2)
419
- .map((token) => `.${token}`)
420
- .join("");
421
- const siblings = current.parentElement
422
- ? Array.from(current.parentElement.children).filter((sibling) => sibling.tagName === current.tagName)
423
- : [];
424
- const nthOfType = siblings.length > 1 ? `:nth-of-type(${siblings.indexOf(current) + 1})` : "";
425
- parts.unshift(`${tag}${id}${classTokens}${id ? "" : nthOfType}`);
426
- current = current.parentElement;
427
- }
428
- return parts.join(" > ");
429
- }
430
-
431
- function visibleChildren(node) {
432
- return Array.from(node?.children || []).filter((child) => isVisible(child));
433
- }
434
-
435
- function visibleHeadingText(node) {
436
- return Array.from(node.querySelectorAll("h1,h2,h3,h4"))
437
- .filter(isVisible)
438
- .map((heading) => renderedText(heading))
439
- .find(Boolean) || "";
440
- }
441
-
442
- function visibleHeadings(node) {
443
- return Array.from(node.querySelectorAll("h1,h2,h3,h4"))
444
- .filter(isVisible)
445
- .map((heading) => ({
446
- level: Number(heading.tagName.replace(/^H/i, "")) || null,
447
- text: renderedText(heading),
448
- }))
449
- .filter((heading) => heading.text);
450
- }
451
-
452
- function isTransparentColor(value) {
453
- const normalized = String(value || "").replace(/\s+/g, "").toLowerCase();
454
- return !normalized || normalized === "transparent" || normalized === "rgba(0,0,0,0)";
455
- }
456
-
457
- function backgroundInfo(node) {
458
- const styles = getComputedStyle(node);
459
- return {
460
- color: styles.backgroundColor,
461
- image: styles.backgroundImage && styles.backgroundImage !== "none" ? styles.backgroundImage : "",
462
- };
463
- }
464
-
465
- function roundedBox(rect) {
466
- return {
467
- top: Math.round(rect.top + window.scrollY),
468
- left: Math.round(rect.left),
469
- width: Math.round(rect.width),
470
- height: Math.round(rect.height),
471
- };
472
- }
473
-
474
- function normalizedBox(rect, sectionRect) {
475
- return {
476
- x: Number(((rect.left - sectionRect.left) / Math.max(sectionRect.width, 1)).toFixed(3)),
477
- y: Number(((rect.top - sectionRect.top) / Math.max(sectionRect.height, 1)).toFixed(3)),
478
- width: Number((rect.width / Math.max(sectionRect.width, 1)).toFixed(3)),
479
- height: Number((rect.height / Math.max(sectionRect.height, 1)).toFixed(3)),
480
- };
481
- }
482
-
483
- function textGeometry(node, rect) {
484
- const styles = getComputedStyle(node);
485
- const lineTops = [];
486
- const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT, {
487
- acceptNode(textNode) {
488
- return cleanText(textNode.nodeValue).length ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
489
- },
490
- });
491
- for (let textNode = walker.nextNode(); textNode; textNode = walker.nextNode()) {
492
- const range = document.createRange();
493
- range.selectNodeContents(textNode);
494
- for (const lineRect of range.getClientRects()) {
495
- if (lineRect.width > 0 && lineRect.height > 0 && !lineTops.some((top) => Math.abs(top - lineRect.top) <= 2)) lineTops.push(lineRect.top);
496
- }
497
- }
498
- const lineCount = Math.max(1, lineTops.length);
499
- const maxWidth = styles.maxWidth;
500
- return {
501
- inlineSize: Math.round(rect.width * 100) / 100,
502
- blockSize: Math.round(rect.height * 100) / 100,
503
- lineCount,
504
- wrapPolicy: lineCount === 1 ? "single-line" : styles.whiteSpace === "nowrap" ? "clipped-or-overflowing" : "wrapped",
505
- whiteSpace: styles.whiteSpace,
506
- maxWidth,
507
- minWidth: styles.minWidth,
508
- fontSize: styles.fontSize,
509
- lineHeight: styles.lineHeight,
510
- letterSpacing: styles.letterSpacing,
511
- };
512
- }
513
-
514
- function layoutRole(node) {
515
- const tag = node.tagName.toLowerCase();
516
- const role = node.getAttribute("role") || "";
517
- const classText = `${node.className || ""} ${node.id || ""}`.toLowerCase();
518
- const peerControls = Array.from(node.parentElement?.children || []).filter((candidate) => {
519
- const candidateTag = candidate.tagName?.toLowerCase?.();
520
- return candidateTag === "button" || candidate.getAttribute?.("role") === "tab";
521
- });
522
- const childControls = Array.from(node.children || []).filter((candidate) => {
523
- const candidateTag = candidate.tagName?.toLowerCase?.();
524
- return candidateTag === "button" || candidate.getAttribute?.("role") === "tab";
525
- });
526
- if (role === "tablist" || /\btabs?\b|tab-list|tablist/.test(classText) || childControls.length >= 3) return "tabs";
527
- if (role === "tab" || /tab__btn|tab-button/.test(classText) || (tag === "button" && peerControls.length >= 3)) return "tab";
528
- if (/^h[1-6]$/.test(tag)) return "heading";
529
- if (tag === "p" || tag === "blockquote") return "body-copy";
530
- if (tag === "video" || tag === "iframe" || tag === "picture" || tag === "img") return "media";
531
- if (tag === "button" || role === "button" || (tag === "a" && /button|cta|contact|learn|read|discover|explore/.test(classText))) return "action";
532
- if (tag === "nav" || role === "navigation") return "navigation";
533
- return "content";
534
- }
535
-
536
- const STYLEABLE_CATEGORY_PROPERTIES = {
537
- text: ["color", "font-family", "font-size", "font-style", "font-weight", "letter-spacing", "line-height", "text-align", "text-decoration", "text-transform", "white-space"],
538
- action: ["color", "background-color", "border-color", "border-radius", "border-style", "border-width", "box-shadow", "cursor", "min-height", "padding", "transition"],
539
- media: ["aspect-ratio", "border-radius", "height", "object-fit", "object-position", "opacity", "width"],
540
- container: ["align-items", "background-color", "background-image", "border-color", "border-radius", "box-shadow", "display", "gap", "grid-template-columns", "justify-content", "overflow", "padding", "position", "z-index"],
541
- };
542
-
543
- function styleableCategories(role) {
544
- if (["heading", "body-copy", "tab"].includes(role)) return role === "tab" ? ["text", "action"] : ["text"];
545
- if (role === "action") return ["text", "action"];
546
- if (["media", "background-media"].includes(role)) return ["media"];
547
- return ["container"];
548
- }
549
-
550
- function computedPropertyUnion(styles, categories) {
551
- const properties = [...new Set(categories.flatMap((category) => STYLEABLE_CATEGORY_PROPERTIES[category] || []))].sort();
552
- return Object.fromEntries(properties.map((property) => [property, styles.getPropertyValue(property)]));
553
- }
554
-
555
- function authoredCssContext(node, categories) {
556
- const allowed = new Set(categories.flatMap((category) => STYLEABLE_CATEGORY_PROPERTIES[category] || []));
557
- const matches = [];
558
- for (const sheet of Array.from(document.styleSheets).slice(0, 80)) {
559
- let rules;
560
- try { rules = Array.from(sheet.cssRules || []); } catch { continue; }
561
- for (const rule of rules.slice(0, 800)) {
562
- if (!rule.selectorText || !rule.style) continue;
563
- let matched = false;
564
- try { matched = node.matches(rule.selectorText); } catch { continue; }
565
- if (!matched) continue;
566
- const declarations = {};
567
- for (const property of Array.from(rule.style)) if (allowed.has(property)) declarations[property] = rule.style.getPropertyValue(property);
568
- if (Object.keys(declarations).length) matches.push({ selector: rule.selectorText, href: sheet.href || "inline", declarations });
569
- if (matches.length >= 20) return matches;
570
- }
571
- }
572
- return matches;
573
- }
574
-
575
- function collectLayoutEvidence(sectionRoot) {
576
- const sectionRect = sectionRoot.getBoundingClientRect();
577
- const sectionStyles = getComputedStyle(sectionRoot);
578
- const selector = [
579
- "h1", "h2", "h3", "h4", "h5", "h6", "p", "blockquote",
580
- "a[href]", "button", "[role='button']", "[role='tablist']", "[role='tab']",
581
- "nav", "[class*='tabs' i]", "[class*='tab-list' i]",
582
- "video", "iframe", "picture", "img",
583
- ].join(",");
584
- const semanticCandidates = Array.from(sectionRoot.querySelectorAll(selector));
585
- const repeatedControlGroups = [...new Set(Array.from(sectionRoot.querySelectorAll("button, [role='tab']"))
586
- .map((control) => control.parentElement)
587
- .filter(Boolean)
588
- .filter((parent) => Array.from(parent.children).filter((child) => child.tagName?.toLowerCase?.() === "button" || child.getAttribute?.("role") === "tab").length >= 3))];
589
- const regionCandidates = [...new Set([...semanticCandidates, ...repeatedControlGroups])]
590
- .filter(isVisible)
591
- .filter((node) => {
592
- const rect = node.getBoundingClientRect();
593
- if (rect.width < 20 || rect.height < 12) return false;
594
- if (["p", "blockquote"].includes(node.tagName.toLowerCase()) && renderedText(node).length < 16) return false;
595
- return true;
596
- });
597
- const regions = [];
598
- for (const node of regionCandidates) {
599
- const role = layoutRole(node);
600
- if (role === "tab" && regionCandidates.some((candidate) => candidate !== node && layoutRole(candidate) === "tabs" && candidate.contains(node))) continue;
601
- let rect = node.getBoundingClientRect();
602
- if (role === "tabs") {
603
- const tabRects = Array.from(node.querySelectorAll("[role='tab'], button, a"))
604
- .filter(isVisible)
605
- .filter((candidate) => layoutRole(candidate) === "tab")
606
- .map((candidate) => candidate.getBoundingClientRect());
607
- if (tabRects.length >= 2) {
608
- const left = Math.min(...tabRects.map((candidate) => candidate.left));
609
- const top = Math.min(...tabRects.map((candidate) => candidate.top));
610
- const right = Math.max(...tabRects.map((candidate) => candidate.right));
611
- const bottom = Math.max(...tabRects.map((candidate) => candidate.bottom));
612
- rect = { left, top, right, bottom, width: right - left, height: bottom - top };
613
- }
614
- }
615
- const styles = getComputedStyle(node);
616
- const categories = styleableCategories(role);
617
- regions.push({
618
- role,
619
- text: ["heading", "body-copy", "action", "tab", "tabs"].includes(role) ? renderedText(node).slice(0, 180) : "",
620
- rect: roundedBox(rect),
621
- normalizedRect: normalizedBox(rect, sectionRect),
622
- position: styles.position,
623
- display: styles.display,
624
- textAlign: styles.textAlign,
625
- zIndex: styles.zIndex,
626
- styleableCategories: categories,
627
- computedStyle: computedPropertyUnion(styles, categories),
628
- authoredCss: authoredCssContext(node, categories),
629
- ...(["heading", "body-copy", "action", "tab", "tabs"].includes(role) ? { textGeometry: textGeometry(node, rect) } : {}),
630
- domRef: {
631
- tag: node.tagName.toLowerCase(),
632
- id: node.id || "",
633
- classTokens: String(node.className || "").split(/\s+/).filter(Boolean).slice(0, 6),
634
- },
635
- });
636
- if (regions.length >= 36) break;
637
- }
638
-
639
- const layerCandidates = Array.from(sectionRoot.querySelectorAll("video, iframe, picture, img, [class*='background' i], [class*='overlay' i], [class*='curtain' i], [class*='mask' i], [class*='panel' i], [class*='frame' i], [class*='border' i]"))
640
- .filter(isVisible)
641
- .map((node) => ({ node, rect: node.getBoundingClientRect(), styles: getComputedStyle(node) }))
642
- .filter(({ rect, styles }) => {
643
- const areaRatio = (rect.width * rect.height) / Math.max(sectionRect.width * Math.min(sectionRect.height, window.innerHeight), 1);
644
- return areaRatio >= 0.18 || ["absolute", "fixed", "sticky"].includes(styles.position);
645
- })
646
- .slice(0, 20)
647
- .map(({ node, rect, styles }) => {
648
- const classText = String(node.className || "").toLowerCase();
649
- const kind = ["video", "iframe"].includes(node.tagName.toLowerCase()) ? "video" : ["picture", "img"].includes(node.tagName.toLowerCase()) ? "image" : "decorative-layer";
650
- const role = /partial.*border|border.*frame|\bframe\b/.test(classText) ? "partial-border-frame"
651
- : /copy.*panel|content.*panel/.test(classText) ? "copy-panel"
652
- : /overlay|curtain|mask|scrim/.test(classText) ? "overlay"
653
- : /background|\bbg\b/.test(classText) ? "background"
654
- : kind === "image" || kind === "video" ? "media" : "decoration";
655
- return {
656
- kind,
657
- role,
658
- rect: roundedBox(rect),
659
- normalizedRect: normalizedBox(rect, sectionRect),
660
- position: styles.position,
661
- zIndex: styles.zIndex,
662
- opacity: styles.opacity,
663
- objectFit: styles.objectFit,
664
- overflow: styles.overflow,
665
- background: backgroundInfo(node),
666
- src: node.currentSrc || node.src || "",
667
- classTokens: String(node.className || "").split(/\s+/).filter(Boolean).slice(0, 8),
668
- };
669
- });
670
-
671
- const pseudoLayers = ["::before", "::after"].map((pseudo) => {
672
- const styles = getComputedStyle(sectionRoot, pseudo);
673
- if (!styles || styles.content === "none" || (isTransparentColor(styles.backgroundColor) && (!styles.backgroundImage || styles.backgroundImage === "none"))) return null;
674
- return {
675
- kind: "pseudo-layer",
676
- pseudo,
677
- position: styles.position,
678
- zIndex: styles.zIndex,
679
- opacity: styles.opacity,
680
- background: {
681
- color: styles.backgroundColor,
682
- image: styles.backgroundImage && styles.backgroundImage !== "none" ? styles.backgroundImage : "",
683
- },
684
- };
685
- }).filter(Boolean);
686
-
687
- return {
688
- vocabularyVersion: 1,
689
- viewport: { width: window.innerWidth, height: window.innerHeight },
690
- canvas: {
691
- display: sectionStyles.display,
692
- position: sectionStyles.position,
693
- overflowX: sectionStyles.overflowX,
694
- overflowY: sectionStyles.overflowY,
695
- minHeight: sectionStyles.minHeight,
696
- background: backgroundInfo(sectionRoot),
697
- },
698
- regions,
699
- layers: [...layerCandidates, ...pseudoLayers],
700
- };
701
- }
702
-
703
- function nodeFingerprint(node) {
704
- const heading = visibleHeadingText(node);
705
- const textPrefix = renderedText(node).slice(0, 160);
706
- return {
707
- heading,
708
- textPrefix,
709
- imageCount: node.querySelectorAll("img").length,
710
- linkCount: node.querySelectorAll("a[href]").length,
711
- };
712
- }
713
-
714
- function findContentRoot() {
715
- const candidateDefs = [
716
- { selector: "main", label: "main" },
717
- { selector: "[role='main']", label: "role-main" },
718
- { selector: "article", label: "article" },
719
- { selector: "#primary", label: "primary-id" },
720
- { selector: ".content-area", label: "content-area" },
721
- { selector: ".site-main", label: "site-main" },
722
- { selector: "body", label: "body" },
723
- ];
724
- const candidates = [];
725
- for (const def of candidateDefs) {
726
- for (const node of Array.from(document.querySelectorAll(def.selector)).filter(isVisible).slice(0, 3)) {
727
- const rect = node.getBoundingClientRect();
728
- const headingCount = node.querySelectorAll("h1,h2,h3,h4").length;
729
- const imageCount = node.querySelectorAll("img").length;
730
- const linkCount = node.querySelectorAll("a[href]").length;
731
- let score = 0;
732
- score += 40;
733
- score += Math.min(rect.width / Math.max(window.innerWidth, 1), 1.2) * 35;
734
- score += Math.min(rect.height / Math.max(window.innerHeight, 1), 4) * 10;
735
- score += Math.min(headingCount, 8) * 4;
736
- score += Math.min(imageCount, 20) * 1.5;
737
- if (linkCount > 3) score += 5;
738
- if (def.label === "main") score += 20;
739
- else if (def.label === "role-main") score += 18;
740
- else if (def.label === "article") score += 10;
741
- else if (def.label === "body") score -= 30;
742
- if (rect.height > document.documentElement.scrollHeight * 0.9) score -= 25;
743
- candidates.push({ node, label: def.label, score });
744
- }
745
- }
746
- candidates.sort((a, b) => b.score - a.score);
747
- return candidates[0] || { node: document.body, label: "body", score: 0 };
748
- }
749
-
750
- function collectModuleStats(node, root) {
751
- const rect = node.getBoundingClientRect();
752
- const children = visibleChildren(node);
753
- const headings = visibleHeadings(node);
754
- const images = Array.from(node.querySelectorAll("img")).filter((img) => isVisible(img));
755
- const links = Array.from(node.querySelectorAll("a[href]")).filter((link) => isVisible(link));
756
- const buttons = Array.from(node.querySelectorAll("button")).filter((button) => isVisible(button));
757
- const paragraphs = Array.from(node.querySelectorAll("p, li, blockquote, figcaption")).filter((item) => isVisible(item));
758
- const childRects = children.map((child) => child.getBoundingClientRect());
759
- const rowCountEstimate = new Set(childRects.map((item) => Math.round(item.top / 24))).size;
760
- const background = backgroundInfo(node);
761
- const role = node.getAttribute("role") || "";
762
- const classText = `${node.className || ""} ${node.id || ""}`.toLowerCase();
763
- const ancestorText = [];
764
- let ancestor = node.parentElement;
765
- for (let depth = 0; ancestor && depth < 4; depth += 1) {
766
- ancestorText.push(`${ancestor.className || ""} ${ancestor.id || ""}`.toLowerCase());
767
- if (ancestor === root) break;
768
- ancestor = ancestor.parentElement;
769
- }
770
- const ancestryText = ancestorText.join(" ");
771
- const reviewPattern = /review|testimonial|verified customer|productreview|based on [\d,.\s]+ reviews|what our customers/i;
772
- const reviewLike = reviewPattern.test(classText)
773
- || headings.some((heading) => reviewPattern.test(heading.text))
774
- || reviewPattern.test(renderedText(node))
775
- || reviewPattern.test(ancestryText);
776
- const pricePattern = /\$\s?\d|\bprice\b|original price|current price|sale\b/i;
777
- const addToCartPattern = /add to cart|quantity|buy now|shop now/i;
778
- const productLike = pricePattern.test(renderedText(node))
779
- || addToCartPattern.test(renderedText(node))
780
- || /product|products|woocommerce|add-to-cart|price|shopify-buy/.test(`${classText} ${ancestryText}`);
781
- const reviewWidgetLike = /reviewsio|judge\.me|stamped|loox|yotpo|trustpilot|productreview/.test(`${classText} ${ancestryText}`);
782
- const galleryLike = /gallery|slider|embla|swiper|carousel|featured/.test(classText);
783
- const logoLikeChildren = images.filter((image) => {
784
- const imageRect = image.getBoundingClientRect();
785
- return imageRect.width <= 240 && imageRect.height <= 140;
786
- }).length;
787
- return {
788
- node,
789
- tag: node.tagName.toLowerCase(),
790
- role,
791
- rect,
792
- heading: headings[0]?.text || "",
793
- headings,
794
- text: renderedText(node).slice(0, 1800) || "",
795
- textLength: renderedText(node).length,
796
- visibleCodeTexts: visibleCodeTexts(node),
797
- imageCount: images.length,
798
- linkCount: links.length,
799
- buttonCount: buttons.length,
800
- paragraphCount: paragraphs.length,
801
- childCount: children.length,
802
- cardLikeChildren: children.filter((child) => {
803
- const childRect = child.getBoundingClientRect();
804
- return childRect.width >= 140 && childRect.height >= 90;
805
- }).length,
806
- rowCountEstimate,
807
- background,
808
- hasBackgroundChange: !isTransparentColor(background.color) || Boolean(background.image),
809
- reviewLike,
810
- reviewWidgetLike,
811
- productLike,
812
- galleryLike,
813
- logoLikeChildren,
814
- path: domPath(node, root?.parentElement || document.body.parentElement),
815
- parentPath: domPath(node.parentElement, root?.parentElement || document.body.parentElement),
816
- nthOfType: node.parentElement
817
- ? Array.from(node.parentElement.children).filter((sibling) => sibling.tagName === node.tagName).indexOf(node) + 1
818
- : 1,
819
- fingerprint: nodeFingerprint(node),
820
- };
821
- }
822
-
823
- function statsClassText(stats) {
824
- return `${stats.node?.className || ""} ${stats.node?.id || ""} ${stats.path || ""}`.toLowerCase();
825
- }
826
-
827
- function sectionCapabilities({ hasCta, hasMedia, repeatingItems = false, isCarouselLike = false }) {
828
- return { hasCta, hasMedia, repeatingItems, isCarouselLike };
829
- }
830
-
831
- const SECTION_DETECTORS = [
832
- {
833
- kind: "header",
834
- match: ({ stats, classText, hasCta }) => (stats.tag === "header" || /\bheader\b|masthead|top-bar|site-header/.test(classText))
835
- ? {
836
- kind: "header",
837
- variant: /top-bar/.test(classText) ? "top-bar" : "generic",
838
- capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1 }),
839
- }
840
- : null,
841
- },
842
- {
843
- kind: "footer",
844
- match: ({ stats, classText, hasCta }) => (stats.tag === "footer" || /\bfooter\b|site-info|copyright|privacy|terms|site-map/.test(classText))
845
- ? {
846
- kind: "footer",
847
- variant: "site-footer",
848
- capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1 }),
849
- }
850
- : null,
851
- },
852
- {
853
- kind: "hero",
854
- match: ({ stats, classText, hasCta, atTopOfPage, hasStrongHeroMedia }) => (
855
- hasStrongHeroMedia
856
- && hasCta
857
- && stats.rect.height >= Math.max(window.innerHeight * 0.22, 220)
858
- && (atTopOfPage || /\bhero\b|banner|masthead|billboard|home-section/.test(classText) || stats.headings.some((heading) => heading.level === 1))
859
- )
860
- ? {
861
- kind: "hero",
862
- variant: stats.galleryLike || stats.cardLikeChildren >= 2 ? "carousel-hero" : "generic-hero",
863
- capabilities: sectionCapabilities({ hasCta: true, hasMedia: true, repeatingItems: stats.cardLikeChildren >= 2, isCarouselLike: stats.galleryLike }),
864
- }
865
- : null,
866
- },
867
- {
868
- kind: "reviews",
869
- match: ({ stats, hasCta }) => (
870
- (stats.reviewWidgetLike || stats.reviewLike)
871
- && !(stats.productLike && !stats.reviewWidgetLike)
872
- && (stats.cardLikeChildren >= 1 || stats.imageCount >= 2 || stats.textLength >= 80)
873
- )
874
- ? {
875
- kind: "reviews",
876
- variant: stats.galleryLike || stats.cardLikeChildren >= 2 ? "review-carousel" : "review-list",
877
- capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1, repeatingItems: true, isCarouselLike: stats.galleryLike }),
878
- }
879
- : null,
880
- },
881
- {
882
- kind: "category-strip",
883
- match: ({ stats }) => (
884
- stats.imageCount === 0
885
- && stats.linkCount === 0
886
- && stats.buttonCount === 0
887
- && stats.paragraphCount >= 4
888
- && stats.textLength >= 40
889
- && stats.textLength <= 280
890
- )
891
- ? {
892
- kind: "category-strip",
893
- variant: "text-category-grid",
894
- capabilities: sectionCapabilities({ hasCta: false, hasMedia: false, repeatingItems: true }),
895
- }
896
- : null,
897
- },
898
- {
899
- kind: "card-collection",
900
- match: ({ stats, hasCta }) => (
901
- (stats.logoLikeChildren >= 4 && stats.imageCount >= 4 && stats.textLength < 420)
902
- || (stats.galleryLike && stats.imageCount >= 5 && stats.textLength < 180)
903
- )
904
- ? {
905
- kind: "card-collection",
906
- variant: "logo-gallery",
907
- capabilities: sectionCapabilities({ hasCta, hasMedia: true, repeatingItems: true, isCarouselLike: stats.galleryLike }),
908
- }
909
- : null,
910
- },
911
- {
912
- kind: "card-collection",
913
- match: ({ stats }) => (stats.productLike && (stats.cardLikeChildren >= 3 || stats.imageCount >= 4) && (stats.buttonCount >= 2 || stats.linkCount >= 6))
914
- ? {
915
- kind: "card-collection",
916
- variant: stats.galleryLike ? "product-carousel" : "product-grid",
917
- capabilities: sectionCapabilities({ hasCta: true, hasMedia: true, repeatingItems: true, isCarouselLike: stats.galleryLike }),
918
- }
919
- : null,
920
- },
921
- {
922
- kind: "card-collection",
923
- match: ({ stats, hasCta }) => (stats.cardLikeChildren >= 3 && (stats.imageCount >= 3 || stats.linkCount >= 3))
924
- ? {
925
- kind: "card-collection",
926
- variant: stats.galleryLike ? "card-carousel" : "card-grid",
927
- capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1, repeatingItems: true, isCarouselLike: stats.galleryLike }),
928
- }
929
- : null,
930
- },
931
- {
932
- kind: "text-media",
933
- match: ({ stats, classText, hasCta }) => (stats.imageCount >= 1 && stats.headings.length >= 1 && stats.paragraphCount >= 1)
934
- ? {
935
- kind: "text-media",
936
- variant: /has-media-on-the-right|media-right/.test(classText) ? "media-right" : "media-left",
937
- capabilities: sectionCapabilities({ hasCta, hasMedia: true }),
938
- }
939
- : null,
940
- },
941
- {
942
- kind: "stat-group",
943
- match: ({ stats }) => (stats.headings.length >= 3 && stats.imageCount === 0 && stats.linkCount === 0 && stats.buttonCount === 0)
944
- ? {
945
- kind: "stat-group",
946
- variant: "step-strip",
947
- capabilities: sectionCapabilities({ hasCta: false, hasMedia: false, repeatingItems: true }),
948
- }
949
- : null,
950
- },
951
- {
952
- kind: "promo-band",
953
- match: ({ stats, hasCta }) => (
954
- stats.imageCount <= 1
955
- && stats.textLength >= 40
956
- && stats.textLength <= 220
957
- && stats.paragraphCount <= 2
958
- && stats.headings.length <= 1
959
- && (stats.hasBackgroundChange || stats.imageCount >= 1)
960
- )
961
- ? {
962
- kind: "promo-band",
963
- variant: hasCta ? "value-prop-cta" : "value-prop-band",
964
- capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1 }),
965
- }
966
- : null,
967
- },
968
- {
969
- kind: "cta-strip",
970
- match: ({ stats, hasCta }) => (
971
- stats.imageCount === 0
972
- && hasCta
973
- && stats.textLength <= 220
974
- && stats.paragraphCount <= 1
975
- && stats.headings.length <= 2
976
- )
977
- ? {
978
- kind: "cta-strip",
979
- variant: "inline-cta-strip",
980
- capabilities: sectionCapabilities({ hasCta: true, hasMedia: false }),
981
- }
982
- : null,
983
- },
984
- {
985
- kind: "cta-strip",
986
- match: ({ stats, hasCta, atTopOfPage }) => ((stats.buttonCount + stats.linkCount) >= 2 && stats.imageCount >= 1 && stats.rect.height >= 120)
987
- ? {
988
- kind: "cta-strip",
989
- variant: atTopOfPage ? "promo-hero-strip" : "promo-strip",
990
- capabilities: sectionCapabilities({ hasCta: true, hasMedia: true }),
991
- }
992
- : null,
993
- },
994
- {
995
- kind: "cta-strip",
996
- match: ({ stats, hasCta }) => (stats.headings.length === 1 && stats.imageCount === 0 && stats.paragraphCount === 0)
997
- ? {
998
- kind: "cta-strip",
999
- variant: "centered-heading-strip",
1000
- capabilities: sectionCapabilities({ hasCta, hasMedia: false }),
1001
- }
1002
- : null,
1003
- },
1004
- {
1005
- kind: "rich-text",
1006
- match: ({ stats, hasCta }) => (stats.textLength >= 60 || stats.headings.length || stats.imageCount)
1007
- ? {
1008
- kind: "rich-text",
1009
- variant: "generic",
1010
- capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1 }),
1011
- }
1012
- : null,
1013
- },
1014
- ];
1015
-
1016
- function classifySection(stats) {
1017
- const classText = statsClassText(stats);
1018
- const atTopOfPage = (stats.rect.top + window.scrollY) < Math.max(window.innerHeight * 0.35, 320);
1019
- const hasCta = (stats.buttonCount + stats.linkCount) >= 1;
1020
- const hasStrongHeroMedia = stats.imageCount >= 1 || stats.hasBackgroundChange;
1021
- for (const detector of SECTION_DETECTORS) {
1022
- const matched = detector.match({ stats, classText, atTopOfPage, hasCta, hasStrongHeroMedia });
1023
- if (matched) return matched;
1024
- }
1025
- return { kind: "unknown", variant: "generic", capabilities: sectionCapabilities({ hasCta, hasMedia: stats.imageCount >= 1 }) };
1026
- }
1027
-
1028
- function sectionScore(stats, root) {
1029
- let score = 0;
1030
- score += 30;
1031
- score += Math.min(stats.rect.width / Math.max(root.getBoundingClientRect().width, 1), 1.2) * 30;
1032
- score += Math.min(stats.rect.height / Math.max(window.innerHeight, 1), 1.5) * 20;
1033
- score += Math.min(stats.headings.length, 4) * 10;
1034
- score += Math.min(stats.imageCount, 6) * 4;
1035
- score += Math.min(stats.cardLikeChildren, 6) * 5;
1036
- if (stats.hasBackgroundChange) score += 10;
1037
- if (stats.rect.width < root.getBoundingClientRect().width * 0.55) score -= 20;
1038
- if (stats.rect.height < 80) score -= 40;
1039
- if (stats.textLength < 15 && stats.imageCount === 0) score -= 60;
1040
- if (/nav|menu|breadcrumb|popup|modal|drawer|cookie/.test(`${stats.role} ${stats.path}`.toLowerCase())) score -= 35;
1041
- if (stats.tag === "main" || stats.tag === "article") score -= 20;
1042
- if (stats.rect.height > root.getBoundingClientRect().height * 0.8) score -= 50;
1043
- if (stats.childCount === 1 && stats.cardLikeChildren <= 1 && stats.headings.length <= 1 && !stats.hasBackgroundChange) score -= 40;
1044
- if (stats.textLength > 3200 && stats.rect.height > root.getBoundingClientRect().height * 0.55 && !stats.hasBackgroundChange) score -= 45;
1045
- return score;
1046
- }
1047
-
1048
- function isWrapperLike(stats, root) {
1049
- return (
1050
- stats.childCount <= 2
1051
- && stats.cardLikeChildren <= 1
1052
- && stats.headings.length <= 1
1053
- && !stats.hasBackgroundChange
1054
- && stats.rect.height > Math.max(window.innerHeight * 1.25, root.getBoundingClientRect().height * 0.45)
1055
- );
1056
- }
1057
-
1058
- function findSegmentationRoot(contentRoot) {
1059
- const warnings = [];
1060
- let current = contentRoot;
1061
- for (let depth = 0; depth < 10; depth += 1) {
1062
- const scopedContentChild = Array.from(current.children || []).find((child) => {
1063
- if (!isVisible(child)) return false;
1064
- const classText = `${child.className || ""} ${child.id || ""}`.toLowerCase();
1065
- return /single-content|entry-content|post-content|article-content|main-content/.test(classText);
1066
- });
1067
- if (scopedContentChild) {
1068
- const childStats = collectModuleStats(scopedContentChild, contentRoot);
1069
- current = scopedContentChild;
1070
- warnings.push(`segmentation-root-descended:${childStats.path}`);
1071
- continue;
1072
- }
1073
- const children = visibleChildren(current).filter((child) => {
1074
- const rect = child.getBoundingClientRect();
1075
- return rect.height >= 24 && (renderedText(child).length >= 20 || child.querySelector("img,svg,a[href],button") || rect.height >= 120);
1076
- });
1077
- if (!children.length) break;
1078
- if (children.length === 1) {
1079
- const childStats = collectModuleStats(children[0], contentRoot);
1080
- if (childStats.rect.width >= current.getBoundingClientRect().width * 0.72) {
1081
- current = children[0];
1082
- warnings.push(`segmentation-root-descended:${childStats.path}`);
1083
- continue;
1084
- }
1085
- }
1086
- const dominant = children.find((child) => {
1087
- const rect = child.getBoundingClientRect();
1088
- return rect.width >= current.getBoundingClientRect().width * 0.72 && rect.height >= current.getBoundingClientRect().height * 0.72;
1089
- });
1090
- if (children.length <= 3 && dominant) {
1091
- const dominantStats = collectModuleStats(dominant, contentRoot);
1092
- if (!dominantStats.hasBackgroundChange && dominantStats.cardLikeChildren <= 1) {
1093
- current = dominant;
1094
- warnings.push(`segmentation-root-descended:${dominantStats.path}`);
1095
- continue;
1096
- }
1097
- }
1098
- const dominantCandidate = children
1099
- .map((child) => ({ child, rect: child.getBoundingClientRect() }))
1100
- .sort((a, b) => b.rect.height - a.rect.height)[0];
1101
- if (dominantCandidate && children.length <= 5) {
1102
- const otherHeight = children
1103
- .filter((child) => child !== dominantCandidate.child)
1104
- .reduce((sum, child) => sum + child.getBoundingClientRect().height, 0);
1105
- const dominantClassText = `${dominantCandidate.child.className || ""} ${dominantCandidate.child.id || ""}`.toLowerCase();
1106
- if (dominantCandidate.rect.height >= current.getBoundingClientRect().height * 0.6 && otherHeight <= dominantCandidate.rect.height * 0.35) {
1107
- const dominantStats = collectModuleStats(dominantCandidate.child, contentRoot);
1108
- if (!dominantStats.hasBackgroundChange) {
1109
- current = dominantCandidate.child;
1110
- warnings.push(`segmentation-root-descended:${dominantStats.path}`);
1111
- continue;
1112
- }
1113
- }
1114
- if (
1115
- dominantCandidate.rect.width >= current.getBoundingClientRect().width * 0.68
1116
- && dominantCandidate.rect.height >= current.getBoundingClientRect().height * 0.45
1117
- && otherHeight <= dominantCandidate.rect.height * 0.8
1118
- && /content|entry|article|post|main/.test(dominantClassText)
1119
- ) {
1120
- const dominantStats = collectModuleStats(dominantCandidate.child, contentRoot);
1121
- current = dominantCandidate.child;
1122
- warnings.push(`segmentation-root-descended:${dominantStats.path}`);
1123
- continue;
1124
- }
1125
- }
1126
- break;
1127
- }
1128
- return { node: current, warnings };
1129
- }
1130
-
1131
- function isStructuralBand(stats, segmentationRoot) {
1132
- const lowerTag = stats.tag.toLowerCase();
1133
- if (/^h[1-6]$/.test(lowerTag) || lowerTag === "p") return false;
1134
- if (lowerTag === "div" && stats.rect.height < 100 && !stats.hasBackgroundChange && stats.imageCount === 0 && stats.cardLikeChildren === 0) return false;
1135
- if (stats.rect.width < segmentationRoot.getBoundingClientRect().width * 0.55 && stats.imageCount === 0 && stats.headings.length === 0) return false;
1136
- return true;
1137
- }
1138
-
1139
- function overlaps(a, b) {
1140
- const top = Math.max(a.rect.top, b.rect.top);
1141
- const bottom = Math.min(a.rect.top + a.rect.height, b.rect.top + b.rect.height);
1142
- const overlapHeight = Math.max(0, bottom - top);
1143
- return overlapHeight >= Math.min(a.rect.height, b.rect.height) * 0.6;
1144
- }
1145
-
1146
- function classifyModule(stats, sectionType = null) {
1147
- const classText = statsClassText(stats);
1148
- const text = String(stats.text || "").toLowerCase();
1149
- const hasCta = stats.linkCount + stats.buttonCount >= 1;
1150
- const sectionKey = sectionType ? `${sectionType.kind}:${sectionType.variant || "*"}` : "";
1151
- const moduleDetectors = {
1152
- "reviews:*": () => ({ kind: "review-card", variant: "customer-review", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } }),
1153
- "stat-group:*": () => ({ kind: "stat-item", variant: "step-or-stat", capabilities: { hasCta: false, hasMedia: false } }),
1154
- "category-strip:*": () => ({ kind: "text-block", variant: "category-item", capabilities: { hasCta, hasMedia: false } }),
1155
- "promo-band:*": () => (stats.imageCount >= 1 && stats.headings.length === 0 && stats.paragraphCount === 0
1156
- ? { kind: "media-item", variant: "promo-media", capabilities: { hasCta, hasMedia: true } }
1157
- : { kind: "text-block", variant: "promo-copy", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } }),
1158
- "cta-strip:*": () => ({ kind: "text-block", variant: hasCta ? "cta-copy" : "strip-copy", capabilities: { hasCta, hasMedia: false } }),
1159
- "text-media:*": () => {
1160
- if (stats.imageCount >= 1 && stats.paragraphCount === 0 && stats.headings.length === 0) return { kind: "media-item", variant: "image-tile", capabilities: { hasCta, hasMedia: true } };
1161
- if (stats.headings.length >= 1 || stats.paragraphCount >= 1) return { kind: "text-block", variant: "copy-block", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1162
- return null;
1163
- },
1164
- "card-collection:logo-gallery": () => ({ kind: "logo-item", variant: "brand-mark", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } }),
1165
- "card-collection:*": () => ({ kind: "card", variant: /product|price|add to cart|book rental|rent/.test(text) ? "product-card" : "content-card", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } }),
1166
- "footer:*": () => ({ kind: "link-group", variant: "footer-links", capabilities: { hasCta: stats.linkCount >= 1, hasMedia: false } }),
1167
- "hero:*": () => {
1168
- if (stats.headings.length >= 1 || stats.paragraphCount >= 1) return { kind: "text-block", variant: "hero-copy", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1169
- if (stats.imageCount >= 1) return { kind: "media-item", variant: "hero-media", capabilities: { hasCta, hasMedia: true } };
1170
- return null;
1171
- },
1172
- };
1173
- const specialized = (moduleDetectors[sectionKey] || moduleDetectors[`${sectionType?.kind}:*`])?.();
1174
- if (specialized) return specialized;
1175
- if (stats.reviewLike || /review|testimonial|verified customer/.test(`${classText} ${text}`)) return { kind: "review-card", variant: "customer-review", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1176
- if (stats.productLike) return { kind: "card", variant: "product-card", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1177
- if (stats.logoLikeChildren >= 1 || (stats.imageCount >= 1 && stats.textLength < 40 && stats.rect.width <= 260)) return { kind: "logo-item", variant: "brand-mark", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1178
- if (stats.imageCount >= 1 && stats.headings.length === 0 && stats.paragraphCount === 0) return { kind: "media-item", variant: "image-tile", capabilities: { hasCta, hasMedia: true } };
1179
- if (stats.headings.length >= 1 || stats.paragraphCount >= 1) return { kind: "text-block", variant: "copy-block", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1180
- return { kind: "unknown", variant: "generic", capabilities: { hasCta, hasMedia: stats.imageCount >= 1 } };
1181
- }
1182
-
1183
- function moduleSummary(stats, sectionRoot, sectionType = null) {
1184
- const moduleType = classifyModule(stats, sectionType);
1185
- return {
1186
- id: "",
1187
- kind: moduleType.kind,
1188
- variant: moduleType.variant,
1189
- capabilities: moduleType.capabilities,
1190
- heading: stats.heading,
1191
- visibleCodeTexts: stats.visibleCodeTexts,
1192
- order: 0,
1193
- confidence: Number(Math.max(0.2, Math.min(0.95, 0.45 + ((stats.imageCount + stats.headings.length + stats.cardLikeChildren) * 0.04))).toFixed(2)),
1194
- rect: {
1195
- top: Math.round(stats.rect.top + window.scrollY),
1196
- left: Math.round(stats.rect.left),
1197
- width: Math.round(stats.rect.width),
1198
- height: Math.round(stats.rect.height),
1199
- },
1200
- counts: {
1201
- headings: stats.headings.length,
1202
- paragraphs: stats.paragraphCount,
1203
- images: stats.imageCount,
1204
- links: stats.linkCount,
1205
- buttons: stats.buttonCount,
1206
- },
1207
- domRef: {
1208
- tag: stats.tag,
1209
- id: stats.node.id || "",
1210
- classTokens: String(stats.node.className || "").split(/\s+/).filter(Boolean).slice(0, 6),
1211
- path: domPath(stats.node, sectionRoot.parentElement || document.body.parentElement),
1212
- parentPath: domPath(stats.node.parentElement, sectionRoot.parentElement || document.body.parentElement),
1213
- nthOfType: stats.nthOfType,
1214
- fingerprint: stats.fingerprint,
1215
- },
1216
- warnings: [],
1217
- };
1218
- }
1219
-
1220
- function moduleCandidatesFrom(containerRoot, sectionRoot, filters = {}) {
1221
- const minHeight = filters.minHeight || 60;
1222
- const minWidth = filters.minWidth || Math.min(sectionRoot.getBoundingClientRect().width * 0.22, 220);
1223
- return visibleChildren(containerRoot)
1224
- .map((child) => collectModuleStats(child, sectionRoot))
1225
- .filter((stats) => stats.rect.height >= minHeight)
1226
- .filter((stats) => stats.rect.width >= minWidth || stats.imageCount >= 1 || stats.headings.length >= 1)
1227
- .filter((stats) => stats.textLength >= 10 || stats.imageCount >= 1)
1228
- .slice(0, 36);
1229
- }
1230
-
1231
- function resolveRepeatingContainer(sectionRoot) {
1232
- let current = sectionRoot;
1233
- for (let depth = 0; depth < 3; depth += 1) {
1234
- const children = visibleChildren(current);
1235
- if (!children.length) return current;
1236
- const repeatedChild = children.find((child) => {
1237
- const classText = `${child.className || ""} ${child.id || ""}`.toLowerCase();
1238
- return /slider|carousel|embla|swiper|viewport|container|grid|gallery|reviews|products|list/.test(classText);
1239
- });
1240
- if (repeatedChild) {
1241
- current = repeatedChild;
1242
- continue;
1243
- }
1244
- if (children.length === 1) {
1245
- current = children[0];
1246
- continue;
1247
- }
1248
- return current;
1249
- }
1250
- return current;
1251
- }
1252
-
1253
- function extractRepeatingItemModules(sectionRoot, sectionType) {
1254
- const container = resolveRepeatingContainer(sectionRoot);
1255
- const children = moduleCandidatesFrom(container, sectionRoot, { minHeight: 80, minWidth: 120 });
1256
- const accepted = [];
1257
- for (const stats of children) {
1258
- if (stats.rect.height > sectionRoot.getBoundingClientRect().height * 0.95 && stats.cardLikeChildren <= 1) continue;
1259
- if (accepted.some((existing) => overlaps(existing, stats))) continue;
1260
- accepted.push(stats);
1261
- }
1262
- return accepted.slice(0, 12).map((stats, index) => {
1263
- const module = moduleSummary(stats, sectionRoot, sectionType);
1264
- module.id = `module-${index + 1}`;
1265
- module.order = index + 1;
1266
- return module;
1267
- }).filter((module) => module.kind !== "unknown");
1268
- }
1269
-
1270
- function dedupeModuleStats(statsList) {
1271
- const accepted = [];
1272
- for (const stats of statsList) {
1273
- if (accepted.some((existing) => overlaps(existing, stats))) continue;
1274
- accepted.push(stats);
1275
- }
1276
- return accepted;
1277
- }
1278
-
1279
- function extractLogoModules(sectionRoot, sectionType) {
1280
- const imageNodes = uniqueElements(
1281
- Array.from(sectionRoot.querySelectorAll("img"))
1282
- .filter(isVisible)
1283
- .map((image) => image.closest("a, figure, li, div") || image),
1284
- );
1285
- const statsList = imageNodes
1286
- .map((node) => collectModuleStats(node, sectionRoot))
1287
- .filter((stats) => stats.imageCount >= 1)
1288
- .filter((stats) => stats.rect.width >= 40 && stats.rect.height >= 24)
1289
- .sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height));
1290
- return dedupeModuleStats(statsList).slice(0, 16).map((stats, index) => {
1291
- const module = moduleSummary(stats, sectionRoot, sectionType);
1292
- module.id = `module-${index + 1}`;
1293
- module.order = index + 1;
1294
- return module;
1295
- }).filter((module) => module.kind !== "unknown");
1296
- }
1297
-
1298
- function extractReviewModules(sectionRoot, sectionType) {
1299
- const reviewNodes = uniqueElements(
1300
- Array.from(sectionRoot.querySelectorAll("[class*='review'], [id*='review'], [class*='testimonial'], [class*='customer']"))
1301
- .filter(isVisible),
1302
- );
1303
- const statsList = reviewNodes
1304
- .map((node) => collectModuleStats(node, sectionRoot))
1305
- .filter((stats) => stats.textLength >= 30 || stats.imageCount >= 1)
1306
- .filter((stats) => stats.rect.height >= 60)
1307
- .sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height));
1308
- const accepted = dedupeModuleStats(statsList)
1309
- .filter((stats) => !stats.node.contains(sectionRoot))
1310
- .slice(0, 12);
1311
- return accepted.map((stats, index) => {
1312
- const module = moduleSummary(stats, sectionRoot, sectionType);
1313
- module.id = `module-${index + 1}`;
1314
- module.order = index + 1;
1315
- return module;
1316
- }).filter((module) => module.kind !== "unknown");
1317
- }
1318
-
1319
- function resolveTextMediaScope(sectionRoot) {
1320
- let current = sectionRoot;
1321
- for (let depth = 0; depth < 3; depth += 1) {
1322
- const children = visibleChildren(current).filter((child) => child.getBoundingClientRect().height >= 24);
1323
- if (children.length !== 1) return current;
1324
- const onlyChild = children[0];
1325
- const onlyRect = onlyChild.getBoundingClientRect();
1326
- if (onlyRect.width < current.getBoundingClientRect().width * 0.72) return current;
1327
- current = onlyChild;
1328
- }
1329
- return current;
1330
- }
1331
-
1332
- function extractTextMediaModules(sectionRoot, sectionType) {
1333
- const scope = resolveTextMediaScope(sectionRoot);
1334
- const children = moduleCandidatesFrom(scope, sectionRoot, { minHeight: 90, minWidth: 120 })
1335
- .sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height));
1336
- const accepted = [];
1337
- for (const stats of children) {
1338
- const moduleType = classifyModule(stats, sectionType);
1339
- if (moduleType.kind === "unknown") continue;
1340
- if (accepted.some((existing) => existing.node.contains(stats.node) && classifyModule(existing, sectionType).kind === moduleType.kind)) continue;
1341
- if (accepted.some((existing) => overlaps(existing, stats))) continue;
1342
- accepted.push(stats);
1343
- }
1344
- return accepted.slice(0, 6).map((stats, index) => {
1345
- const module = moduleSummary(stats, sectionRoot, sectionType);
1346
- module.id = `module-${index + 1}`;
1347
- module.order = index + 1;
1348
- return module;
1349
- });
1350
- }
1351
-
1352
- function extractStatModules(sectionRoot, sectionType) {
1353
- const children = visibleChildren(sectionRoot)
1354
- .map((child) => collectModuleStats(child, sectionRoot))
1355
- .filter((stats) => stats.headings.length >= 1 || stats.textLength >= 20)
1356
- .filter((stats) => stats.rect.height >= 40)
1357
- .slice(0, 12);
1358
- return children.map((stats, index) => {
1359
- const module = moduleSummary(stats, sectionRoot, sectionType);
1360
- module.id = `module-${index + 1}`;
1361
- module.order = index + 1;
1362
- return module;
1363
- }).filter((module) => module.kind !== "unknown");
1364
- }
1365
-
1366
- function extractGenericModules(sectionRoot, sectionType) {
1367
- const children = visibleChildren(sectionRoot)
1368
- .map((child) => collectModuleStats(child, sectionRoot))
1369
- .filter((stats) => stats.rect.width >= Math.min(sectionRoot.getBoundingClientRect().width * 0.22, 220) || stats.imageCount >= 1)
1370
- .filter((stats) => stats.rect.height >= 90)
1371
- .filter((stats) => stats.textLength >= 10 || stats.imageCount >= 1)
1372
- .slice(0, 16);
1373
- const accepted = [];
1374
- for (const stats of children) {
1375
- if (stats.rect.height > sectionRoot.getBoundingClientRect().height * 0.92 && stats.cardLikeChildren <= 1) continue;
1376
- if (accepted.some((existing) => overlaps(existing, stats))) continue;
1377
- accepted.push(stats);
1378
- }
1379
- return accepted.slice(0, 8).map((stats, index) => {
1380
- const module = moduleSummary(stats, sectionRoot, sectionType);
1381
- module.id = `module-${index + 1}`;
1382
- module.order = index + 1;
1383
- return module;
1384
- }).filter((module) => module.kind !== "unknown");
1385
- }
1386
-
1387
- const MODULE_EXTRACTORS = {
1388
- "reviews:*": extractReviewModules,
1389
- "card-collection:logo-gallery": extractLogoModules,
1390
- "card-collection:*": extractRepeatingItemModules,
1391
- "text-media:*": extractTextMediaModules,
1392
- "hero:*": extractTextMediaModules,
1393
- "stat-group:*": extractStatModules,
1394
- "footer:*": extractStatModules,
1395
- "category-strip:*": extractStatModules,
1396
- "cta-strip:*": extractStatModules,
1397
- "promo-band:*": extractStatModules,
1398
- };
1399
-
1400
- function extractSectionModules(sectionRoot, sectionType) {
1401
- const exact = MODULE_EXTRACTORS[`${sectionType.kind}:${sectionType.variant}`];
1402
- const family = MODULE_EXTRACTORS[`${sectionType.kind}:*`];
1403
- const extractor = exact || family || extractGenericModules;
1404
- return extractor(sectionRoot, sectionType);
1405
- }
1406
-
1407
- function extractSections() {
1408
- const contentRootChoice = findContentRoot();
1409
- const contentRoot = contentRootChoice.node || document.body;
1410
- const segmentationRootChoice = findSegmentationRoot(contentRoot);
1411
- const segmentationRoot = segmentationRootChoice.node || contentRoot;
1412
- const diagnostics = {
1413
- source: "dom-layout+a11y",
1414
- contentRoot: contentRootChoice.label || "body",
1415
- segmentationRootPath: domPath(segmentationRoot, contentRoot.parentElement || document.body.parentElement),
1416
- accessibilitySnapshotAvailable: Boolean(repeaterPlan?.accessibilitySnapshotAvailable),
1417
- accessibilityRoot: repeaterPlan?.accessibilityRoot || null,
1418
- candidateCount: 0,
1419
- acceptedSectionCount: 0,
1420
- acceptedModuleCount: 0,
1421
- warnings: [],
1422
- };
1423
- if (contentRootChoice.label !== "main" && contentRootChoice.label !== "role-main") {
1424
- diagnostics.warnings.push(`content-root-fallback-used:${contentRootChoice.label}`);
1425
- }
1426
- diagnostics.warnings.push(...segmentationRootChoice.warnings);
1427
- const primaryChildren = visibleChildren(segmentationRoot)
1428
- .map((node) => collectModuleStats(node, segmentationRoot))
1429
- .filter((stats) => stats.rect.height >= 24)
1430
- .filter((stats) => stats.textLength >= 10 || stats.imageCount >= 1 || stats.buttonCount >= 1 || stats.linkCount >= 1);
1431
- const primaryBandStats = primaryChildren
1432
- .filter((stats) => isStructuralBand(stats, segmentationRoot))
1433
- .map((stats) => ({
1434
- ...stats,
1435
- score: sectionScore(stats, segmentationRoot) + 22,
1436
- isDirectChild: true,
1437
- }))
1438
- .filter((stats) => stats.score >= 30)
1439
- .filter((stats) => stats.rect.height >= 40)
1440
- .sort((a, b) => a.rect.top - b.rect.top);
1441
- const candidateNodes = uniqueElements([
1442
- ...primaryBandStats.map((stats) => stats.node),
1443
- ...Array.from(segmentationRoot.querySelectorAll("section, article, aside, [role='region'], [data-testid], div"))
1444
- .filter(isVisible)
1445
- .slice(0, 260),
1446
- ]).filter((node) => node !== contentRoot && node !== segmentationRoot);
1447
- const candidateStats = candidateNodes
1448
- .map((node) => collectModuleStats(node, segmentationRoot))
1449
- .filter((stats) => stats.rect.width >= Math.min(segmentationRoot.getBoundingClientRect().width * 0.45, 520))
1450
- .filter((stats) => stats.rect.height >= 80)
1451
- .filter((stats) => stats.textLength >= 20 || stats.imageCount >= 1)
1452
- .map((stats) => {
1453
- let score = sectionScore(stats, segmentationRoot);
1454
- if (stats.node.parentElement === segmentationRoot) score += 18;
1455
- if (isStructuralBand(stats, segmentationRoot)) score += 8;
1456
- return { ...stats, score, isDirectChild: stats.node.parentElement === segmentationRoot };
1457
- })
1458
- .sort((a, b) => a.rect.top - b.rect.top || b.score - a.score);
1459
- diagnostics.candidateCount = candidateStats.length;
1460
- const accepted = [...primaryBandStats];
1461
- for (const stats of candidateStats) {
1462
- if (stats.score < 35) continue;
1463
- if (isWrapperLike(stats, segmentationRoot)) continue;
1464
- const duplicate = accepted.find((existing) => overlaps(existing, stats));
1465
- if (!duplicate) {
1466
- accepted.push(stats);
1467
- continue;
1468
- }
1469
- if (duplicate.isDirectChild && !stats.isDirectChild && duplicate.score >= stats.score - 15) continue;
1470
- if (stats.isDirectChild && !duplicate.isDirectChild && stats.score >= duplicate.score - 15) {
1471
- accepted.splice(accepted.indexOf(duplicate), 1, stats);
1472
- continue;
1473
- }
1474
- const statsContainsDuplicate = stats.node.contains(duplicate.node);
1475
- const duplicateContainsStats = duplicate.node.contains(stats.node);
1476
- if (isWrapperLike(duplicate, segmentationRoot) && duplicateContainsStats) {
1477
- accepted.splice(accepted.indexOf(duplicate), 1, stats);
1478
- } else if (isWrapperLike(stats, segmentationRoot) && statsContainsDuplicate) {
1479
- diagnostics.warnings.push(`nested-candidates-collapsed:${stats.path}`);
1480
- continue;
1481
- } else if (duplicateContainsStats && duplicate.rect.height > stats.rect.height * 1.5 && stats.score >= duplicate.score - 8) {
1482
- accepted.splice(accepted.indexOf(duplicate), 1, stats);
1483
- } else if (statsContainsDuplicate && stats.rect.height > duplicate.rect.height * 1.5) {
1484
- diagnostics.warnings.push(`nested-candidates-collapsed:${stats.path}`);
1485
- continue;
1486
- } else if (stats.score > duplicate.score) {
1487
- accepted.splice(accepted.indexOf(duplicate), 1, stats);
1488
- }
1489
- }
1490
- if (!accepted.length) {
1491
- diagnostics.warnings.push("no-strong-section-boundaries");
1492
- }
1493
- const sections = accepted.slice(0, 18).map((stats, index) => {
1494
- const sectionRoot = stats.node;
1495
- const sectionType = classifySection(stats);
1496
- const modules = extractSectionModules(sectionRoot, sectionType).map((module, moduleIndex) => ({
1497
- ...module,
1498
- id: `section-${String(index + 1).padStart(3, "0")}-module-${String(moduleIndex + 1).padStart(3, "0")}`,
1499
- order: moduleIndex + 1,
1500
- }));
1501
- diagnostics.acceptedModuleCount += modules.length;
1502
- return {
1503
- id: `section-${String(index + 1).padStart(3, "0")}`,
1504
- kind: sectionType.kind,
1505
- variant: sectionType.variant,
1506
- capabilities: sectionType.capabilities,
1507
- source: "dom-layout+a11y",
1508
- tag: stats.tag,
1509
- idAttr: sectionRoot.id || "",
1510
- className: sectionRoot.className?.toString?.() || "",
1511
- heading: stats.heading,
1512
- text: stats.text,
1513
- visibleCodeTexts: stats.visibleCodeTexts,
1514
- order: index + 1,
1515
- confidence: Number(Math.max(0.3, Math.min(0.98, stats.score / 100)).toFixed(2)),
1516
- rect: {
1517
- x: Math.round(stats.rect.x),
1518
- y: Math.round(stats.rect.y),
1519
- width: Math.round(stats.rect.width),
1520
- height: Math.round(stats.rect.height),
1521
- top: Math.round(stats.rect.top + window.scrollY),
1522
- left: Math.round(stats.rect.left),
1523
- },
1524
- layoutHints: {
1525
- fullWidth: stats.rect.width >= segmentationRoot.getBoundingClientRect().width * 0.85,
1526
- columnCount: Math.max(1, Math.min(stats.rowCountEstimate > 1 ? Math.ceil(stats.cardLikeChildren / stats.rowCountEstimate) : stats.cardLikeChildren, 4)) || 1,
1527
- hasGrid: stats.cardLikeChildren >= 3 && stats.rowCountEstimate >= 1,
1528
- hasRepeatingChildren: stats.cardLikeChildren >= 3,
1529
- },
1530
- counts: {
1531
- headings: stats.headings.length,
1532
- paragraphs: stats.paragraphCount,
1533
- images: stats.imageCount,
1534
- links: stats.linkCount,
1535
- buttons: stats.buttonCount,
1536
- },
1537
- background: stats.background,
1538
- layoutEvidence: collectLayoutEvidence(sectionRoot),
1539
- domRef: {
1540
- tag: stats.tag,
1541
- id: sectionRoot.id || "",
1542
- classTokens: String(sectionRoot.className || "").split(/\s+/).filter(Boolean).slice(0, 6),
1543
- path: stats.path,
1544
- parentPath: stats.parentPath,
1545
- nthOfType: stats.nthOfType,
1546
- fingerprint: stats.fingerprint,
1547
- },
1548
- a11y: {
1549
- role: stats.role || null,
1550
- headingLevels: stats.headings.map((heading) => heading.level).filter(Boolean),
1551
- containsList: sectionRoot.querySelectorAll("ul,ol,[role='list']").length > 0,
1552
- },
1553
- modules,
1554
- warnings: [],
1555
- };
1556
- });
1557
- diagnostics.acceptedSectionCount = sections.length;
1558
- return { sections, diagnostics };
1559
- }
1560
-
1561
- const sectionResult = extractSections();
1562
- const footer = (() => {
1563
- const node = Array.from(document.querySelectorAll("footer, .site-footer, [role='contentinfo']")).find(isVisible);
1564
- if (!node) return null;
1565
- const links = Array.from(node.querySelectorAll("a[href]")).filter(isVisible).map(linkRecord);
1566
- const cleanNodeText = (source) => renderedText(source);
1567
- const text = cleanNodeText(node).slice(0, 6000);
1568
- const legalText = Array.from(node.querySelectorAll("p, small, [class*='legal' i], [class*='disclaimer' i]"))
1569
- .filter(isVisible)
1570
- .map(cleanNodeText)
1571
- .filter((value) => value.length >= 120 || /disclaimer|portfolio companies|copyright|©/i.test(value))
1572
- .filter((value, index, values) => values.indexOf(value) === index)
1573
- .join("\n\n") || text;
1574
- return {
1575
- text,
1576
- legalText: legalText.slice(0, 6000),
1577
- links: dedupeNav(links).slice(0, 80),
1578
- domRef: {
1579
- tag: node.tagName.toLowerCase(),
1580
- id: node.id || "",
1581
- classTokens: String(node.className || "").split(/\s+/).filter(Boolean).slice(0, 8),
1582
- path: domPath(node, document.body.parentElement),
1583
- },
1584
- };
1585
- })();
1586
- const computedTokens = Array.from(document.querySelectorAll("body, h1, h2, h3, p, a, button")).slice(0, 80).map((node) => {
1587
- const styles = getComputedStyle(node);
1588
- return {
1589
- tag: node.tagName.toLowerCase(),
1590
- text: renderedText(node).slice(0, 120) || "",
1591
- color: styles.color,
1592
- backgroundColor: styles.backgroundColor,
1593
- fontFamily: styles.fontFamily,
1594
- fontSize: styles.fontSize,
1595
- fontWeight: styles.fontWeight,
1596
- borderRadius: styles.borderRadius,
1597
- boxShadow: styles.boxShadow,
1598
- };
1599
- });
1600
- const direction = document.documentElement.dir || document.body.dir || getComputedStyle(document.body).direction || "ltr";
1601
- function nextVisibleNonHeadingSibling(node) {
1602
- let current = node?.nextElementSibling || null;
1603
- while (current) {
1604
- if (isVisible(current) && !/^H[1-6]$/.test(current.tagName)) return current;
1605
- current = current.nextElementSibling;
1606
- }
1607
- return null;
1608
- }
1609
-
1610
- function findContentContainerForHeading(heading) {
1611
- const direct = nextVisibleNonHeadingSibling(heading);
1612
- if (direct) return direct;
1613
- const parentDirect = nextVisibleNonHeadingSibling(heading.parentElement);
1614
- if (parentDirect) return parentDirect;
1615
- return null;
1616
- }
1617
-
1618
- function nearestCommonAncestor(a, b, limit) {
1619
- let current = a || null;
1620
- while (current) {
1621
- if (current.contains(b)) return current;
1622
- if (limit && current === limit) break;
1623
- current = current.parentElement;
1624
- }
1625
- return null;
1626
- }
1627
-
1628
- function uniqueElements(nodes) {
1629
- const seen = new Set();
1630
- const result = [];
1631
- for (const node of nodes || []) {
1632
- if (!node || seen.has(node)) continue;
1633
- seen.add(node);
1634
- result.push(node);
1635
- }
1636
- return result;
1637
- }
1638
-
1639
- function queryScopeNodes(scopeNodes, selector) {
1640
- const seen = new Set();
1641
- const matches = [];
1642
- for (const node of scopeNodes || []) {
1643
- if (node?.matches?.(selector) && !seen.has(node)) {
1644
- seen.add(node);
1645
- matches.push(node);
1646
- }
1647
- for (const child of node?.querySelectorAll?.(selector) || []) {
1648
- if (seen.has(child)) continue;
1649
- seen.add(child);
1650
- matches.push(child);
1651
- }
1652
- }
1653
- return matches;
1654
- }
1655
-
1656
- function collectContentNodes(content, nextHeadingNode) {
1657
- if (!content) return [];
1658
- const nodes = [];
1659
- if (nextHeadingNode && content.parentElement && nextHeadingNode.parentElement === content.parentElement) {
1660
- let current = content;
1661
- while (current && current !== nextHeadingNode) {
1662
- if (isVisible(current)) nodes.push(current);
1663
- current = current.nextElementSibling;
1664
- }
1665
- }
1666
- if (!nodes.length) nodes.push(content);
1667
- return uniqueElements(nodes);
1668
- }
1669
-
1670
- function nodeSignature(node) {
1671
- if (!node) return "";
1672
- const tag = node.tagName.toLowerCase();
1673
- const classTokens = String(node.className || "")
1674
- .split(/\s+/)
1675
- .filter(Boolean)
1676
- .slice(0, 4)
1677
- .sort()
1678
- .join(".");
1679
- const childTags = Array.from(node.children).slice(0, 8).map((child) => child.tagName.toLowerCase()).join(",");
1680
- return `${tag}:${classTokens}:${childTags}`;
1681
- }
1682
-
1683
- function normalized(value) {
1684
- return cleanText(value).toLowerCase();
1685
- }
1686
-
1687
- function extractParagraphs(scopeNodes, heading) {
1688
- const seen = new Set();
1689
- const paragraphs = [];
1690
- const candidates = queryScopeNodes(scopeNodes, "p, li, blockquote, figcaption").filter(isVisible);
1691
- for (const node of candidates) {
1692
- const text = renderedText(node);
1693
- if (!text || text.length < 20) continue;
1694
- const key = normalized(text);
1695
- if (seen.has(key)) continue;
1696
- seen.add(key);
1697
- paragraphs.push(text);
1698
- }
1699
- if (!paragraphs.length) {
1700
- const fallbackBlocks = queryScopeNodes(scopeNodes, "div, span").filter((node) => {
1701
- if (!isVisible(node) || node === heading || heading?.contains(node)) return false;
1702
- if (node.querySelector("p, li, blockquote, figcaption")) return false;
1703
- if (node.querySelector("img")) return false;
1704
- const text = renderedText(node);
1705
- if (!text || text.length < 30) return false;
1706
- if (Array.from(node.children).some((child) => renderedText(child) === text)) return false;
1707
- const display = getComputedStyle(node).display;
1708
- return /block|flex|grid|inline-block|table/.test(display);
1709
- });
1710
- for (const node of fallbackBlocks) {
1711
- const text = renderedText(node);
1712
- const key = normalized(text);
1713
- if (seen.has(key)) continue;
1714
- seen.add(key);
1715
- paragraphs.push(text);
1716
- }
1717
- }
1718
- if (paragraphs.length) return paragraphs;
1719
- const fallback = cleanText(scopeNodes.map((node) => renderedText(node)).join(" "));
1720
- return fallback ? [fallback] : [];
1721
- }
1722
-
1723
- function extractImages(scopeNodes) {
1724
- function bestSrcFromSet(value) {
1725
- const candidates = String(value || "")
1726
- .split(",")
1727
- .map((item) => item.trim())
1728
- .map((item) => {
1729
- const match = item.match(/^(\S+)(?:\s+(\d+)w)?/);
1730
- return match ? { url: match[1], width: Number(match[2] || 0) } : null;
1731
- })
1732
- .filter(Boolean)
1733
- .sort((a, b) => b.width - a.width);
1734
- return candidates[0]?.url || "";
1735
- }
1736
-
1737
- function bestImageSrc(img) {
1738
- return (
1739
- img.getAttribute("data-src")
1740
- || img.getAttribute("data-lazy-src")
1741
- || bestSrcFromSet(img.getAttribute("data-srcset"))
1742
- || bestSrcFromSet(img.getAttribute("srcset"))
1743
- || img.currentSrc
1744
- || img.src
1745
- || ""
1746
- );
1747
- }
1748
-
1749
- const seen = new Set();
1750
- return queryScopeNodes(scopeNodes, "img")
1751
- .filter((img) => {
1752
- const rect = img.getBoundingClientRect();
1753
- return (isVisible(img) || Boolean(bestImageSrc(img))) && rect.width >= 24 && rect.height >= 24;
1754
- })
1755
- .map((img) => {
1756
- const rect = img.getBoundingClientRect();
1757
- return {
1758
- src: bestImageSrc(img),
1759
- alt: cleanText(img.getAttribute("alt") || ""),
1760
- width: Math.round(rect.width),
1761
- height: Math.round(rect.height),
1762
- rect: {
1763
- left: rect.left,
1764
- top: rect.top,
1765
- width: rect.width,
1766
- height: rect.height,
1767
- },
1768
- };
1769
- })
1770
- .filter((image) => image.src && !image.src.startsWith("data:image/svg+xml"))
1771
- .filter((image) => {
1772
- if (seen.has(image.src)) return false;
1773
- seen.add(image.src);
1774
- return true;
1775
- });
1776
- }
1777
-
1778
- function extractLinksFromContainer(scopeNodes) {
1779
- const seen = new Set();
1780
- return queryScopeNodes(scopeNodes, "a[href]")
1781
- .map((anchor) => ({
1782
- href: anchor.href || anchor.getAttribute("href") || "",
1783
- label: semanticText(anchor).slice(0, 160),
1784
- }))
1785
- .filter((link) => link.href)
1786
- .filter((link) => {
1787
- const key = `${link.label}\u0000${link.href}`;
1788
- if (seen.has(key)) return false;
1789
- seen.add(key);
1790
- return true;
1791
- })
1792
- .slice(0, 24);
1793
- }
1794
-
1795
- function inferItemLayoutHints(heading, scopeNodes, images, paragraphs) {
1796
- const descendants = queryScopeNodes(scopeNodes, "img, p, li, blockquote, figcaption, div, span").filter((node) => {
1797
- const text = renderedText(node);
1798
- if (node.tagName === "IMG") return true;
1799
- return text.length >= 20 && isVisible(node);
1800
- });
1801
- const firstKind = descendants[0]?.tagName === "IMG" ? "media-first" : descendants[0] ? "text-first" : "unknown";
1802
- const textNodes = queryScopeNodes(scopeNodes, "p, li, blockquote, figcaption, div, span").filter((node) => {
1803
- if (!isVisible(node) || node === heading || heading?.contains(node)) return false;
1804
- const text = renderedText(node);
1805
- if (!text || text.length < 20) return false;
1806
- if (Array.from(node.children).some((child) => renderedText(child) === text)) return false;
1807
- return true;
1808
- });
1809
- const averageCenter = (nodes) => {
1810
- if (!nodes.length) return null;
1811
- const centers = nodes.map((node) => {
1812
- const rect = node.getBoundingClientRect();
1813
- return rect.left + (rect.width / 2);
1814
- });
1815
- return centers.reduce((sum, value) => sum + value, 0) / centers.length;
1816
- };
1817
- const textCenter = averageCenter(textNodes);
1818
- const imageCenter = images.length
1819
- ? images.reduce((sum, image) => sum + (image.rect.left + (image.rect.width / 2)), 0) / images.length
1820
- : null;
1821
- let textPosition = "stacked";
1822
- let mediaPosition = "stacked";
1823
- if (textCenter != null && imageCenter != null) {
1824
- const delta = imageCenter - textCenter;
1825
- const scopeWidth = Math.max(...scopeNodes.map((node) => node.getBoundingClientRect().width), 0);
1826
- const threshold = Math.max(scopeWidth * 0.12, 60);
1827
- if (Math.abs(delta) > threshold) {
1828
- mediaPosition = delta < 0 ? "left" : "right";
1829
- textPosition = delta < 0 ? "right" : "left";
1830
- }
1831
- }
1832
- return {
1833
- mediaCount: images.length,
1834
- paragraphCount: paragraphs.length,
1835
- firstContentKind: firstKind,
1836
- headingLevel: Number(heading.tagName.replace(/^H/i, "")) || null,
1837
- textPosition,
1838
- mediaPosition,
1839
- };
1840
- }
1841
-
1842
- function scoreItemContainer(node, { heading, content, main }) {
1843
- if (!node || !main.contains(node)) return Number.NEGATIVE_INFINITY;
1844
- if (!node.contains(heading) || !node.contains(content)) return Number.NEGATIVE_INFINITY;
1845
- const rect = node.getBoundingClientRect();
1846
- if (rect.width < 180 || rect.height < 80) return Number.NEGATIVE_INFINITY;
1847
- const classText = `${node.className || ""} ${node.id || ""}`.toLowerCase();
1848
- const imageCount = node.querySelectorAll("img").length;
1849
- const paragraphCount = node.querySelectorAll("p, li, blockquote, figcaption").length;
1850
- const headingCount = node.querySelectorAll("h1,h2,h3,h4,h5,h6").length;
1851
- let score = 0;
1852
- score += 40;
1853
- score += Math.min(imageCount, 6) * 6;
1854
- score += Math.min(paragraphCount, 6) * 5;
1855
- if (headingCount === 1) score += 18;
1856
- else score -= Math.max(0, headingCount - 1) * 12;
1857
- if (node === content) score -= 10;
1858
- if (/slider|carousel|gallery|nav|menu|footer|share/.test(classText)) score -= 35;
1859
- if (rect.height > window.innerHeight * 2.5) score -= 25;
1860
- if (rect.width < Math.min(window.innerWidth * 0.45, 320)) score -= 20;
1861
- return score;
1862
- }
1863
-
1864
- function findItemContainerForHeading(heading, main) {
1865
- const content = findContentContainerForHeading(heading);
1866
- if (!content) return null;
1867
- const common = nearestCommonAncestor(heading, content, main) || nearestCommonAncestor(heading, content, document.body);
1868
- const candidates = uniqueElements([
1869
- common,
1870
- content,
1871
- content.parentElement,
1872
- heading.parentElement,
1873
- heading.parentElement?.parentElement,
1874
- heading.closest("section,article,li,[data-testid],[class]"),
1875
- content.closest("section,article,li,[data-testid],[class]"),
1876
- ]).filter((node) => node && main.contains(node));
1877
- const scored = candidates
1878
- .map((node) => ({ node, score: scoreItemContainer(node, { heading, content, main }) }))
1879
- .filter((entry) => Number.isFinite(entry.score))
1880
- .sort((a, b) => b.score - a.score);
1881
- const best = scored[0];
1882
- if (!best) return null;
1883
- return { container: best.node, content, score: best.score, signature: nodeSignature(best.node) };
1884
- }
1885
-
1886
- function dominantParentGroup(matches) {
1887
- const groups = [];
1888
- for (const match of matches) {
1889
- const parent = match.container.parentElement;
1890
- if (!parent) continue;
1891
- const existing = groups.find((group) => group.parent === parent);
1892
- if (existing) existing.items.push(match);
1893
- else groups.push({ parent, items: [match] });
1894
- }
1895
- groups.sort((a, b) => b.items.length - a.items.length);
1896
- return groups[0] || null;
1897
- }
1898
-
1899
- function extractRepeaters(plan) {
1900
- const signals = Array.isArray(plan?.signals) ? plan.signals : [];
1901
- const main = document.querySelector("main");
1902
- const diagnostics = {
1903
- source: "accessibility+dom",
1904
- accessibilitySnapshotAvailable: Boolean(plan?.accessibilitySnapshotAvailable),
1905
- signalCount: signals.length,
1906
- candidateCount: plan?.candidateCount || signals.length,
1907
- acceptedRepeaterCount: 0,
1908
- signals: [],
1909
- warnings: [...(plan?.warnings || [])],
1910
- };
1911
- if (!main) {
1912
- diagnostics.warnings.push("main-content-missing: manual repeater review required");
1913
- return { repeaters: [], diagnostics };
1914
- }
1915
- const headingNodes = Array.from(main.querySelectorAll("h1,h2,h3,h4,h5,h6")).filter((node) => renderedText(node));
1916
- const headingRegistry = headingNodes.map((node, index) => ({
1917
- node,
1918
- index,
1919
- text: renderedText(node),
1920
- normalized: normalized(renderedText(node)),
1921
- level: Number(node.tagName.replace(/^H/i, "")) || 0,
1922
- }));
1923
- const repeaters = signals.map((signal, signalIndex) => {
1924
- let minIndex = -1;
1925
- const matched = [];
1926
- for (const expected of signal.items || []) {
1927
- const expectedText = normalized(expected.heading);
1928
- const match = headingRegistry.find((entry) => entry.index > minIndex && entry.level === signal.headingLevel && entry.normalized === expectedText);
1929
- if (!match) continue;
1930
- minIndex = match.index;
1931
- const resolved = findItemContainerForHeading(match.node, main);
1932
- if (!resolved) continue;
1933
- matched.push({ ...resolved, heading: match });
1934
- }
1935
- const dominantParent = dominantParentGroup(matched);
1936
- const resolvedItems = dominantParent && dominantParent.items.length >= 3 ? dominantParent.items : matched;
1937
- const items = resolvedItems.map((resolved, resolvedIndex) => {
1938
- const nextHeadingNode = resolvedItems[resolvedIndex + 1]?.heading?.node || null;
1939
- const scopeNodes = collectContentNodes(resolved.content, nextHeadingNode);
1940
- const paragraphs = extractParagraphs(scopeNodes, resolved.heading.node);
1941
- const images = extractImages(scopeNodes);
1942
- return {
1943
- heading: resolved.heading.text,
1944
- paragraphs,
1945
- images,
1946
- links: extractLinksFromContainer(scopeNodes),
1947
- layoutHints: inferItemLayoutHints(resolved.heading.node, scopeNodes, images, paragraphs),
1948
- };
1949
- }).filter((item) => item.paragraphs.length && item.images.length);
1950
- const signalDiagnostics = {
1951
- label: signal.label || `main-repeater-${signalIndex + 1}`,
1952
- roleSignature: signal.roleSignature || null,
1953
- headingLevel: signal.headingLevel || null,
1954
- expectedItemCount: signal.items?.length || 0,
1955
- matchedHeadingCount: matched.length,
1956
- resolvedItemCount: items.length,
1957
- dominantParentTag: dominantParent?.parent?.tagName?.toLowerCase() || null,
1958
- dominantParentClass: dominantParent?.parent?.className?.toString?.() || "",
1959
- containerSignatures: Array.from(new Set(resolvedItems.map((item) => item.signature))).slice(0, 6),
1960
- warnings: [],
1961
- };
1962
- diagnostics.signals.push(signalDiagnostics);
1963
- if (items.length < 3) {
1964
- signalDiagnostics.warnings.push("dom-resolution-ambiguous");
1965
- diagnostics.warnings.push(`repeater-resolution-ambiguous:${signalDiagnostics.label}`);
1966
- return null;
1967
- }
1968
- const mediaCounts = items.map((item) => item.images.length);
1969
- const repeater = {
1970
- kind: "content-repeater",
1971
- source: "accessibility+dom",
1972
- label: signal.label || `main-repeater-${signalIndex + 1}`,
1973
- itemCount: items.length,
1974
- schema: {
1975
- hasHeading: items.every((item) => Boolean(item.heading)),
1976
- hasParagraphs: items.every((item) => item.paragraphs.length > 0),
1977
- hasImages: items.every((item) => item.images.length > 0),
1978
- maxImagesPerItem: Math.max(...mediaCounts),
1979
- },
1980
- layoutHints: {
1981
- alternating: new Set(items.map((item) => item.layoutHints.textPosition || item.layoutHints.firstContentKind)).size > 1,
1982
- },
1983
- items,
1984
- };
1985
- diagnostics.acceptedRepeaterCount += 1;
1986
- return repeater;
1987
- }).filter(Boolean);
1988
- if (!signals.length) {
1989
- const mainHeadingCount = main.querySelectorAll("h2,h3,h4,h5,h6").length;
1990
- const mainImageCount = main.querySelectorAll("img").length;
1991
- if (mainHeadingCount >= 4 && mainImageCount >= 4) {
1992
- diagnostics.warnings.push("possible-repeated-content-without-high-confidence-structure: manual review required");
1993
- }
1994
- } else if (!repeaters.length) {
1995
- diagnostics.warnings.push("accessibility-signals-found-but-no-structured-repeaters-resolved: manual review required");
1996
- }
1997
- return { repeaters, diagnostics };
1998
- }
1999
-
2000
- function extractAccordionRepeaters(main) {
2001
- if (!main) return [];
2002
- const groups = [];
2003
- const candidates = Array.from(main.querySelectorAll("details")).filter((node) => renderedText(node.querySelector("summary")));
2004
- for (const node of candidates) {
2005
- const parent = node.parentElement;
2006
- if (!parent) continue;
2007
- const existing = groups.find((group) => group.parent === parent);
2008
- if (existing) existing.items.push(node);
2009
- else groups.push({ parent, items: [node] });
2010
- }
2011
- const nativeRecords = groups.filter((group) => group.items.length >= 2).map((group, groupIndex) => {
2012
- const items = group.items.map((node) => {
2013
- const summary = node.querySelector("summary");
2014
- const clone = node.cloneNode(true);
2015
- clone.querySelector("summary")?.remove();
2016
- const paragraphs = Array.from(clone.querySelectorAll("p,li")).map((child) => semanticText(child)).filter(Boolean);
2017
- const fallbackText = semanticText(clone);
2018
- const images = Array.from(clone.querySelectorAll("img")).map((image) => ({ src: image.currentSrc || image.src || "", alt: image.alt || "" })).filter((image) => image.src && !/(emoji|twemoji|wp-smiley)/i.test(image.src));
2019
- const links = Array.from(clone.querySelectorAll("a[href]")).map((link) => ({ text: semanticText(link), url: link.href })).filter((link) => link.url);
2020
- const content = paragraphs.length ? paragraphs : fallbackText ? [fallbackText] : [];
2021
- return {
2022
- heading: renderedText(summary),
2023
- paragraphs: content,
2024
- images,
2025
- links,
2026
- initialExpanded: Boolean(node.open),
2027
- itemStructure: images.length ? content.length ? "media-and-text" : "media-only" : "text-only",
2028
- layoutHints: { mediaCount: images.length, paragraphCount: content.length, firstContentKind: images.length ? "media-first" : "text-first", headingLevel: null },
2029
- };
2030
- });
2031
- const mediaCounts = items.map((item) => item.images.length);
2032
- const expanded = items.map((item, index) => item.initialExpanded ? index : null).filter((index) => index !== null);
2033
- return {
2034
- kind: "accordion-repeater",
2035
- source: "dom:details",
2036
- label: cleanText(group.parent.getAttribute("aria-label")) || `accordion-${groupIndex + 1}`,
2037
- itemCount: items.length,
2038
- schema: { hasHeading: true, hasParagraphs: items.every((item) => item.paragraphs.length > 0), hasImages: items.some((item) => item.images.length > 0), maxImagesPerItem: Math.max(...mediaCounts, 0) },
2039
- state: { initialExpandedIndices: expanded, multipleOpen: expanded.length > 1, openBehavior: "requires-interaction-probe" },
2040
- items,
2041
- };
2042
- });
2043
- const customRoots = Array.from(main.querySelectorAll(".tatsu-accordion, [class*='accordion' i]")).filter((root, index, all) =>
2044
- root.querySelectorAll(".accordion-head").length >= 2 && !all.some((other) => other !== root && other.contains(root) && other.querySelectorAll(".accordion-head").length >= 2),
2045
- );
2046
- const customRecords = customRoots.map((root, rootIndex) => {
2047
- const heads = Array.from(root.querySelectorAll(".accordion-head")).filter((head) => renderedText(head));
2048
- const items = heads.map((head) => {
2049
- const contentRoot = head.nextElementSibling?.matches?.(".accordion-content,[role='region']") ? head.nextElementSibling : document.getElementById(head.getAttribute("aria-controls") || "");
2050
- const paragraphs = Array.from(contentRoot?.querySelectorAll?.("p,li") || []).map((child) => semanticText(child)).filter(Boolean);
2051
- const fallbackText = contentRoot ? semanticText(contentRoot) : "";
2052
- const content = paragraphs.length ? paragraphs : fallbackText ? [fallbackText] : [];
2053
- const images = Array.from(contentRoot?.querySelectorAll?.("img") || []).map((image) => ({ src: image.currentSrc || image.src || "", alt: image.alt || "" })).filter((image) => image.src && !/(emoji|twemoji|wp-smiley)/i.test(image.src));
2054
- const links = Array.from(contentRoot?.querySelectorAll?.("a[href]") || []).map((link) => ({ text: semanticText(link), url: link.href })).filter((link) => link.url);
2055
- const expanded = head.getAttribute("aria-expanded") === "true" || contentRoot && getComputedStyle(contentRoot).display !== "none" && getComputedStyle(contentRoot).visibility !== "hidden" && contentRoot.getBoundingClientRect().height > 0;
2056
- return { heading: renderedText(head), paragraphs: content, images, links, initialExpanded: Boolean(expanded), itemStructure: images.length ? content.length ? "media-and-text" : "media-only" : "text-only", layoutHints: { mediaCount: images.length, paragraphCount: content.length, firstContentKind: images.length ? "media-first" : "text-first", headingLevel: Number(head.tagName.slice(1)) || null } };
2057
- });
2058
- const expanded = items.map((item, index) => item.initialExpanded ? index : null).filter((index) => index !== null);
2059
- return { kind: "accordion-repeater", source: "dom:accordion-head", label: cleanText(root.getAttribute("aria-label")) || `accordion-${rootIndex + 1}`, itemCount: items.length, schema: { hasHeading: true, hasParagraphs: items.every((item) => item.paragraphs.length > 0), hasImages: items.some((item) => item.images.length > 0), maxImagesPerItem: Math.max(0, ...items.map((item) => item.images.length)) }, state: { initialExpandedIndices: expanded, multipleOpen: expanded.length > 1, openBehavior: root.classList.contains("tatsu-accordion") ? "single" : "requires-interaction-probe" }, items };
2060
- }).filter((record) => record.itemCount >= 2 && record.items.every((item) => item.paragraphs.length));
2061
- return [...nativeRecords, ...customRecords];
2062
- }
2063
-
2064
- const repeaterResult = extractRepeaters(repeaterPlan);
2065
- const accordionRepeaters = extractAccordionRepeaters(document.querySelector("main, [role='main'], #main, #content") || document.body);
2066
- const visualAssets = collectVisualAssets();
2067
- const visibleLinks = dedupeNav(Array.from(document.querySelectorAll("a[href]")).filter(isVisible).map(linkRecord)).slice(0, 240)
2068
- .map((link) => ({ text: link.label, url: link.href }));
2069
- return {
2070
- sections: sectionResult.sections,
2071
- sectionDiagnostics: sectionResult.diagnostics,
2072
- footer,
2073
- computedTokens,
2074
- repeaters: [...repeaterResult.repeaters, ...accordionRepeaters],
2075
- repeaterDiagnostics: repeaterResult.diagnostics,
2076
- ignoredSurfaces,
2077
- visualAssets,
2078
- links: visibleLinks,
2079
- chrome: {
2080
- direction,
2081
- viewport: { width: window.innerWidth, height: window.innerHeight },
2082
- header: {
2083
- variants: [initialHeader, scrolledHeader],
2084
- maxStickyViewportRatio: 0.16,
2085
- },
2086
- navigation,
2087
- heroEvidence: {
2088
- visibleText,
2089
- forbiddenSeoOnlyText: [],
2090
- },
2091
- },
2092
- };
2093
- }, repeaterPlan);
2094
- const normalizedDesktopDom = await page.content();
2095
- browserData.chrome.heroEvidence.forbiddenSeoOnlyText = seoOnlyText({ seo, visibleText: browserData.chrome.heroEvidence.visibleText });
2096
- const extractionWarnings = [
2097
- ...((desktopNavigation.warnings || []).map((warning) => `navigation: ${warning}`)),
2098
- ...(browserData.sectionDiagnostics?.warnings || []),
2099
- ...(browserData.repeaterDiagnostics?.warnings || []),
2100
- ];
2101
- return {
2102
- url,
2103
- path: new URL(url).pathname || "/",
2104
- area: classifyUrl(url),
2105
- title: seo.title,
2106
- seo,
2107
- navigation: desktopNavigation,
2108
- sections: browserData.sections.length ? browserData.sections : extractSectionsFromHtml(html),
2109
- sectionDiagnostics: browserData.sectionDiagnostics || {
2110
- source: "dom-layout+a11y",
2111
- contentRoot: "unknown",
2112
- accessibilitySnapshotAvailable: Boolean(repeaterPlan?.accessibilitySnapshotAvailable),
2113
- accessibilityRoot: repeaterPlan?.accessibilityRoot || null,
2114
- candidateCount: 0,
2115
- acceptedSectionCount: 0,
2116
- acceptedModuleCount: 0,
2117
- warnings: ["no-strong-section-boundaries"],
2118
- },
2119
- repeaters: browserData.repeaters || [],
2120
- repeaterDiagnostics: browserData.repeaterDiagnostics || { source: "accessibility+dom", signalCount: 0, acceptedRepeaterCount: 0, warnings: [] },
2121
- footer: browserData.footer || null,
2122
- ignoredSurfaces: browserData.ignoredSurfaces || [],
2123
- normalizationActions: (browserData.ignoredSurfaces || []).flatMap((surface) => [
2124
- {
2125
- phase: "before-scroll-sweep",
2126
- action: "quarantine-obstructive-surface",
2127
- surfaceId: surface.id,
2128
- kind: surface.kind,
2129
- reason: surface.reason,
2130
- reversible: true,
2131
- },
2132
- {
2133
- phase: "after-scroll-sweep",
2134
- action: "verify-surface-remains-quarantined",
2135
- surfaceId: surface.id,
2136
- kind: surface.kind,
2137
- reversible: true,
2138
- },
2139
- ]),
2140
- visualAssets: browserData.visualAssets || [],
2141
- links: browserData.links?.length ? browserData.links : extractLinks(html, url),
2142
- tokens: { ...inferTokensFromHtml(html), computed: browserData.computedTokens },
2143
- chrome: browserData.chrome,
2144
- responsiveTextGeometry,
2145
- screenshots: screenshotMap,
2146
- navigationProfiles,
2147
- rawDomSnapshots: htmlByViewport,
2148
- normalizedDomSnapshots: { desktop: normalizedDesktopDom },
2149
- accessibilityEvidence: {
2150
- snapshotAvailable: Boolean(repeaterPlan?.accessibilitySnapshotAvailable),
2151
- root: repeaterPlan?.accessibilityRoot || null,
2152
- signalCount: repeaterPlan?.signals?.length || 0,
2153
- },
2154
- extractionWarnings: extractionWarnings.length ? extractionWarnings : undefined,
2155
- extractedAt: new Date().toISOString(),
2156
- };
2157
- } finally {
2158
- await browser.close();
2159
- }
2160
- }
2161
-
2162
- async function captureImportantTextGeometry(page) {
2163
- return page.evaluate(() => {
2164
- const clean = (value) => String(value || "").replace(/\s+/g, " ").trim();
2165
- const visible = (node) => {
2166
- const rect = node.getBoundingClientRect();
2167
- const style = getComputedStyle(node);
2168
- return rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden" && Number(style.opacity || 1) > 0;
2169
- };
2170
- const lineCount = (node) => {
2171
- const tops = [];
2172
- const walker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
2173
- for (let textNode = walker.nextNode(); textNode; textNode = walker.nextNode()) {
2174
- if (!clean(textNode.nodeValue)) continue;
2175
- const range = document.createRange();
2176
- range.selectNodeContents(textNode);
2177
- for (const rect of range.getClientRects()) {
2178
- if (rect.width > 0 && rect.height > 0 && !tops.some((top) => Math.abs(top - rect.top) <= 2)) tops.push(rect.top);
2179
- }
2180
- }
2181
- return Math.max(1, tops.length);
2182
- };
2183
- return Array.from(document.querySelectorAll("h1,h2,h3"))
2184
- .filter(visible)
2185
- .map((node) => {
2186
- const rect = node.getBoundingClientRect();
2187
- const style = getComputedStyle(node);
2188
- const lines = lineCount(node);
2189
- return {
2190
- text: clean(node.textContent).slice(0, 240),
2191
- tag: node.tagName.toLowerCase(),
2192
- inlineSize: Math.round(rect.width * 100) / 100,
2193
- blockSize: Math.round(rect.height * 100) / 100,
2194
- lineCount: lines,
2195
- wrapPolicy: lines === 1 ? "single-line" : style.whiteSpace === "nowrap" ? "clipped-or-overflowing" : "wrapped",
2196
- fontSize: style.fontSize,
2197
- lineHeight: style.lineHeight,
2198
- maxWidth: style.maxWidth,
2199
- };
2200
- })
2201
- .filter((item) => item.text)
2202
- .slice(0, 48);
2203
- });
2204
- }
2205
-
2206
- async function extractWithFetch(url) {
2207
- const html = await fetchText(url);
2208
- const seo = extractSeo(html, url);
2209
- return {
2210
- url,
2211
- path: new URL(url).pathname || "/",
2212
- area: classifyUrl(url),
2213
- title: seo.title,
2214
- seo,
2215
- sections: extractSectionsFromHtml(html),
2216
- sectionDiagnostics: {
2217
- source: "none",
2218
- contentRoot: "none",
2219
- accessibilitySnapshotAvailable: false,
2220
- accessibilityRoot: null,
2221
- candidateCount: 0,
2222
- acceptedSectionCount: 0,
2223
- acceptedModuleCount: 0,
2224
- warnings: ["browser-unavailable: section extraction requires browser-based DOM, layout, and accessibility evidence"],
2225
- },
2226
- repeaters: [],
2227
- repeaterDiagnostics: {
2228
- source: "none",
2229
- accessibilitySnapshotAvailable: false,
2230
- signalCount: 0,
2231
- acceptedRepeaterCount: 0,
2232
- warnings: ["browser-unavailable: repeater extraction requires browser-based accessibility and DOM evidence"],
2233
- },
2234
- footer: null,
2235
- links: extractLinks(html, url),
2236
- tokens: inferTokensFromHtml(html),
2237
- chrome: {
2238
- direction: "unknown",
2239
- header: { variants: [], maxStickyViewportRatio: 0.16 },
2240
- navigation: [],
2241
- heroEvidence: {
2242
- visibleText: [],
2243
- forbiddenSeoOnlyText: seoOnlyText({ seo, visibleText: [] }),
2244
- },
2245
- warnings: ["browser-unavailable: header behavior, dropdown hierarchy, logo footprint, and hero visible-text evidence require manual screenshot review"],
2246
- },
2247
- screenshots: {},
2248
- extractionWarnings: ["browser-unavailable: repeater extraction requires browser-based accessibility and DOM evidence"],
2249
- extractedAt: new Date().toISOString(),
2250
- };
2251
- }
2252
-
2253
- async function detectRepeaterSignals(page) {
2254
- const candidates = [
2255
- { selector: 'main, [role="main"]', label: "main" },
2256
- { selector: "article", label: "article" },
2257
- { selector: "body", label: "body" },
2258
- ];
2259
- const failures = [];
2260
- for (const candidate of candidates) {
2261
- const locator = page.locator(candidate.selector).first();
2262
- const count = await locator.count();
2263
- if (count === 0) {
2264
- failures.push(`${candidate.label}: selector did not match any element`);
2265
- continue;
2266
- }
2267
- try {
2268
- const snapshot = await locator.ariaSnapshot({ mode: "ai" });
2269
- const detection = detectRepeaterSignalsFromAria(snapshot);
2270
- return {
2271
- accessibilitySnapshotAvailable: true,
2272
- accessibilityRoot: candidate.label,
2273
- candidateCount: detection.candidateCount,
2274
- signals: detection.signals,
2275
- warnings: detection.signals.length
2276
- ? []
2277
- : [`accessibility-snapshot-captured-on-${candidate.label}-but-no-high-confidence-repeater-patterns-found`],
2278
- };
2279
- } catch (error) {
2280
- failures.push(`${candidate.label}: ${error.message}`);
2281
- }
2282
- }
2283
- return {
2284
- accessibilitySnapshotAvailable: false,
2285
- accessibilityRoot: null,
2286
- candidateCount: 0,
2287
- signals: [],
2288
- warnings: [`accessibility-snapshot-unavailable: ${failures.join("; ")}`],
2289
- };
2290
- }
2291
-
2292
- async function navigateWithAdaptiveProfile(page, url) {
2293
- const profiles = [
2294
- { name: "normal", waitUntil: "networkidle", timeoutMs: 45000, settleMs: 0 },
2295
- { name: "slow-site", waitUntil: "domcontentloaded", timeoutMs: 90000, settleMs: 2000 },
2296
- ];
2297
- const warnings = [];
2298
- let lastError = null;
2299
- for (const profile of profiles) {
2300
- try {
2301
- await page.goto(url, { waitUntil: profile.waitUntil, timeout: profile.timeoutMs });
2302
- if (profile.settleMs > 0) {
2303
- await page.waitForTimeout(profile.settleMs);
2304
- }
2305
- if (profile.name !== "normal") {
2306
- warnings.push(`used ${profile.name} navigation profile after the default profile did not settle cleanly`);
2307
- }
2308
- return {
2309
- profile: profile.name,
2310
- waitUntil: profile.waitUntil,
2311
- timeoutMs: profile.timeoutMs,
2312
- warnings,
2313
- };
2314
- } catch (error) {
2315
- lastError = error;
2316
- warnings.push(`${profile.name} failed: ${error.message}`);
2317
- }
2318
- }
2319
- throw lastError;
2320
- }
2321
-
2322
- function detectRepeaterSignalsFromAria(snapshot) {
2323
- const tree = parseAriaSnapshot(snapshot);
2324
- if (!tree) return { signals: [], candidateCount: 0 };
2325
- const mains = [];
2326
- walkAriaTree(tree, (node) => {
2327
- if (node.role === "main") mains.push(node);
2328
- });
2329
- const root = mains.at(-1) || tree;
2330
- const candidates = [];
2331
- walkAriaTree(root, (node) => {
2332
- const grouped = groupHeadingContentPairs(node.children || []);
2333
- for (const group of grouped) {
2334
- if (group.items.length >= 3) candidates.push(group);
2335
- }
2336
- });
2337
- candidates.sort((a, b) => {
2338
- if (b.items.length !== a.items.length) return b.items.length - a.items.length;
2339
- return averageMediaCount(b.items) - averageMediaCount(a.items);
2340
- });
2341
- return {
2342
- candidateCount: candidates.length,
2343
- signals: candidates.slice(0, 3).map((candidate, index) => ({
2344
- label: `main-repeated-content-${index + 1}`,
2345
- headingLevel: candidate.headingLevel,
2346
- roleSignature: `heading-${candidate.headingLevel}+generic`,
2347
- items: candidate.items.map((item) => ({
2348
- heading: item.heading,
2349
- paragraphCount: item.paragraphCount,
2350
- imageCount: item.imageCount,
2351
- })),
2352
- })),
2353
- };
2354
- }
2355
-
2356
- function averageMediaCount(items) {
2357
- return items.reduce((sum, item) => sum + item.imageCount, 0) / Math.max(items.length, 1);
2358
- }
2359
-
2360
- function groupHeadingContentPairs(children = []) {
2361
- const grouped = new Map();
2362
- for (let index = 0; index < children.length - 1; index += 1) {
2363
- const heading = children[index];
2364
- const content = children[index + 1];
2365
- if (heading.role !== "heading" || content.role !== "generic") continue;
2366
- const headingLevel = heading.level || 0;
2367
- if (headingLevel < 2) continue;
2368
- const paragraphCount = countAriaRole(content, "paragraph");
2369
- const imageCount = countAriaRole(content, "img");
2370
- if (!heading.name || paragraphCount < 1 || imageCount < 1) continue;
2371
- const key = String(headingLevel);
2372
- if (!grouped.has(key)) grouped.set(key, { headingLevel, items: [] });
2373
- grouped.get(key).items.push({
2374
- heading: heading.name,
2375
- paragraphCount,
2376
- imageCount,
2377
- });
2378
- }
2379
- return Array.from(grouped.values());
2380
- }
2381
-
2382
- function countAriaRole(node, role) {
2383
- let count = 0;
2384
- walkAriaTree(node, (current) => {
2385
- if (current.role === role) count += 1;
2386
- });
2387
- return count;
2388
- }
2389
-
2390
- function walkAriaTree(node, visit) {
2391
- if (!node) return;
2392
- visit(node);
2393
- for (const child of node.children || []) walkAriaTree(child, visit);
2394
- }
2395
-
2396
- function parseAriaSnapshot(snapshot) {
2397
- const lines = String(snapshot || "")
2398
- .split("\n")
2399
- .map((line) => line.match(/^(\s*)- (.*)$/))
2400
- .filter(Boolean)
2401
- .map(([, indent, content]) => ({ depth: Math.floor(indent.length / 2), content }));
2402
- if (!lines.length) return null;
2403
- const root = { role: "root", children: [] };
2404
- const stack = [root];
2405
- for (const line of lines) {
2406
- const node = parseAriaLine(line.content);
2407
- if (!node) continue;
2408
- while (stack.length > line.depth + 1) stack.pop();
2409
- const parent = stack[stack.length - 1];
2410
- parent.children.push(node);
2411
- stack.push(node);
2412
- }
2413
- return root;
2414
- }
2415
-
2416
- function parseAriaLine(content) {
2417
- const trimmed = String(content || "").trim();
2418
- if (!trimmed) return null;
2419
- if (trimmed.startsWith("text:")) {
2420
- return { role: "text", name: trimmed.slice(5).trim(), children: [] };
2421
- }
2422
- const roleMatch = trimmed.match(/^([a-zA-Z0-9-]+)/);
2423
- if (!roleMatch) return null;
2424
- const role = roleMatch[1];
2425
- const nameMatch = trimmed.match(/"([^"]+)"/);
2426
- const levelMatch = trimmed.match(/\[level=(\d+)\]/);
2427
- return {
2428
- role,
2429
- name: nameMatch?.[1] || "",
2430
- level: levelMatch ? Number(levelMatch[1]) : null,
2431
- raw: trimmed,
2432
- children: [],
2433
- };
2434
- }
2435
-
2436
- function seoOnlyText({ seo, visibleText }) {
2437
- const visible = new Set((visibleText || []).map(normalizeEvidenceText));
2438
- return [
2439
- seo?.title,
2440
- seo?.openGraph?.["og:title"],
2441
- seo?.jsonLd?.name,
2442
- seo?.jsonLd?.description,
2443
- seo?.description,
2444
- ]
2445
- .flat()
2446
- .filter(Boolean)
2447
- .map((value) => String(value).replace(/&#039;/g, "'").replace(/&quot;/g, "\"").replace(/&amp;/g, "&").trim())
2448
- .filter((value, index, values) => values.indexOf(value) === index)
2449
- .filter((value) => !visible.has(normalizeEvidenceText(value)));
2450
- }
2451
-
2452
- function normalizeEvidenceText(value) {
2453
- return String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
2454
- }
2455
-
2456
- if (import.meta.url === `file://${process.argv[1]}`) {
2457
- main().catch((error) => {
2458
- console.error(error.stack || error.message);
2459
- process.exit(1);
2460
- });
2461
- }