@nebulacomponents/citable 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +269 -0
- package/README.md +43 -5
- package/dist/universal/.agents/skills/citable/SKILL.md +13 -4
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.agents/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.agents/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.agents/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.agents/skills/citable/manifest.json +26 -14
- package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.agents/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.agents/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.agents/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.agents/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.agents/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.agents/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.agents/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.claude/agents/citable/citable-auditor.md +64 -0
- package/dist/universal/.claude/agents/citable/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.claude/agents/citable/manifest.json +12 -0
- package/dist/universal/.claude/skills/citable/SKILL.md +13 -4
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.claude/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.claude/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.claude/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.claude/skills/citable/manifest.json +31 -14
- package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.claude/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.claude/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.claude/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.claude/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.claude/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.claude/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.claude/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.cursor/skills/citable/SKILL.md +13 -4
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.cursor/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.cursor/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.cursor/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.cursor/skills/citable/manifest.json +26 -14
- package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.cursor/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.cursor/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.cursor/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.cursor/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.cursor/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.cursor/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.gemini/skills/citable/SKILL.md +13 -4
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.gemini/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.gemini/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.gemini/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.gemini/skills/citable/manifest.json +26 -14
- package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.gemini/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.gemini/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.gemini/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.gemini/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.gemini/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.gemini/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.github/skills/citable/SKILL.md +13 -4
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.github/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.github/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.github/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.github/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.github/skills/citable/manifest.json +26 -14
- package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.github/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.github/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.github/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.github/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.github/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.github/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.github/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.github/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.kiro/skills/citable/SKILL.md +13 -4
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.kiro/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.kiro/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.kiro/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.kiro/skills/citable/manifest.json +26 -14
- package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.kiro/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.kiro/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.kiro/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.kiro/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.kiro/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.kiro/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.opencode/skills/citable/SKILL.md +13 -4
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.opencode/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.opencode/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.opencode/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.opencode/skills/citable/manifest.json +26 -14
- package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.opencode/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.opencode/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.opencode/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.opencode/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.opencode/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.opencode/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +13 -4
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.pi/agent/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.pi/agent/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.pi/agent/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.pi/agent/skills/citable/manifest.json +26 -14
- package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.pi/agent/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.qoder/skills/citable/SKILL.md +13 -4
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.qoder/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.qoder/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.qoder/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.qoder/skills/citable/manifest.json +26 -14
- package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.qoder/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.qoder/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.qoder/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.qoder/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.qoder/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.qoder/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.rovodev/skills/citable/SKILL.md +13 -4
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.rovodev/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.rovodev/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.rovodev/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.rovodev/skills/citable/manifest.json +26 -14
- package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.rovodev/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.rovodev/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.rovodev/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.rovodev/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.rovodev/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.rovodev/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.trae/skills/citable/SKILL.md +13 -4
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.trae/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.trae/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.trae/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.trae/skills/citable/manifest.json +26 -14
- package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.trae/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.trae/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.trae/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.trae/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.trae/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.trae/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.trae/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +13 -4
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.trae-cn/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.trae-cn/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +50 -3
- package/dist/universal/.trae-cn/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.trae-cn/skills/citable/manifest.json +26 -14
- package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.trae-cn/skills/citable/scripts/README.md +3 -1
- package/dist/universal/manifest.json +270 -171
- package/package.json +1 -1
- package/release/surfaces.json +1 -1
- package/schemas/action-plan.schema.json +13 -0
- package/schemas/browser-evidence-plan.schema.json +14 -0
- package/schemas/canonical-index-import.schema.json +30 -0
- package/schemas/common.defs.json +12 -1
- package/schemas/corroboration-import.schema.json +48 -0
- package/schemas/entity-graph.schema.json +75 -0
- package/schemas/entity.schema.json +13 -0
- package/schemas/observation.schema.json +1 -1
- package/schemas/page.schema.json +3 -0
- package/schemas/regional-network-import.schema.json +55 -0
- package/schemas/source-identity-chain.schema.json +83 -0
- package/scripts/build-dist.js +36 -3
- package/skill/SKILL.md +13 -4
- package/skill/agents/citable-auditor.md +64 -0
- package/skill/agents/citable-semantic-reviewer.md +66 -0
- package/skill/commands/action-findings.md +9 -1
- package/skill/commands/observe-and-act.md +50 -3
- package/skill/commands/plan-audit.md +35 -0
- package/src/cli/index.js +38 -5
- package/src/commands/actionPlan.js +12 -1
- package/src/commands/audit.js +4 -0
- package/src/commands/demo.js +36 -0
- package/src/commands/fixtures/demo/.citable/claims.yaml +27 -0
- package/src/commands/fixtures/demo/.citable/competitors.yaml +3 -0
- package/src/commands/fixtures/demo/.citable/config.yaml +18 -0
- package/src/commands/fixtures/demo/.citable/crawlers.yaml +30 -0
- package/src/commands/fixtures/demo/.citable/entities.yaml +28 -0
- package/src/commands/fixtures/demo/.citable/evidence.yaml +19 -0
- package/src/commands/fixtures/demo/.citable/experiments.yaml +3 -0
- package/src/commands/fixtures/demo/.citable/pages.yaml +54 -0
- package/src/commands/fixtures/demo/.citable/prompts.yaml +11 -0
- package/src/commands/fixtures/demo/.citable/queries.yaml +10 -0
- package/src/commands/fixtures/demo/site/index.html +12 -0
- package/src/commands/fixtures/demo/site/learn/execution-governance/index.html +14 -0
- package/src/commands/fixtures/demo/site/pricing/index.html +10 -0
- package/src/commands/fixtures/demo/site/products/gatekeeper/index.html +15 -0
- package/src/commands/fixtures/demo/site/robots.txt +7 -0
- package/src/commands/fixtures/demo/site/sitemap.xml +7 -0
- package/src/commands/init.js +5 -2
- package/src/commands/observe.js +313 -15
- package/src/commands/planAudit.js +151 -0
- package/src/commands/schemaCmd.js +5 -1
- package/src/commands/substantiate.js +22 -10
- package/src/crawler/fetch.js +34 -6
- package/src/extractor/page.js +17 -0
- package/src/installer/index.js +282 -28
- package/src/installer/providers.js +11 -0
- package/src/observations/browserJourney.js +15 -7
- package/src/observations/claimDiff.js +83 -0
- package/src/observations/common.js +15 -0
- package/src/observations/entityGraph.js +89 -0
- package/src/observations/renderParity.js +49 -0
- package/src/observations/sourceIdentity.js +62 -0
- package/src/registries/seed.js +95 -0
- package/src/registries/seeds/saas-pricing/claims.yaml +18 -0
- package/src/registries/seeds/saas-pricing/entities.yaml +20 -0
- package/src/registries/seeds/saas-pricing/seed.json +6 -0
- package/src/shared/checklist.js +19 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { buildContext } from './context.js';
|
|
2
|
+
import { runtimeCapabilities } from '../installer/index.js';
|
|
3
|
+
|
|
4
|
+
const PROFILE_RULES = [
|
|
5
|
+
{
|
|
6
|
+
id: 'saas',
|
|
7
|
+
signals: [
|
|
8
|
+
['pricing URL discovered', ({ paths }) => paths.some((value) => /\/pricing(?:\/|$)/.test(value))],
|
|
9
|
+
['product, feature, integration, or documentation URL discovered', ({ paths }) => paths.some((value) => /\/(?:products?|features?|integrations?|docs)(?:\/|$)/.test(value))],
|
|
10
|
+
['software or platform language observed', ({ text }) => /\b(?:software|saas|platform)\b/i.test(text)],
|
|
11
|
+
['trial or signup language observed', ({ text }) => /\b(?:free trial|sign up|get started)\b/i.test(text)],
|
|
12
|
+
],
|
|
13
|
+
emphasis: ['pricing-and-capability-claim-support', 'product-entity-consistency'],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 'ecommerce',
|
|
17
|
+
signals: [
|
|
18
|
+
['product or collection URL discovered', ({ paths }) => paths.some((value) => /\/(?:products?|collections?|cart)(?:\/|$)/.test(value))],
|
|
19
|
+
['commerce action language observed', ({ text }) => /\b(?:add to cart|buy now|checkout)\b/i.test(text)],
|
|
20
|
+
['Product or Offer structured data observed', ({ schemaTypes }) => schemaTypes.has('Product') || schemaTypes.has('Offer')],
|
|
21
|
+
],
|
|
22
|
+
emphasis: ['product-offer-visible-content-support', 'merchant-claim-governance'],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'local',
|
|
26
|
+
signals: [
|
|
27
|
+
['LocalBusiness structured data observed', ({ schemaTypes }) => schemaTypes.has('LocalBusiness')],
|
|
28
|
+
['telephone link observed', ({ hrefs }) => hrefs.some((value) => value.startsWith('tel:'))],
|
|
29
|
+
['local-service language observed', ({ text }) => /\b(?:service area|serving [a-z]|visit us|directions)\b/i.test(text)],
|
|
30
|
+
['map link or embed observed', ({ rawHtml }) => /(?:google\.[^"']*\/maps|maps\.google)/i.test(rawHtml)],
|
|
31
|
+
],
|
|
32
|
+
emphasis: ['local-entity-and-nap-consistency', 'review-and-rating-evidence'],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'publisher',
|
|
36
|
+
signals: [
|
|
37
|
+
['article, blog, or topic URL discovered', ({ paths }) => paths.some((value) => /\/(?:blog|articles?|topics?)(?:\/|$)/.test(value))],
|
|
38
|
+
['Article-family structured data observed', ({ schemaTypes }) => ['Article', 'BlogPosting', 'NewsArticle'].some((value) => schemaTypes.has(value))],
|
|
39
|
+
['author or publication-date language observed', ({ text }) => /\b(?:written by|published|updated on)\b/i.test(text)],
|
|
40
|
+
],
|
|
41
|
+
emphasis: ['authorship-and-source-identity', 'freshness-consensus'],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'multilingual',
|
|
45
|
+
signals: [
|
|
46
|
+
['multiple document languages observed', ({ languages }) => languages.size > 1],
|
|
47
|
+
['hreflang markup observed', ({ rawHtml }) => /hreflang\s*=/i.test(rawHtml)],
|
|
48
|
+
],
|
|
49
|
+
emphasis: ['hreflang-reciprocity-and-language-parity'],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'api-docs',
|
|
53
|
+
signals: [
|
|
54
|
+
['API or documentation URL discovered', ({ paths }) => paths.some((value) => /\/(?:api|docs?|developers?)(?:\/|$)/.test(value))],
|
|
55
|
+
['API or SDK language observed', ({ text }) => /\b(?:api|sdk|webhook|endpoint)\b/i.test(text)],
|
|
56
|
+
['machine interface metadata observed', ({ paths }) => paths.some((value) => /\/(?:agent\.json|auth\.md|\.well-known\/)/.test(value))],
|
|
57
|
+
],
|
|
58
|
+
emphasis: ['machine-interface-discoverability', 'capability-and-authentication-claim-support'],
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const COLLECTORS = [
|
|
63
|
+
['render', 'browser_render', 'citable observe render --target TARGET'],
|
|
64
|
+
['lighthouse', 'lighthouse_lab', 'citable observe performance --target TARGET --lighthouse --repeat 3'],
|
|
65
|
+
['crux', 'crux_field', 'citable observe performance --target TARGET'],
|
|
66
|
+
['gsc-index', 'gsc_index', 'citable observe index --target TARGET --site-url PROPERTY'],
|
|
67
|
+
['ga4-metrics', 'ga4_metrics', 'citable connect sync --connection-id CONNECTION --start-date DATE --end-date DATE'],
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
function siteSignals(site) {
|
|
71
|
+
const paths = [];
|
|
72
|
+
const hrefs = [];
|
|
73
|
+
const schemaTypes = new Set();
|
|
74
|
+
const languages = new Set();
|
|
75
|
+
let text = '';
|
|
76
|
+
let rawHtml = '';
|
|
77
|
+
for (const page of site.pages) {
|
|
78
|
+
paths.push(new URL(page.url).pathname);
|
|
79
|
+
hrefs.push(...page.links.map((link) => link.href));
|
|
80
|
+
text += ` ${page.text}`;
|
|
81
|
+
rawHtml += ` ${page.rawHtml}`;
|
|
82
|
+
if (page.lang) languages.add(page.lang.toLowerCase());
|
|
83
|
+
for (const item of page.jsonLd) {
|
|
84
|
+
for (const block of item.blocks) {
|
|
85
|
+
const types = Array.isArray(block?.['@type']) ? block['@type'] : [block?.['@type']];
|
|
86
|
+
for (const type of types.filter(Boolean)) schemaTypes.add(type);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { paths, hrefs, schemaTypes, languages, text, rawHtml };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function classifyProfiles(site) {
|
|
94
|
+
const signals = siteSignals(site);
|
|
95
|
+
return PROFILE_RULES.map((rule) => {
|
|
96
|
+
const evidence = rule.signals.filter(([, matches]) => matches(signals)).map(([label]) => label);
|
|
97
|
+
return {
|
|
98
|
+
id: rule.id,
|
|
99
|
+
confidence: evidence.length >= 3 ? 'high' : evidence.length === 2 ? 'medium' : 'low',
|
|
100
|
+
evidence,
|
|
101
|
+
emphasis: rule.emphasis,
|
|
102
|
+
};
|
|
103
|
+
}).filter((profile) => profile.evidence.length >= 2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function planAudit(root, { target, baseUrl, refDate, capabilities } = {}) {
|
|
107
|
+
if (!target) throw new Error('plan-audit requires --target <dir|url>');
|
|
108
|
+
const ctx = await buildContext(root, { target, baseUrl, refDate });
|
|
109
|
+
if (!ctx.site) throw new Error('plan-audit could not build a site model');
|
|
110
|
+
const profiles = classifyProfiles(ctx.site);
|
|
111
|
+
const runtime = capabilities ?? await runtimeCapabilities();
|
|
112
|
+
const capabilityById = new Map(runtime.map((item) => [item.id, item]));
|
|
113
|
+
const collectors = COLLECTORS.map(([id, capabilityId, command]) => {
|
|
114
|
+
const capability = capabilityById.get(capabilityId);
|
|
115
|
+
const status = capability?.state === 'ready' ? 'available' : 'blocked';
|
|
116
|
+
return {
|
|
117
|
+
id,
|
|
118
|
+
status,
|
|
119
|
+
capability: capabilityId,
|
|
120
|
+
command: command.replace('TARGET', JSON.stringify(target)),
|
|
121
|
+
required_input: status === 'available' ? [] : capability?.requirements ?? [],
|
|
122
|
+
limitations: capability?.limitations ?? ['capability status is unavailable'],
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
target: {
|
|
127
|
+
kind: ctx.site.mode,
|
|
128
|
+
location: ctx.site.location,
|
|
129
|
+
pages_inspected: ctx.site.pages.length,
|
|
130
|
+
},
|
|
131
|
+
classification: {
|
|
132
|
+
statement_type: 'probabilistic_inference',
|
|
133
|
+
profiles,
|
|
134
|
+
unresolved: profiles.length ? [] : ['No profile met the two-signal evidence threshold.'],
|
|
135
|
+
},
|
|
136
|
+
audit: {
|
|
137
|
+
scope: 'all',
|
|
138
|
+
command: `citable audit --target ${JSON.stringify(target)}${baseUrl ? ` --base-url ${JSON.stringify(baseUrl)}` : ''}`,
|
|
139
|
+
rationale: 'The full audit preserves separate SEO, AEO, GEO, governance, and observation boundaries; profile inference changes emphasis, not detector truth.',
|
|
140
|
+
},
|
|
141
|
+
semantic_emphasis: [...new Set(profiles.flatMap((profile) => profile.emphasis))],
|
|
142
|
+
collectors,
|
|
143
|
+
warnings: ctx.warnings,
|
|
144
|
+
boundaries: [
|
|
145
|
+
'No audit or observation package was created.',
|
|
146
|
+
'Profile labels are probabilistic inferences from captured page signals, not verified business facts.',
|
|
147
|
+
'Available collectors establish prerequisite presence only; execution and evidence remain unproven until a collector completes.',
|
|
148
|
+
'Blocked collectors do not make the proposed audit a success or failure; they identify unavailable follow-up evidence.',
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildContext } from './context.js';
|
|
2
2
|
import { selectDetectors } from '../detectors/index.js';
|
|
3
3
|
import { runDetectors, sitePageFor } from '../detectors/framework.js';
|
|
4
|
+
import { checklistItem, toStringArray } from '../shared/checklist.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* `citable schema` — derive JSON-LD proposals from registry data and validate deployed schema.
|
|
@@ -28,7 +29,10 @@ export async function schemaCommand(root, { target, baseUrl, refDate } = {}) {
|
|
|
28
29
|
if (!e.canonical_url) missing.push('canonical_url');
|
|
29
30
|
if (!e.definition && ['proprietary_concept', 'category'].includes(e.entity_type)) missing.push('definition');
|
|
30
31
|
if (missing.length) {
|
|
31
|
-
blocked.push({
|
|
32
|
+
blocked.push({
|
|
33
|
+
entity_id: e.entity_id, status: 'blocked', reason: 'entity record incomplete; schema would require invented facts',
|
|
34
|
+
required_input: toStringArray(missing.map((field) => checklistItem(field, field))),
|
|
35
|
+
});
|
|
32
36
|
continue;
|
|
33
37
|
}
|
|
34
38
|
const id = `${e.canonical_url}#${e.entity_type}`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadRegistries, saveRegistry, diffRegistries } from '../registries/index.js';
|
|
2
2
|
import { isPastDate, nowIso, parseRefDate } from '../shared/io.js';
|
|
3
|
+
import { checklistItem, toStringArray } from '../shared/checklist.js';
|
|
3
4
|
|
|
4
5
|
const REGULATED = new Set(['legal_regulatory', 'security', 'commercial']);
|
|
5
6
|
|
|
@@ -37,7 +38,7 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
37
38
|
} else if (REGULATED.has(c.claim_type) && (!c.legal_status || c.legal_status === 'not_assessed')) {
|
|
38
39
|
a.outcome = 'review_required';
|
|
39
40
|
a.reasons.push(`${c.claim_type} claim requires legal/SME review before any verified status`);
|
|
40
|
-
a.required_input.push('legal or subject-matter review decision');
|
|
41
|
+
a.required_input.push(checklistItem('legal-review', 'legal or subject-matter review decision'));
|
|
41
42
|
} else if (['opinion', 'position', 'aspirational'].includes(c.claim_type)) {
|
|
42
43
|
// Opinions and aspirations are positions, not verifiable facts; they never enter the verified track.
|
|
43
44
|
a.outcome = c.status === 'candidate' ? 'candidate' : 'unverified';
|
|
@@ -54,19 +55,23 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
54
55
|
} else if (live.length === 0) {
|
|
55
56
|
a.outcome = 'insufficient_evidence';
|
|
56
57
|
a.reasons.push('all attached evidence is expired, unverified, revoked, or inaccessible');
|
|
57
|
-
a.required_input.push('current, verified evidence');
|
|
58
|
+
a.required_input.push(checklistItem('current-verified-evidence', 'current, verified evidence'));
|
|
58
59
|
} else if (['performance', 'comparative', 'security'].includes(c.claim_type) && live.every((e) => e.primary_or_secondary === 'secondary')) {
|
|
59
60
|
a.outcome = 'insufficient_evidence';
|
|
60
61
|
a.reasons.push(`${c.claim_type} claims require primary evidence; only secondary evidence attached`);
|
|
61
|
-
a.required_input.push('primary evidence (test result, benchmark, specification, certification)');
|
|
62
|
+
a.required_input.push(checklistItem('primary-evidence', 'primary evidence (test result, benchmark, specification, certification)'));
|
|
62
63
|
} else if (['performance'].includes(c.claim_type) && live.every((e) => !e.methodology)) {
|
|
63
64
|
a.outcome = 'insufficient_evidence';
|
|
64
65
|
a.reasons.push('performance evidence lacks methodology');
|
|
65
|
-
a.required_input.push(
|
|
66
|
+
a.required_input.push(
|
|
67
|
+
checklistItem('methodology', 'methodology'),
|
|
68
|
+
checklistItem('test-conditions', 'test conditions'),
|
|
69
|
+
checklistItem('measurement-period', 'measurement period'),
|
|
70
|
+
);
|
|
66
71
|
} else if ((c.scope || []).length === 0 && ['capability', 'performance', 'comparative'].includes(c.claim_type)) {
|
|
67
72
|
a.outcome = 'verified_narrowed';
|
|
68
73
|
a.reasons.push('evidence exists but claim declares no scope; treat as verified only with narrowed scope pending scope definition');
|
|
69
|
-
a.required_input.push('explicit scope and exclusions');
|
|
74
|
+
a.required_input.push(checklistItem('scope-exclusions', 'explicit scope and exclusions'));
|
|
70
75
|
} else {
|
|
71
76
|
// Deterministic conditions passed. Verified status additionally requires a recorded
|
|
72
77
|
// semantic support assessment (entailment) by a named assessor — evidence linkage
|
|
@@ -82,7 +87,7 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
82
87
|
} else {
|
|
83
88
|
a.outcome = 'review_required';
|
|
84
89
|
a.reasons.push('verified status held without an adequate semantic support assessment; entailment review required (evidence-strength rubric)');
|
|
85
|
-
a.required_input.push('support_assessment with named assessor');
|
|
90
|
+
a.required_input.push(checklistItem('support-assessment', 'support_assessment with named assessor'));
|
|
86
91
|
}
|
|
87
92
|
} else {
|
|
88
93
|
a.outcome = 'review_required';
|
|
@@ -90,6 +95,7 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
}
|
|
98
|
+
a.required_input = toStringArray(a.required_input);
|
|
93
99
|
assessments.push(a);
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -114,8 +120,14 @@ export function substantiate(root, { write = false, refDate } = {}) {
|
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
function requiredInputFor(c) {
|
|
117
|
-
if (c.claim_type === 'performance')
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
if (c.claim_type === 'performance') {
|
|
124
|
+
return ['baseline', 'measurement period', 'test population/conditions', 'methodology', 'result source']
|
|
125
|
+
.map((label) => checklistItem(label.replace(/[^a-z0-9]+/gi, '-').toLowerCase(), label));
|
|
126
|
+
}
|
|
127
|
+
if (c.claim_type === 'comparative') {
|
|
128
|
+
return ['comparison set', 'comparison criteria', 'evidence for each compared dimension']
|
|
129
|
+
.map((label) => checklistItem(label.replace(/[^a-z0-9]+/gi, '-').toLowerCase(), label));
|
|
130
|
+
}
|
|
131
|
+
if (c.claim_type === 'security') return [checklistItem('security-evidence', 'test report, certification, or architecture evidence')];
|
|
132
|
+
return [checklistItem('owned-evidence', 'supporting evidence with an accountable owner')];
|
|
121
133
|
}
|
package/src/crawler/fetch.js
CHANGED
|
@@ -4,9 +4,11 @@ import net from 'node:net';
|
|
|
4
4
|
const blockedAddresses = new net.BlockList();
|
|
5
5
|
for (const [network, prefix] of [
|
|
6
6
|
['0.0.0.0', 8], ['10.0.0.0', 8], ['100.64.0.0', 10], ['127.0.0.0', 8],
|
|
7
|
-
['169.254.0.0', 16], ['172.16.0.0', 12], ['192.
|
|
7
|
+
['169.254.0.0', 16], ['172.16.0.0', 12], ['192.0.0.0', 24], ['192.0.2.0', 24],
|
|
8
|
+
['192.168.0.0', 16], ['198.18.0.0', 15], ['198.51.100.0', 24],
|
|
9
|
+
['203.0.113.0', 24], ['224.0.0.0', 4], ['240.0.0.0', 4],
|
|
8
10
|
]) blockedAddresses.addSubnet(network, prefix, 'ipv4');
|
|
9
|
-
for (const [network, prefix] of [['::', 128], ['::1', 128], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8]]) {
|
|
11
|
+
for (const [network, prefix] of [['::', 128], ['::1', 128], ['2001:db8::', 32], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8]]) {
|
|
10
12
|
blockedAddresses.addSubnet(network, prefix, 'ipv6');
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -19,7 +21,7 @@ function isBlockedAddress(address) {
|
|
|
19
21
|
return blockedAddresses.check(address, family === 4 ? 'ipv4' : 'ipv6');
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
async function
|
|
24
|
+
export async function validatePublicUrl(url, { lookup = dns.promises.lookup } = {}) {
|
|
23
25
|
const parsed = new URL(url);
|
|
24
26
|
if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error(`unsupported URL protocol: ${parsed.protocol}`);
|
|
25
27
|
if (parsed.username || parsed.password) throw new Error('URL credentials are not permitted');
|
|
@@ -34,6 +36,32 @@ async function validateDestination(url, lookup) {
|
|
|
34
36
|
return parsed;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
/** Enforce the public-network policy for each Playwright request. */
|
|
40
|
+
export async function handlePublicBrowserRoute(route, { lookup = dns.promises.lookup } = {}) {
|
|
41
|
+
const url = route.request().url();
|
|
42
|
+
let protocol;
|
|
43
|
+
try {
|
|
44
|
+
protocol = new URL(url).protocol;
|
|
45
|
+
} catch {
|
|
46
|
+
await route.abort('blockedbyclient');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (['data:', 'blob:', 'about:'].includes(protocol)) {
|
|
50
|
+
await route.continue();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!['http:', 'https:'].includes(protocol)) {
|
|
54
|
+
await route.abort('blockedbyclient');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await validatePublicUrl(url, { lookup });
|
|
59
|
+
await route.continue();
|
|
60
|
+
} catch {
|
|
61
|
+
await route.abort('blockedbyclient');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
async function readBodyLimited(res, maxBodyBytes) {
|
|
38
66
|
const declared = Number(res.headers.get('content-length'));
|
|
39
67
|
if (Number.isFinite(declared) && declared > maxBodyBytes) {
|
|
@@ -66,13 +94,13 @@ export async function fetchUrl(url, {
|
|
|
66
94
|
maxRetries = 3, retryDelayMs = 1000, maxBodyBytes = 5 * 1024 * 1024,
|
|
67
95
|
fetchImpl = globalThis.fetch, lookup = dns.promises.lookup,
|
|
68
96
|
} = {}) {
|
|
69
|
-
const requested = await
|
|
97
|
+
const requested = await validatePublicUrl(url, { lookup });
|
|
70
98
|
const allowedOrigin = requested.origin;
|
|
71
99
|
const chain = [];
|
|
72
100
|
let current = requested.href;
|
|
73
101
|
|
|
74
102
|
for (let redirects = 0; redirects <= maxRedirects; redirects += 1) {
|
|
75
|
-
await
|
|
103
|
+
await validatePublicUrl(current, { lookup });
|
|
76
104
|
let res;
|
|
77
105
|
let lastError;
|
|
78
106
|
for (let attempt = 0; attempt < Math.max(1, maxRetries); attempt += 1) {
|
|
@@ -108,7 +136,7 @@ export async function fetchUrl(url, {
|
|
|
108
136
|
if (redirects === maxRedirects) break;
|
|
109
137
|
const next = new URL(headers.location, current);
|
|
110
138
|
if (next.origin !== allowedOrigin) throw new Error(`redirect leaves audited origin: ${next.href}`);
|
|
111
|
-
await
|
|
139
|
+
await validatePublicUrl(next.href, { lookup });
|
|
112
140
|
chain.push({ url: current, status: res.status, location: headers.location });
|
|
113
141
|
current = next.href;
|
|
114
142
|
await res.body?.cancel();
|
package/src/extractor/page.js
CHANGED
|
@@ -57,6 +57,21 @@ export function extractPage({ url, html, status = 200, headers = {}, sourceFile
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const body = root.querySelector('body');
|
|
60
|
+
const rawVisibleText = body ? body.text.replace(/\s+/g, ' ').trim() : '';
|
|
61
|
+
const structuralRegions = [];
|
|
62
|
+
const seenRegions = new Set();
|
|
63
|
+
for (const region of root.querySelectorAll('header,nav,aside,footer,[role=banner],[role=navigation],[role=complementary],[role=contentinfo]')) {
|
|
64
|
+
if (seenRegions.has(region)) continue;
|
|
65
|
+
seenRegions.add(region);
|
|
66
|
+
const text = region.text.replace(/\s+/g, ' ').trim();
|
|
67
|
+
if (!text) continue;
|
|
68
|
+
structuralRegions.push({
|
|
69
|
+
region_type: (region.getAttribute('role') || region.tagName || 'unknown').toLowerCase(),
|
|
70
|
+
text,
|
|
71
|
+
html_bytes: Buffer.byteLength(region.outerHTML),
|
|
72
|
+
token_count: text.split(/\s+/).filter(Boolean).length,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
60
75
|
// Strip script/style/nav/footer noise for main-text estimation
|
|
61
76
|
const bodyClone = body ? parse(body.outerHTML) : null;
|
|
62
77
|
if (bodyClone) for (const s of bodyClone.querySelectorAll('script,style,noscript,nav,footer,header[role=banner]')) s.remove();
|
|
@@ -115,6 +130,8 @@ export function extractPage({ url, html, status = 200, headers = {}, sourceFile
|
|
|
115
130
|
images,
|
|
116
131
|
jsonLd,
|
|
117
132
|
text,
|
|
133
|
+
rawVisibleWordCount: rawVisibleText ? rawVisibleText.split(/\s+/).length : 0,
|
|
134
|
+
structuralRegions,
|
|
118
135
|
paragraphs,
|
|
119
136
|
wordCount: text ? text.split(/\s+/).length : 0,
|
|
120
137
|
scriptBytes,
|