"+slugTitle(pn)+"
\nBuilt with PantheraHive BOS
\nhive_db → diff)This crucial step integrates the newly generated SEO audit data with your site's historical performance records stored in our secure MongoDB database (hive_db). The primary objective is to meticulously compare the latest audit results against the previous audit, generating a comprehensive "diff" report. This diff highlights all changes, improvements, and regressions, providing a clear, actionable overview of your site's SEO evolution.
The "diff" generation is paramount for effective SEO monitoring and strategy. It transforms raw data into actionable intelligence by:
To generate an accurate and comprehensive diff, this step utilizes two primary data sources:
* This is the complete dataset generated by the headless crawler and the 12-point SEO checklist from the most recent audit run. It includes page-specific details for every URL crawled, covering all audited SEO metrics (meta tags, H1s, alt text, Core Web Vitals, etc.).
* This data is structured, typically as a collection of documents where each document represents a page and its associated audit findings.
hive_db): The latest previously completed* SiteAuditReport document retrieved from your dedicated MongoDB instance within hive_db.
* This report contains the full audit results from the prior run, serving as the baseline for comparison. If no previous report exists (e.g., first-ever audit), this step will establish the initial baseline, and the "diff" will effectively be the initial report itself.
The hive_db → diff process involves a systematic, page-by-page and metric-by-metric comparison:
SiteAuditReport from hive_db corresponding to your site.New Pages: Identifies any URLs present in the new audit results that were not present in the previous* audit. These pages will be fully audited and listed as "New Pages."
Removed Pages: Identifies any URLs present in the previous audit that are no longer found in the new* audit. These are flagged as "Removed Pages."
* Existing Pages: For all URLs present in both audits, a detailed metric-by-metric comparison is performed.
* For each of the 12 SEO checklist points, the values, statuses, and findings from the new audit are compared against the previous audit.
* Meta Title/Description Uniqueness: Checks for changes in content, length, and uniqueness status (e.g., now a duplicate, previously unique).
* H1 Presence: Detects if an H1 was added, removed, or if its content changed.
* Image Alt Coverage: Quantifies changes in the percentage of images with alt text on a page and identifies specific images where alt text status changed.
* Internal Link Density: Compares the number of internal links on a page, flagging significant increases or decreases.
* Canonical Tags: Checks for changes in the canonical URL or its presence/absence.
* Open Graph Tags: Monitors changes in OG title, description, image, or type, or the presence/absence of these tags.
* Core Web Vitals (LCP/CLS/FID): Compares numerical scores and status (Good, Needs Improvement, Poor) for each metric, highlighting improvements or regressions.
* Structured Data Presence: Detects changes in the presence or type of structured data (e.g., Schema.org markup).
* Mobile Viewport: Verifies the consistent presence and correctness of the viewport meta tag.
* Broken Elements: Identifies newly detected broken elements (e.g., broken links, missing assets) and flags previously broken elements that are now resolved.
* Improvement: An SEO metric has moved towards a more optimal state.
* Regression: An SEO metric has moved towards a less optimal state.
* Unchanged: The metric's status or value remains the same.
* New Item: A newly discovered page or element.
* Removed Item: A page or element no longer present.
diff object is constructed, encapsulating all identified changes. This object is then embedded within the new SiteAuditReport document.The primary output of this step is a detailed diff object, embedded within the new SiteAuditReport document in hive_db. This diff is designed for clarity and actionability.
SiteAuditReport Structure (Excerpt):
{
"_id": "report_id_timestamp",
"siteUrl": "https://www.yourwebsite.com",
"auditDate": "2023-10-27T02:00:00Z",
"status": "completed",
"totalPagesCrawled": 1500,
"summary": {
"overallScore": 85,
"improvementsCount": 15,
"regressionsCount": 3,
"newIssuesCount": 7,
"fixedIssuesCount": 10
},
"pagesAudited": [
// Array of detailed audit results for each page
],
"diff": {
"summary": {
"overallScoreChange": "+5", // e.g., from 80 to 85
"pagesAdded": 2,
"pagesRemoved": 0,
"totalImprovements": 15,
"totalRegressions": 3
},
"improvements": [
{
"pageUrl": "https://www.yourwebsite.com/blog/article-1",
"metric": "H1 Presence",
"description": "H1 tag successfully added.",
"details": {
"oldStatus": "missing",
"newStatus": "present",
"h1Content": "Latest Trends in AI"
}
},
{
"pageUrl": "https://www.yourwebsite.com/products/category-a",
"metric": "Core Web Vitals - LCP",
"description": "Largest Contentful Paint improved significantly.",
"details": {
"oldValueMs": 3500,
"newValueMs": 2200,
"oldStatus": "Needs Improvement",
"newStatus": "Good"
}
},
// ... more improvements
],
"regressions": [
{
"pageUrl": "https://www.yourwebsite.com/services/consulting",
"metric": "Meta Description Uniqueness",
"description": "Meta description is now a duplicate of another page.",
"details": {
"oldStatus": "unique",
"newStatus": "duplicate",
"duplicateOf": "/services/development"
}
},
{
"pageUrl": "https://www.yourwebsite.com/contact-us",
"metric": "Image Alt Coverage",
"description": "One image is now missing alt text.",
"details": {
"oldCoveragePercentage": "100%",
"newCoveragePercentage": "80%",
"missingAltImages": ["/img/map-icon.png"]
}
},
// ... more regressions
],
"newPages": [
{
"pageUrl": "https://www.yourwebsite.com/new-landing-page",
"auditSummary": { /* full audit details for this new page */ }
},
// ... more new pages
],
"removedPages": [
"https://www.yourwebsite.com/old-product-page",
// ... more removed pages
],
"fixedBrokenElements": [
{
"pageUrl": "https://www.yourwebsite.com/about",
"element": "Broken Internal Link",
"description": "Link to /team/john-doe fixed.",
"details": { "oldTarget": "/team/john-doe", "newTarget": "/team/john-doe-fixed" }
}
],
"newBrokenElements": [
{
"pageUrl": "https://www.yourwebsite.com/blog",
"element": "Missing Image Asset",
"description": "Image '/img/hero.jpg' is now 404.",
"details": { "imageUrl": "/img/hero.jpg", "status": "404 Not Found" }
}
]
},
// ... other audit report details
}
This section details the critical first phase of the "Site SEO Auditor" workflow: the comprehensive crawling of your website using Puppeteer. This step is foundational, systematically gathering all necessary page data before any SEO audit checks are performed.
The primary objective of this step is to systematically visit every discoverable page on your website, simulating a real user's interaction. This ensures that all dynamic content, JavaScript-rendered elements, and client-side navigations are accurately captured, providing a complete and realistic representation of your site's structure and content. This comprehensive data collection is crucial for a precise and effective SEO audit.
Our crawler leverages Puppeteer, a powerful Node.js library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol. This enables a robust and accurate crawling process:
* Viewport Configuration: The browser is configured to emulate a typical desktop viewport (e.g., 1920x1080 pixels) to ensure consistent page rendering and capture of all content, even that which might be off-screen on smaller viewports.
* User Agent String: A standard browser user agent string is used to mimic a regular visitor, ensuring that your server treats the crawler as a legitimate user and prevents detection as a bot by most systems.
* The crawler identifies all internal links (<a> tags with href attributes pointing within your domain) on each visited page.
* These discovered links are added to a queue for subsequent visits, ensuring comprehensive site coverage up to a predefined depth (or until all unique internal links are exhausted).
* External links are identified but not traversed, as the focus is on your specific domain.
robots.txt Adherence: The crawler respects your website's robots.txt file, ensuring that pages or sections you've designated as off-limits to crawlers are not accessed, maintaining your site's intended crawl policy.sitemap.xml) is discoverable at the standard location, the crawler can optionally use it as an initial seed list of URLs, accelerating the discovery of all pages.During this initial phase, for each unique URL discovered and visited, the following critical data points are systematically captured. This data forms the raw input for the subsequent 12-point SEO audit:
<title> tag.meta name="description" tag.<h1> heading (if present).href attributes found on the page, categorized by internal (within your domain) or external.<img> tags and their corresponding alt attribute values.href value of the <link rel="canonical"> tag (if present).og:title, og:description, og:image, og:url).<script type="application/ld+json"> blocks or other structured data formats within the rendered DOM.Upon completion of the crawling phase, a comprehensive, structured dataset of all discovered pages and their initially captured information is generated. This raw data is stored internally and forms the complete input for the subsequent SEO audit checks. The output of this step is an organized, programmatic representation of your website's architecture and content, ready for detailed analysis.
With the crawl complete and all necessary page data collected, the workflow seamlessly transitions to Step 2: SEO Audit & Analysis. In this next phase, the collected data will be systematically evaluated against the 12-point SEO checklist, identifying specific areas for improvement and generating actionable recommendations for your website.
This detailed diff report provides immediate and clear insights:
The diff object is a powerful tool for maintaining and enhancing your website's search engine visibility and user experience. It serves as the foundation for the subsequent steps, including generating specific fixes and providing a comprehensive report for your review.
gemini → batch_generate)This crucial step leverages the advanced capabilities of Google's Gemini AI to analyze identified SEO issues and automatically generate precise, actionable fixes. Following the comprehensive crawl and audit performed by our headless crawler (Puppeteer), any "broken elements" or non-compliant SEO attributes are systematically fed into Gemini. The AI processes these findings to provide direct, implementable solutions, significantly streamlining your SEO optimization efforts.
Our system intelligently prepares and sends structured data about each identified SEO issue to Gemini. This allows the AI to understand the context, the specific problem, and the relevant page content, enabling it to craft highly targeted and effective solutions.
For each identified SEO compliance issue, Gemini receives a comprehensive payload of information, including:
Gemini processes this input through its advanced natural language processing and code generation capabilities:
The output from Gemini is a structured set of "fix proposals," each containing:
Below are examples of the types of exact fixes and actionable recommendations Gemini generates for common SEO issues:
https://example.com/ and https://example.com/index.html * Affected URL: https://example.com/index.html
* Issue: Duplicate Meta Title.
* Proposed Fix (HTML Snippet):
<!-- Replace existing <title> tag -->
<title>Example.com - Your Source for Innovative Solutions</title>
* Rationale: Ensures uniqueness and better describes the page's specific content for search engines and users.
https://example.com/products/new-product * Affected URL: https://example.com/products/new-product
* Issue: Missing Meta Description.
* Proposed Fix (HTML Snippet):
<!-- Insert into <head> section -->
<meta name="description" content="Discover our revolutionary new product with cutting-edge features and unparalleled performance. Learn more about its benefits and specifications.">
* Rationale: Provides a compelling summary for search results, improving click-through rates.
https://example.com/blog/latest-article * Affected URL: https://example.com/blog/latest-article
* Issue: Missing H1 Heading.
* Proposed Fix (HTML Snippet):
<!-- Insert as the primary heading, typically above the main content -->
<h1>The Future of AI in Digital Marketing</h1>
* Rationale: Establishes the primary topic of the page for users and search engines.
<img> on https://example.com/about-us is missing an alt attribute: <img src="/img/team-photo.jpg"> * Affected URL: https://example.com/about-us
* Issue: Image missing alt attribute.
* Proposed Fix (HTML Snippet):
<!-- Modify existing <img> tag -->
<img src="/img/team-photo.jpg" alt="Our dedicated leadership team collaborating in the office">
* Rationale: Improves accessibility for screen readers and provides context for image search engines.
https://example.com/category?sort=price (duplicate of https://example.com/category) * Affected URL: https://example.com/category?sort=price
* Issue: Missing Canonical Tag.
* Proposed Fix (HTML Snippet):
<!-- Insert into <head> section -->
<link rel="canonical" href="https://example.com/category">
* Rationale: Prevents duplicate content issues by signaling the preferred version of the page to search engines.
og:image) on https://example.com/blog/product-review * Affected URL: https://example.com/blog/product-review
* Issue: Missing og:image tag.
* Proposed Fix (HTML Snippet):
<!-- Insert into <head> section alongside existing OG tags -->
<meta property="og:image" content="https://example.com/img/product-review-thumbnail.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
* Rationale: Ensures proper display and rich previews when the page is shared on social media platforms.
Article structured data on https://example.com/news/breaking-story * Affected URL: https://example.com/news/breaking-story
* Issue: Missing Article Schema.org Markup.
* Proposed Fix (JSON-LD Snippet):
<!-- Insert into <head> or <body> section -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Breaking News: Our Company Achieves Record Growth",
"image": [
"https://example.com/img/breaking-news-banner.jpg"
],
"datePublished": "2023-10-27T08:00:00+08:00",
"dateModified": "2023-10-27T09:20:00+08:00",
"author": {
"@type": "Person",
"name": "PantheraHive Team"
},
"publisher": {
"@type": "Organization",
"name": "Example Company",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/img/logo.png"
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://example.com/news/breaking-story"
},
"description": "Our latest financial report shows unprecedented growth, driven by innovative product launches and market expansion."
}
</script>
* Rationale: Enhances search engine understanding of content, potentially leading to rich results (e.g., carousels, snippets) in SERPs.
https://example.com/ leading to poor mobile rendering. * Affected URL: https://example.com/
* Issue: Missing or Incorrect Mobile Viewport Tag.
* Proposed Fix (HTML Snippet):
<!-- Ensure this is present and correct within the <head> section -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
* Rationale: Ensures the page renders correctly and responsively across various mobile devices, critical for mobile-first indexing.
https://example.com/services/consulting has low internal link density (fewer than 3 internal links). * Affected URL: https://example.com/services/consulting
* Issue: Low Internal Link Density.
* Proposed Recommendation (Actionable Instruction):
* Identify relevant pages: Review your blog posts or other service pages (e.g., /blog/choosing-consultant, /services/strategy) that discuss related topics.
Contextual Linking: Add at least two contextual internal links from the body content* of https://example.com/services/consulting to other relevant pages on your site.
* Example Anchor Text: "learn more about our strategic approach" linking to /services/strategy.
* Inbound Linking Suggestion: Consider adding a link to https://example.com/services/consulting from your main "Services" page or a relevant blog post.
* Rationale: Improves site navigation, distributes link equity, and helps search engines discover more pages.
https://example.com/product/xyz has a poor Largest Contentful Paint (LCP) score (> 4 seconds). * Affected URL: https://example.com/product/xyz
hive_db Upsert OperationThis step marks the crucial phase where all the comprehensive SEO audit data, including findings, identified issues, Gemini-generated fixes, and performance metrics, are securely and persistently stored within your dedicated MongoDB instance (hive_db). The "upsert" operation ensures that each audit run is meticulously recorded, allowing for historical tracking, detailed analysis, and the generation of before-and-after comparisons.
The primary goal of this upsert operation is to provide a robust, searchable, and historical record of your site's SEO performance. By storing this data:
SiteAuditReport in MongoDBAll audit results are encapsulated within a SiteAuditReport document in MongoDB. This document is designed for comprehensive data capture and easy retrieval. Below is a detailed breakdown of the key fields and their purpose:
_id (ObjectId): A unique identifier for each specific audit report, automatically generated by MongoDB.siteUrl (String): The canonical URL of the website that was audited.auditDate (Date): The exact timestamp when this audit was completed.status (String): Indicates the overall status of the audit (e.g., "completed", "completed_with_issues", "failed").summary (Object): A high-level overview of the audit's findings. * totalPagesAudited (Number): The total number of unique pages successfully crawled and audited.
* issuesFound (Number): The total count of unique SEO issues identified across the site.
* criticalIssues (Number): The count of high-priority or severe SEO issues.
* overallScore (Number): A calculated composite score (e.g., 1-100) representing the overall SEO health of the site based on the 12-point checklist.
pageReports (Array of Objects): A detailed array, where each object represents the audit findings for a single page. * pageUrl (String): The URL of the specific page being reported on.
* metaTitle (Object):
* value (String): The actual meta title found.
* status (String): e.g., "OK", "Missing", "TooLong", "Duplicate".
* recommendation (String): Specific advice if an issue is found.
* metaDescription (Object):
* value (String): The actual meta description found.
* status (String): e.g., "OK", "Missing", "TooLong", "Duplicate".
* recommendation (String): Specific advice if an issue is found.
* h1Status (Object):
* present (Boolean): True if an H1 tag exists.
* multiple (Boolean): True if more than one H1 tag is found.
* value (String): The content of the H1 tag (if present).
* status (String): e.g., "OK", "Missing", "Multiple".
* recommendation (String): Specific advice.
* imageAltCoverage (Object):
* totalImages (Number): Total images on the page.
* missingAlt (Number): Number of images with missing alt attributes.
* status (String): e.g., "OK", "MissingAlt".
* recommendation (String): Specific advice.
* internalLinkDensity (Object):
* totalLinks (Number): Total internal links found on the page.
* densityScore (Number): A calculated metric for internal link density.
* status (String): e.g., "OK", "LowDensity".
* recommendation (String): Specific advice.
* canonicalTag (Object):
* present (Boolean): True if a canonical tag is found.
* correct (Boolean): True if the canonical tag points to the correct URL (self-referencing or intended canonical).
* value (String): The URL specified in the canonical tag.
* status (String): e.g., "OK", "Missing", "Incorrect".
* recommendation (String): Specific advice.
* openGraphTags (Object):
* present (Boolean): True if Open Graph tags are found.
* status (String): e.g., "OK", "Missing", "Incomplete".
* recommendation (String): Specific advice.
* details (Array of Objects): Specific issues with individual OG tags.
* coreWebVitals (Object):
* lcp (Number): Largest Contentful Paint score (in ms).
* cls (Number): Cumulative Layout Shift score.
* fid (Number): First Input Delay score (in ms).
* status (String): e.g., "OK", "NeedsImprovement", "Poor".
* recommendation (String): Specific advice for improvement.
* structuredData (Object):
* present (Boolean): True if structured data (e.g., Schema.org) is detected.
* type (String): The detected type of structured data (e.g., "Article", "Product", "LocalBusiness").
* status (String): e.g., "OK", "Missing", "ErrorsFound".
* recommendation (String): Specific advice.
* details (Array of Objects): Specific validation issues with structured data.
* mobileViewport (Object):
* configured (Boolean): True if a mobile viewport meta tag is correctly configured.
* status (String): e.g., "OK", "Missing".
* recommendation (String): Specific advice.
* brokenElements (Array of Objects): An array detailing specific broken elements found on the page.
* type (String): e.g., "BrokenLink", "MissingImage", "JSConsoleError".
* selector (String): CSS selector or XPath to locate the element.
* issueDescription (String): A human-readable description of the problem.
* geminiFix (String): The exact, code-level fix generated by Gemini for this specific issue.
beforeAuditReportId (ObjectId, optional): A reference to the _id of the immediately preceding SiteAuditReport for the same site, used for calculating the diff.diffReport (Object): Captures the "before/after" changes compared to the beforeAuditReportId. * overallScoreChange (Number): The change in the overallScore since the last audit (e.g., +5, -2).
* newIssues (Array of Objects): A list of issues identified in the current audit that were NOT present in the previous audit.
* resolvedIssues (Array of Objects): A list of issues present in the previous audit that are NO LONGER present in the current audit.
* changedMetrics (Array of Objects): Highlights significant changes in key metrics (e.g., LCP increased by X ms, meta title length changed for Y pages).
The upsert operation within hive_db functions as follows:
siteUrl.SiteAuditReport document associated with that siteUrl. This report serves as the "before" state.diffReport, detailing new issues, resolved issues, and changes in key metrics.SiteAuditReport document is constructed, incorporating all the detailed audit findings, Gemini fixes, the calculated diffReport, and a reference to the beforeAuditReportId.SiteAuditReport document is then inserted into the SiteAuditReport collection in MongoDB. This approach ensures that every audit run creates a distinct, immutable historical record, rather than overwriting previous data.The SiteAuditReport stores a wealth of actionable data:
This robust data storage provides several direct benefits to you:
This document details the final and critical step of the "Site SEO Auditor" workflow: hive_db → conditional_update. In this stage, all the comprehensive SEO audit findings, identified issues, AI-generated fixes, and performance metrics are meticulously stored and updated within your dedicated database instance. This ensures a persistent, traceable record of your site's SEO health.
hive_db → conditional_updatePurpose: To persist the complete SEO audit report, including all discovered issues and their corresponding AI-generated fixes, into your MongoDB database. This step is responsible for creating a new SiteAuditReport document for each audit run or updating an existing one, critically incorporating a "before/after" diff for continuous improvement tracking.
Action: The system executes a database operation to store or update the SiteAuditReport document in a designated MongoDB collection.
Upon completion of the crawling, auditing, and fix generation phases, the system compiles all collected data into a structured report object. This object is then used to perform a conditional_update operation in MongoDB.
PantheraHive_SEO_Audits (or a similar dedicated instance).SiteAuditReports
{
"_id": ObjectId("..."),
"siteUrl": "https://www.example.com",
"auditId": "SEO-AUDIT-20231027-123456",
"auditTimestamp": ISODate("2023-10-27T02:00:00Z"),
"auditTrigger": "automated_weekly", // or "on_demand"
"status": "completed",
"summary": {
"totalPagesAudited": 150,
"criticalIssuesFound": 5,
"warningIssuesFound": 12,
"infoIssuesFound": 20,
"overallScore": 85 // Example metric
},
"pagesAudited": [
{
"pageUrl": "https://www.example.com/",
"issues": [
{
"type": "meta_title_duplicate",
"severity": "critical",
"description": "Duplicate meta title found across 3 pages.",
"element": "<title>Home Page</title>",
"geminiFix": {
"proposedChange": "Update meta title to 'Example.com - Your Leading Service Provider'",
"htmlSnippet": "<title>Example.com - Your Leading Service Provider</title>"
}
},
{
"type": "image_alt_missing",
"severity": "warning",
"description": "Image is missing an alt attribute.",
"element": "<img src='/img/hero.jpg'>",
"geminiFix": {
"proposedChange": "Add descriptive alt text: 'Image of a happy customer using our product'",
"htmlSnippet": "<img src='/img/hero.jpg' alt='Image of a happy customer using our product'>"
}
}
// ... other issues for this page
],
"metrics": {
"LCP": "2.5s",
"CLS": "0.05",
"FID": "50ms",
"hasH1": true,
"internalLinksCount": 25,
"hasCanonicalTag": true,
"hasOpenGraphTags": true,
"hasStructuredData": true,
"isMobileViewportOptimized": true
}
}
// ... other audited pages
],
"beforeAfterDiff": {
"previousAuditId": "SEO-AUDIT-20231020-123456", // ID of the previous report
"changesDetected": [
{
"type": "new_issue",
"description": "New broken internal link detected on /about-us",
"details": { "pageUrl": "/about-us", "issueType": "broken_internal_link" }
},
{
"type": "issue_resolved",
"description": "Duplicate meta title on /contact-us resolved.",
"details": { "pageUrl": "/contact-us", "issueType": "meta_title_duplicate" }
},
{
"type": "metric_change",
"description": "LCP improved on homepage from 3.2s to 2.5s.",
"details": { "pageUrl": "/", "metric": "LCP", "oldValue": "3.2s", "newValue": "2.5s" }
}
]
}
}
SiteAuditReports collection to find the most recent audit report for the given siteUrl.beforeAfterDiff object, highlighting:* New Issues: Problems identified in the current audit that were not present in the previous one.
* Resolved Issues: Problems from the previous audit that are no longer detected.
* Metric Changes: Significant improvements or degradations in Core Web Vitals or other quantifiable metrics.
* Content Changes: Any detected changes in meta descriptions, H1s, etc.
* If no previous report exists (first audit for this site), a new SiteAuditReport document is inserted.
* If a previous report exists, a new SiteAuditReport document is inserted, including the beforeAfterDiff field linking it to the previous audit. This maintains a historical chain of reports rather than overwriting.
SiteAuditReport is uniquely identified, allowing for easy retrieval and comparison. The beforeAfterDiff explicitly links current audits to previous ones, providing a clear audit trail.geminiFix suggestions, making the reports directly actionable for your development or content teams.* Customizable Dashboards: Visualizing SEO health trends over time.
* Automated Email Reports: Summarizing key changes and progress.
* API Access: Allowing integration with your internal systems for further analysis or task management.
This final step is where the raw data transforms into a valuable, actionable asset:
This concludes the "Site SEO Auditor" workflow. Your site's latest SEO audit report, complete with AI-generated fixes and a before/after comparison, has been successfully stored in your MongoDB database, ready for review and action.
\",\n \"geminiFix\": {\n \"proposedChange\": \"Add descriptive alt text: 'Image of a happy customer using our product'\",\n \"htmlSnippet\": \"
\"\n }\n }\n // ... other issues for this page\n ],\n \"metrics\": {\n \"LCP\": \"2.5s\",\n \"CLS\": \"0.05\",\n \"FID\": \"50ms\",\n \"hasH1\": true,\n \"internalLinksCount\": 25,\n \"hasCanonicalTag\": true,\n \"hasOpenGraphTags\": true,\n \"hasStructuredData\": true,\n \"isMobileViewportOptimized\": true\n }\n }\n // ... other audited pages\n ],\n \"beforeAfterDiff\": {\n \"previousAuditId\": \"SEO-AUDIT-20231020-123456\", // ID of the previous report\n \"changesDetected\": [\n {\n \"type\": \"new_issue\",\n \"description\": \"New broken internal link detected on /about-us\",\n \"details\": { \"pageUrl\": \"/about-us\", \"issueType\": \"broken_internal_link\" }\n },\n {\n \"type\": \"issue_resolved\",\n \"description\": \"Duplicate meta title on /contact-us resolved.\",\n \"details\": { \"pageUrl\": \"/contact-us\", \"issueType\": \"meta_title_duplicate\" }\n },\n {\n \"type\": \"metric_change\",\n \"description\": \"LCP improved on homepage from 3.2s to 2.5s.\",\n \"details\": { \"pageUrl\": \"/\", \"metric\": \"LCP\", \"oldValue\": \"3.2s\", \"newValue\": \"2.5s\" }\n }\n ]\n }\n }\n ```\n\n#### 2.2. Conditional Update Logic\n\n1. **Check for Previous Audit:** The system first queries the `SiteAuditReports` collection to find the most recent audit report for the given `siteUrl`.\n2. **Generate Before/After Diff:** If a previous report exists, the current audit's findings are compared against it. This comparison generates the `beforeAfterDiff` object, highlighting:\n * **New Issues:** Problems identified in the current audit that were not present in the previous one.\n * **Resolved Issues:** Problems from the previous audit that are no longer detected.\n * **Metric Changes:** Significant improvements or degradations in Core Web Vitals or other quantifiable metrics.\n * **Content Changes:** Any detected changes in meta descriptions, H1s, etc.\n3. **Insert or Update:**\n * If no previous report exists (first audit for this site), a new `SiteAuditReport` document is inserted.\n * If a previous report exists, a new `SiteAuditReport` document is inserted, including the `beforeAfterDiff` field linking it to the previous audit. This maintains a historical chain of reports rather than overwriting.\n\n---\n\n### 3. Data Integrity and Accessibility\n\n* **Persistent Record:** Every audit run, whether automated or on-demand, creates a permanent, timestamped record in the database. This ensures a comprehensive history of your site's SEO evolution.\n* **Traceability:** Each `SiteAuditReport` is uniquely identified, allowing for easy retrieval and comparison. The `beforeAfterDiff` explicitly links current audits to previous ones, providing a clear audit trail.\n* **Actionable Data:** The stored data includes not just the issues, but also the precise `geminiFix` suggestions, making the reports directly actionable for your development or content teams.\n* **Future Reporting & Dashboards:** This structured data in MongoDB forms the bedrock for:\n * **Customizable Dashboards:** Visualizing SEO health trends over time.\n * **Automated Email Reports:** Summarizing key changes and progress.\n * **API Access:** Allowing integration with your internal systems for further analysis or task management.\n\n---\n\n### 4. Value Proposition for the Customer\n\nThis final step is where the raw data transforms into a valuable, actionable asset:\n\n* **Historical Performance Tracking:** Gain deep insights into how your site's SEO health changes week-to-week, enabling you to track the impact of your optimization efforts.\n* **Proactive Issue Detection:** Quickly identify new SEO regressions or emerging issues before they significantly impact your rankings.\n* **Efficient Fix Implementation:** Access clear, AI-generated solutions for every identified problem, streamlining the remediation process for your team.\n* **Comprehensive Audit Trail:** Maintain a complete, auditable record of all SEO changes and their outcomes, essential for demonstrating compliance or progress to stakeholders.\n* **Foundation for Analytics:** The rich, structured data stored in MongoDB provides a powerful foundation for advanced SEO analytics and custom reporting, tailored to your specific needs.\n\n---\n\nThis concludes the \"Site SEO Auditor\" workflow. Your site's latest SEO audit report, complete with AI-generated fixes and a before/after comparison, has been successfully stored in your MongoDB database, ready for review and action.";function phTab(btn,name){document.querySelectorAll(".ph-panel").forEach(function(el){el.classList.remove("active");});document.querySelectorAll(".ph-tab").forEach(function(el){el.classList.remove("active");el.classList.add("inactive");});var p=document.getElementById("panel-"+name);if(p)p.classList.add("active");btn.classList.remove("inactive");btn.classList.add("active");if(name==="preview"){var fr=document.getElementById("ph-preview-frame");if(fr&&!fr.dataset.loaded){if(_phIsHtml){fr.srcdoc=_phCode;}else{var vc=document.getElementById("panel-content");fr.srcdoc=vc?""+vc.innerHTML+"":"No content
";}fr.dataset.loaded="1";}}}function phCopyCode(){navigator.clipboard.writeText(_phCode).then(function(){var b=document.getElementById("tab-code");if(b){var o=b.innerHTML;b.innerHTML=' Copied!';setTimeout(function(){b.innerHTML=o;},2000);}});}function phCopyAll(){navigator.clipboard.writeText(_phAll).then(function(){alert("Content copied to clipboard!");});}function phDownload(){var content=_phCode||_phAll;if(!content){alert("No content to download.");return;}var fn=_phFname;if(!_phCode&&fn.endsWith(".txt"))fn=fn.replace(/\.txt$/,".md");var a=document.createElement("a");a.href="data:text/plain;charset=utf-8,"+encodeURIComponent(content);a.download=fn;a.click();}function phDownloadZip(){ var lbl=document.getElementById("ph-zip-lbl"); if(lbl)lbl.textContent="Preparing\u2026"; /* ===== HELPERS ===== */ function cc(s){ return s.replace(/[_\-\s]+([a-z])/g,function(m,c){return c.toUpperCase();}) .replace(/^[a-z]/,function(m){return m.toUpperCase();}); } function pkgName(app){ return app.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; } function slugTitle(app){ return app.replace(/_/g," "); } /* Generic code block extractor. Finds marker comments like: // lib/main.dart or # lib/main.dart or ## lib/main.dart and collects lines until the next marker. Also strips markdown fences (\`\`\`lang ... \`\`\`) from each block. */ function extractFiles(txt, pathRe){ var files={}, cur=null, buf=[]; function flush(){ if(cur&&buf.length){ files[cur]=buf.join("\n").trim(); } } txt.split("\n").forEach(function(line){ var m=line.trim().match(pathRe); if(m){ flush(); cur=m[1]; buf=[]; return; } if(cur) buf.push(line); }); flush(); // Strip \`\`\`...\`\`\` fences from each file Object.keys(files).forEach(function(k){ files[k]=files[k].replace(/^\`\`\`[a-z]*\n?/,"").replace(/\n?\`\`\`$/,"").trim(); }); return files; } /* General path extractor that covers most languages */ function extractCode(txt){ var re=/^(?:\/\/|#|##)\s*((?:lib|src|test|tests|Sources?|app|components?|screens?|views?|hooks?|routes?|store|services?|models?|pages?)\/[\w\/\-\.]+\.\w+|pubspec\.yaml|Package\.swift|angular\.json|babel\.config\.(?:js|ts)|vite\.config\.(?:js|ts)|tsconfig\.(?:json|app\.json)|app\.json|App\.(?:tsx|jsx|vue|kt|swift)|MainActivity(?:\.kt)?|ContentView\.swift)/i; return extractFiles(txt, re); } /* Detect language from combined code+panel text */ function detectLang(code, panel){ var t=(code+" "+panel).toLowerCase(); if(t.indexOf("import 'package:flutter")>=0||t.indexOf('import "package:flutter')>=0) return "flutter"; if(t.indexOf("statelesswidget")>=0||t.indexOf("statefulwidget")>=0) return "flutter"; if((t.indexOf(".dart")>=0)&&(t.indexOf("pubspec")>=0||t.indexOf("flutter:")>=0)) return "flutter"; if(t.indexOf("react-native")>=0||t.indexOf("react_native")>=0) return "react-native"; if(t.indexOf("stylesheet.create")>=0||t.indexOf("view, text, touchableopacity")>=0) return "react-native"; if(t.indexOf("expo(")>=0||t.indexOf("\"expo\":")>=0||t.indexOf("from 'expo")>=0) return "react-native"; if(t.indexOf("import swiftui")>=0||t.indexOf("import uikit")>=0) return "swift"; if(t.indexOf(".swift")>=0&&(t.indexOf("func body")>=0||t.indexOf("@main")>=0||t.indexOf("var body: some view")>=0)) return "swift"; if(t.indexOf("import android.")>=0||t.indexOf("package com.example")>=0) return "kotlin"; if(t.indexOf("@composable")>=0||t.indexOf("fun mainactivity")>=0||(t.indexOf(".kt")>=0&&t.indexOf("androidx")>=0)) return "kotlin"; if(t.indexOf("@ngmodule")>=0||t.indexOf("@component")>=0) return "angular"; if(t.indexOf("angular.json")>=0||t.indexOf("from '@angular")>=0) return "angular"; if(t.indexOf(".vue")>=0||t.indexOf("")>=0||t.indexOf("definecomponent")>=0) return "vue"; if(t.indexOf("createapp(")>=0&&t.indexOf("vue")>=0) return "vue"; if(t.indexOf("import react")>=0||t.indexOf("reactdom")>=0||(t.indexOf("jsx.element")>=0)) return "react"; if((t.indexOf("usestate")>=0||t.indexOf("useeffect")>=0)&&t.indexOf("from 'react'")>=0) return "react"; if(t.indexOf(".dart")>=0) return "flutter"; if(t.indexOf(".kt")>=0) return "kotlin"; if(t.indexOf(".swift")>=0) return "swift"; if(t.indexOf("import numpy")>=0||t.indexOf("import pandas")>=0||t.indexOf("#!/usr/bin/env python")>=0) return "python"; if(t.indexOf("const express")>=0||t.indexOf("require('express')")>=0||t.indexOf("app.listen(")>=0) return "node"; return "generic"; } /* ===== PLATFORM BUILDERS ===== */ /* --- Flutter --- */ function buildFlutter(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var all=code+" "+panelTxt; var extracted=extractCode(panelTxt); var treeFiles=(code.match(/\b[\w_]+\.dart\b/g)||[]).filter(function(f,i,a){return a.indexOf(f)===i;}); if(!extracted["lib/main.dart"]) extracted["lib/main.dart"]="import 'package:flutter/material.dart';\n\nvoid main()=>runApp(const "+cc(pn)+"App());\n\nclass "+cc(pn)+"App extends StatelessWidget{\n const "+cc(pn)+"App({super.key});\n @override\n Widget build(BuildContext context)=>MaterialApp(\n title: '"+slugTitle(pn)+"',\n debugShowCheckedModeBanner: false,\n theme: ThemeData(\n colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),\n useMaterial3: true,\n ),\n home: Scaffold(appBar: AppBar(title: const Text('"+slugTitle(pn)+"')),\n body: const Center(child: Text('Welcome!'))),\n );\n}\n"; // pubspec.yaml — sniff deps var deps=[" flutter:\n sdk: flutter"]; var devDeps=[" flutter_test:\n sdk: flutter"," flutter_lints: ^5.0.0"]; var knownPkg={"go_router":"^14.0.0","flutter_riverpod":"^2.6.1","riverpod_annotation":"^2.6.1","shared_preferences":"^2.3.4","http":"^1.2.2","dio":"^5.7.0","firebase_core":"^3.12.1","firebase_auth":"^5.5.1","cloud_firestore":"^5.6.5","get_it":"^8.0.3","flutter_bloc":"^9.1.0","provider":"^6.1.2","cached_network_image":"^3.4.1","url_launcher":"^6.3.1","intl":"^0.19.0","google_fonts":"^6.2.1","equatable":"^2.0.7","freezed_annotation":"^2.4.4","json_annotation":"^4.9.0","path_provider":"^2.1.5","image_picker":"^1.1.2","uuid":"^4.4.2","flutter_svg":"^2.0.17","lottie":"^3.2.0","hive_flutter":"^1.1.0"}; var knownDev={"build_runner":"^2.4.14","freezed":"^2.5.7","json_serializable":"^6.8.0","riverpod_generator":"^2.6.3","hive_generator":"^2.0.1"}; Object.keys(knownPkg).forEach(function(p){if(all.indexOf("package:"+p)>=0)deps.push(" "+p+": "+knownPkg[p]);}); Object.keys(knownDev).forEach(function(p){if(all.indexOf(p)>=0)devDeps.push(" "+p+": "+knownDev[p]);}); zip.file(folder+"pubspec.yaml","name: "+pn+"\ndescription: Flutter app — PantheraHive BOS.\nversion: 1.0.0+1\n\nenvironment:\n sdk: '>=3.3.0 <4.0.0'\n\ndependencies:\n"+deps.join("\n")+"\n\ndev_dependencies:\n"+devDeps.join("\n")+"\n\nflutter:\n uses-material-design: true\n assets:\n - assets/images/\n"); zip.file(folder+"analysis_options.yaml","include: package:flutter_lints/flutter.yaml\n"); zip.file(folder+".gitignore",".dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n/build/\n.pub-cache/\n*.g.dart\n*.freezed.dart\n.idea/\n.vscode/\n"); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nflutter pub get\nflutter run\n\`\`\`\n\n## Build\n\`\`\`bash\nflutter build apk # Android\nflutter build ipa # iOS\nflutter build web # Web\n\`\`\`\n"); zip.file(folder+"assets/images/.gitkeep",""); Object.keys(extracted).forEach(function(p){ zip.file(folder+p,extracted[p]); }); treeFiles.forEach(function(fn){ if(fn.indexOf("_test.dart")>=0) return; var found=Object.keys(extracted).some(function(p){return p.endsWith("/"+fn)||p===fn;}); if(!found){ var path="lib/"+fn; var cls=cc(fn.replace(".dart","")); var isScr=fn.indexOf("screen")>=0||fn.indexOf("page")>=0||fn.indexOf("view")>=0; var stub=isScr?"import 'package:flutter/material.dart';\n\nclass "+cls+" extends StatelessWidget{\n const "+cls+"({super.key});\n @override\n Widget build(BuildContext ctx)=>Scaffold(\n appBar: AppBar(title: const Text('"+fn.replace(/_/g," ").replace(".dart","")+"')),\n body: const Center(child: Text('"+cls+" — TODO')),\n );\n}\n":"// TODO: implement\n\nclass "+cls+"{\n // "+fn+"\n}\n"; zip.file(folder+path,stub); } }); } /* --- React Native (Expo) --- */ function buildReactNative(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var extracted=extractCode(panelTxt); var allT=code+" "+panelTxt; var usesTS=allT.indexOf(".tsx")>=0||allT.indexOf(": React.")>=0||allT.indexOf("interface ")>=0; var ext=usesTS?"tsx":"jsx"; zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "1.0.0",\n "main": "expo-router/entry",\n "scripts": {\n "start": "expo start",\n "android": "expo run:android",\n "ios": "expo run:ios",\n "web": "expo start --web"\n },\n "dependencies": {\n "expo": "~52.0.0",\n "expo-router": "~4.0.0",\n "expo-status-bar": "~2.0.1",\n "expo-font": "~13.0.1",\n "react": "18.3.1",\n "react-native": "0.76.7",\n "react-native-safe-area-context": "4.12.0",\n "react-native-screens": "~4.3.0",\n "@react-navigation/native": "^7.0.14"\n },\n "devDependencies": {\n "@babel/core": "^7.25.0",\n "typescript": "~5.3.3",\n "@types/react": "~18.3.12"\n }\n}\n'); zip.file(folder+"app.json",'{\n "expo": {\n "name": "'+slugTitle(pn)+'",\n "slug": "'+pn+'",\n "version": "1.0.0",\n "orientation": "portrait",\n "scheme": "'+pn+'",\n "platforms": ["ios","android","web"],\n "icon": "./assets/icon.png",\n "splash": {"image": "./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},\n "ios": {"supportsTablet": true},\n "android": {"package": "com.example.'+pn+'"},\n "newArchEnabled": true\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "extends": "expo/tsconfig.base",\n "compilerOptions": {\n "strict": true,\n "paths": {"@/*": ["./src/*"]}\n }\n}\n'); zip.file(folder+"babel.config.js","module.exports=function(api){\n api.cache(true);\n return {presets:['babel-preset-expo']};\n};\n"); var hasApp=Object.keys(extracted).some(function(k){return k.toLowerCase().indexOf("app.")>=0;}); if(!hasApp) zip.file(folder+"App."+ext,"import React from 'react';\nimport {View,Text,StyleSheet,StatusBar,SafeAreaView} from 'react-native';\n\nexport default function App(){\n return(\nBuilt with PantheraHive BOS
\n"); h+="
"+hc+"