@reddoorla/maintenance 0.93.1 → 0.94.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.
Files changed (136) hide show
  1. package/README.md +14 -13
  2. package/dist/airtable-JGCPS7JH.js +9 -0
  3. package/dist/{announce-6OS4C57D.js → announce-PGT4SZZB.js} +6 -6
  4. package/dist/{bump-deps-GI7PHMOC.js → bump-deps-6OR7LL4E.js} +2 -2
  5. package/dist/{chunk-7NYN2P6I.js → chunk-3Z6X3BHU.js} +15 -8
  6. package/dist/chunk-3Z6X3BHU.js.map +1 -0
  7. package/dist/{chunk-MVDMKKBN.js → chunk-4AIYWWKV.js} +12 -5
  8. package/dist/chunk-4AIYWWKV.js.map +1 -0
  9. package/dist/{chunk-QLIUDTY6.js → chunk-5EEMTJMT.js} +10 -7
  10. package/dist/chunk-5EEMTJMT.js.map +1 -0
  11. package/dist/{chunk-4NUKMPOO.js → chunk-5FCBYK6L.js} +3 -3
  12. package/dist/{chunk-PR343K7F.js → chunk-5GUJ36TL.js} +2 -2
  13. package/dist/{chunk-P6AORG32.js → chunk-6R56OKJZ.js} +2 -2
  14. package/dist/{chunk-E22WAIF2.js → chunk-AEUABPAJ.js} +3 -3
  15. package/dist/{chunk-ZRI5VRPG.js → chunk-BYGPUUCT.js} +2 -2
  16. package/dist/{chunk-BJQCIJOY.js → chunk-DHVJ7H2F.js} +230 -805
  17. package/dist/chunk-DHVJ7H2F.js.map +1 -0
  18. package/dist/{chunk-SKI23ZZH.js → chunk-HTABUCF4.js} +2 -2
  19. package/dist/{chunk-FCS36FDT.js → chunk-KQB2BX4V.js} +3 -3
  20. package/dist/{chunk-AX22DH6S.js → chunk-KZRR4NFG.js} +3 -3
  21. package/dist/{chunk-CVJF2IC5.js → chunk-TGUESYGF.js} +17 -3
  22. package/dist/{chunk-CVJF2IC5.js.map → chunk-TGUESYGF.js.map} +1 -1
  23. package/dist/{chunk-JB4Q3C3M.js → chunk-TO7DJYLZ.js} +92 -15
  24. package/dist/{chunk-JB4Q3C3M.js.map → chunk-TO7DJYLZ.js.map} +1 -1
  25. package/dist/chunk-VELWHWOW.js +4194 -0
  26. package/dist/chunk-VELWHWOW.js.map +1 -0
  27. package/dist/{chunk-NDCOOI3V.js → chunk-WMBLR5VA.js} +3 -3
  28. package/dist/{chunk-QBAAZY7W.js → chunk-WVNDEYXL.js} +3 -3
  29. package/dist/{chunk-7BZUOZPU.js → chunk-XN37VTDB.js} +2 -2
  30. package/dist/{chunk-Q47DU255.js → chunk-Z2K4JPQ3.js} +2 -2
  31. package/dist/cli/bin.js +26 -26
  32. package/dist/cli/commands/audit.js +11 -11
  33. package/dist/{convert-to-pnpm-ONFP3JKF.js → convert-to-pnpm-3RVGUBBR.js} +2 -2
  34. package/dist/{db-HSZ6D5DU.js → db-FL7ZBF6H.js} +5 -5
  35. package/dist/{digest-EOA4JLTV.js → digest-TZ3QQOSF.js} +7 -7
  36. package/dist/{digest-collectors-ZA3BTS3R.js → digest-collectors-EHFY2OQC.js} +6 -6
  37. package/dist/{ensure-site-SF6KICTN.js → ensure-site-WIDO6LGU.js} +3 -3
  38. package/dist/{fleet-state-3LQUQHYD.js → fleet-state-UEVZ3X3J.js} +4 -4
  39. package/dist/{forms-notify-target-RMZPKT6S.js → forms-notify-target-2LWF5C5B.js} +3 -3
  40. package/dist/{github-signals-3WLIJ5RR.js → github-signals-GAHNOAHS.js} +5 -5
  41. package/dist/{header-image-DOCDFFRO.js → header-image-YN3AQRY3.js} +2 -2
  42. package/dist/{health-endpoint-DHKJUPRM.js → health-endpoint-XLNBWVHK.js} +2 -2
  43. package/dist/{health-mirror-55RRU6BI.js → health-mirror-EB76EERV.js} +4 -4
  44. package/dist/{import-airtable-OHVY344G.js → import-airtable-TAKSN4NV.js} +3 -3
  45. package/dist/index.d.ts +87 -6
  46. package/dist/index.js +14 -11
  47. package/dist/index.js.map +1 -1
  48. package/dist/{init-ZRKWYCKA.js → init-F5OBBWA5.js} +6 -6
  49. package/dist/{launch-6X5OXPF2.js → launch-SUTMI4NG.js} +9 -9
  50. package/dist/{lighthouse-airtable-3GUWHETW.js → lighthouse-airtable-YMH4UA7R.js} +3 -3
  51. package/dist/{onboard-6NHGCKGB.js → onboard-NKE6GZKL.js} +2 -2
  52. package/dist/{orchestrate-KMQIUP3H.js → orchestrate-MWXENQOS.js} +4 -4
  53. package/dist/{parity-623WNFZG.js → parity-CAJ477PS.js} +5 -5
  54. package/dist/{pipeline-AQBFG5WM.js → pipeline-PRMD4EJT.js} +3 -2
  55. package/dist/{preflight-VPHQYWTY.js → preflight-4VQP7JJL.js} +5 -5
  56. package/dist/{prismic-ci-A2HI7NNJ.js → prismic-ci-QIO3I4KB.js} +2 -2
  57. package/dist/{prismic-models-QTKIMFBC.js → prismic-models-RWDVKZ2W.js} +6 -6
  58. package/dist/prospect/types.d.ts +584 -1
  59. package/dist/prospect/types.js +10 -1
  60. package/dist/{prospect-audit-RKUVSTLN.js → prospect-audit-CIL3NCPM.js} +3 -3
  61. package/dist/{render-7G43BSK5.js → render-ZUQBJYJR.js} +3 -2
  62. package/dist/{render-7G43BSK5.js.map → render-ZUQBJYJR.js.map} +1 -1
  63. package/dist/{render-from-row-P5CWM3MQ.js → render-from-row-IFSBHWDH.js} +3 -3
  64. package/dist/{renovate-dispatch-6GQWTRKX.js → renovate-dispatch-U5Q52SKT.js} +2 -2
  65. package/dist/{report-Y4LXDHRV.js → report-6FFHGUUS.js} +14 -14
  66. package/dist/{report-mirror-6ZDCAQZB.js → report-mirror-IP64R7B3.js} +4 -4
  67. package/dist/{self-updating-D5XOWS7V.js → self-updating-5DVDIEHB.js} +2 -2
  68. package/dist/{selftest-IFGSJP4X.js → selftest-LJYXBIO5.js} +6 -6
  69. package/dist/{site-mirror-3E7JYTJG.js → site-mirror-UKHREAD6.js} +4 -4
  70. package/dist/{smoke-suite-GXC5JZPH.js → smoke-suite-NUTC7IUK.js} +3 -3
  71. package/dist/{submissions-UMJS3YST.js → submissions-5GKJPCIH.js} +2 -2
  72. package/dist/{svelte-codemods-A3EARSRP.js → svelte-codemods-U4NIT6CV.js} +2 -2
  73. package/dist/{sync-ESP5ZQUU.js → sync-K3PKHVLH.js} +5 -5
  74. package/dist/{sync-configs-KSESOHFF.js → sync-configs-S2JYY6QU.js} +2 -2
  75. package/dist/{upgrade-ZLNVEIEA.js → upgrade-YFZCMUPE.js} +2 -2
  76. package/dist/{websites-D6WYFAES.js → websites-TB4TCAWM.js} +2 -2
  77. package/dist/{write-audits-to-airtable-K352LZQY.js → write-audits-to-airtable-53EVXKMU.js} +4 -4
  78. package/package.json +1 -1
  79. package/dist/airtable-QG5ICI3Y.js +0 -9
  80. package/dist/chunk-7NYN2P6I.js.map +0 -1
  81. package/dist/chunk-BJQCIJOY.js.map +0 -1
  82. package/dist/chunk-MVDMKKBN.js.map +0 -1
  83. package/dist/chunk-QLIUDTY6.js.map +0 -1
  84. /package/dist/{airtable-QG5ICI3Y.js.map → airtable-JGCPS7JH.js.map} +0 -0
  85. /package/dist/{announce-6OS4C57D.js.map → announce-PGT4SZZB.js.map} +0 -0
  86. /package/dist/{bump-deps-GI7PHMOC.js.map → bump-deps-6OR7LL4E.js.map} +0 -0
  87. /package/dist/{chunk-4NUKMPOO.js.map → chunk-5FCBYK6L.js.map} +0 -0
  88. /package/dist/{chunk-PR343K7F.js.map → chunk-5GUJ36TL.js.map} +0 -0
  89. /package/dist/{chunk-P6AORG32.js.map → chunk-6R56OKJZ.js.map} +0 -0
  90. /package/dist/{chunk-E22WAIF2.js.map → chunk-AEUABPAJ.js.map} +0 -0
  91. /package/dist/{chunk-ZRI5VRPG.js.map → chunk-BYGPUUCT.js.map} +0 -0
  92. /package/dist/{chunk-SKI23ZZH.js.map → chunk-HTABUCF4.js.map} +0 -0
  93. /package/dist/{chunk-FCS36FDT.js.map → chunk-KQB2BX4V.js.map} +0 -0
  94. /package/dist/{chunk-AX22DH6S.js.map → chunk-KZRR4NFG.js.map} +0 -0
  95. /package/dist/{chunk-NDCOOI3V.js.map → chunk-WMBLR5VA.js.map} +0 -0
  96. /package/dist/{chunk-QBAAZY7W.js.map → chunk-WVNDEYXL.js.map} +0 -0
  97. /package/dist/{chunk-7BZUOZPU.js.map → chunk-XN37VTDB.js.map} +0 -0
  98. /package/dist/{chunk-Q47DU255.js.map → chunk-Z2K4JPQ3.js.map} +0 -0
  99. /package/dist/{convert-to-pnpm-ONFP3JKF.js.map → convert-to-pnpm-3RVGUBBR.js.map} +0 -0
  100. /package/dist/{db-HSZ6D5DU.js.map → db-FL7ZBF6H.js.map} +0 -0
  101. /package/dist/{digest-EOA4JLTV.js.map → digest-TZ3QQOSF.js.map} +0 -0
  102. /package/dist/{digest-collectors-ZA3BTS3R.js.map → digest-collectors-EHFY2OQC.js.map} +0 -0
  103. /package/dist/{ensure-site-SF6KICTN.js.map → ensure-site-WIDO6LGU.js.map} +0 -0
  104. /package/dist/{fleet-state-3LQUQHYD.js.map → fleet-state-UEVZ3X3J.js.map} +0 -0
  105. /package/dist/{forms-notify-target-RMZPKT6S.js.map → forms-notify-target-2LWF5C5B.js.map} +0 -0
  106. /package/dist/{github-signals-3WLIJ5RR.js.map → github-signals-GAHNOAHS.js.map} +0 -0
  107. /package/dist/{header-image-DOCDFFRO.js.map → header-image-YN3AQRY3.js.map} +0 -0
  108. /package/dist/{health-endpoint-DHKJUPRM.js.map → health-endpoint-XLNBWVHK.js.map} +0 -0
  109. /package/dist/{health-mirror-55RRU6BI.js.map → health-mirror-EB76EERV.js.map} +0 -0
  110. /package/dist/{import-airtable-OHVY344G.js.map → import-airtable-TAKSN4NV.js.map} +0 -0
  111. /package/dist/{init-ZRKWYCKA.js.map → init-F5OBBWA5.js.map} +0 -0
  112. /package/dist/{launch-6X5OXPF2.js.map → launch-SUTMI4NG.js.map} +0 -0
  113. /package/dist/{lighthouse-airtable-3GUWHETW.js.map → lighthouse-airtable-YMH4UA7R.js.map} +0 -0
  114. /package/dist/{onboard-6NHGCKGB.js.map → onboard-NKE6GZKL.js.map} +0 -0
  115. /package/dist/{orchestrate-KMQIUP3H.js.map → orchestrate-MWXENQOS.js.map} +0 -0
  116. /package/dist/{parity-623WNFZG.js.map → parity-CAJ477PS.js.map} +0 -0
  117. /package/dist/{pipeline-AQBFG5WM.js.map → pipeline-PRMD4EJT.js.map} +0 -0
  118. /package/dist/{preflight-VPHQYWTY.js.map → preflight-4VQP7JJL.js.map} +0 -0
  119. /package/dist/{prismic-ci-A2HI7NNJ.js.map → prismic-ci-QIO3I4KB.js.map} +0 -0
  120. /package/dist/{prismic-models-QTKIMFBC.js.map → prismic-models-RWDVKZ2W.js.map} +0 -0
  121. /package/dist/{prospect-audit-RKUVSTLN.js.map → prospect-audit-CIL3NCPM.js.map} +0 -0
  122. /package/dist/{render-from-row-P5CWM3MQ.js.map → render-from-row-IFSBHWDH.js.map} +0 -0
  123. /package/dist/{renovate-dispatch-6GQWTRKX.js.map → renovate-dispatch-U5Q52SKT.js.map} +0 -0
  124. /package/dist/{report-Y4LXDHRV.js.map → report-6FFHGUUS.js.map} +0 -0
  125. /package/dist/{report-mirror-6ZDCAQZB.js.map → report-mirror-IP64R7B3.js.map} +0 -0
  126. /package/dist/{self-updating-D5XOWS7V.js.map → self-updating-5DVDIEHB.js.map} +0 -0
  127. /package/dist/{selftest-IFGSJP4X.js.map → selftest-LJYXBIO5.js.map} +0 -0
  128. /package/dist/{site-mirror-3E7JYTJG.js.map → site-mirror-UKHREAD6.js.map} +0 -0
  129. /package/dist/{smoke-suite-GXC5JZPH.js.map → smoke-suite-NUTC7IUK.js.map} +0 -0
  130. /package/dist/{submissions-UMJS3YST.js.map → submissions-5GKJPCIH.js.map} +0 -0
  131. /package/dist/{svelte-codemods-A3EARSRP.js.map → svelte-codemods-U4NIT6CV.js.map} +0 -0
  132. /package/dist/{sync-ESP5ZQUU.js.map → sync-K3PKHVLH.js.map} +0 -0
  133. /package/dist/{sync-configs-KSESOHFF.js.map → sync-configs-S2JYY6QU.js.map} +0 -0
  134. /package/dist/{upgrade-ZLNVEIEA.js.map → upgrade-YFZCMUPE.js.map} +0 -0
  135. /package/dist/{websites-D6WYFAES.js.map → websites-TB4TCAWM.js.map} +0 -0
  136. /package/dist/{write-audits-to-airtable-K352LZQY.js.map → write-audits-to-airtable-53EVXKMU.js.map} +0 -0
@@ -582,6 +582,424 @@ type GoalFit = {
582
582
  };
583
583
  declare function orderRequirements(reqs: GoalRequirement[]): GoalRequirement[];
584
584
 
585
+ /**
586
+ * What they are running, named back to them.
587
+ *
588
+ * This is not a check and it must never try to be. Nothing here passes or
589
+ * fails, nothing here enters the denominator, and no line of it belongs in the
590
+ * fix list. It exists to answer the reader's first silent question — do these
591
+ * people know what they are talking about — before a single finding is made.
592
+ *
593
+ * "You're on WordPress running the Astra theme with Elementor, your forms are
594
+ * Gravity Forms, your DNS is at GoDaddy and your mail is Google Workspace" is a
595
+ * sentence nobody sending a cold audit can write, and it costs us almost
596
+ * nothing because every receipt for it is already in the crawl.
597
+ *
598
+ * The discipline is the same as everywhere else in this codebase, and it is the
599
+ * whole reason `evidence` is non-optional on `StackItem`: every line is an
600
+ * OBSERVATION WITH A RECEIPT — the exact URL or header we read it off — never
601
+ * an inference we cannot show. A reader who disputes a line can go and look at
602
+ * the thing we looked at.
603
+ *
604
+ * The failure this module must not commit is the mirror of the one the checks
605
+ * must not commit. There, our missing measurement must not become their defect.
606
+ * Here, our missing measurement must not become "they don't have one": a
607
+ * WordPress site behind a caching plugin that rewrites asset paths is
608
+ * completely invisible to this, and so is any site whose markup we never read.
609
+ * `measured` says whether we were in a position to see anything at all, and the
610
+ * report must be able to say "we could not tell" rather than implying absence.
611
+ */
612
+ type StackLayer = "cms" | "theme" | "plugin" | "page-builder" | "framework" | "hosting" | "ecommerce" | "forms" | "analytics" | "fonts";
613
+ /** Display order in the readout. Broadest thing first — a reader wants "you're
614
+ * on WordPress" before "you load Hotjar". */
615
+ declare const LAYER_ORDER: StackLayer[];
616
+ declare const LAYER_LABELS: Record<StackLayer, string>;
617
+ type StackItem = {
618
+ layer: StackLayer;
619
+ /** What we are naming, spelled the way its own vendor spells it. */
620
+ name: string;
621
+ /**
622
+ * The receipt — the URL or response header we read it off, truncated only for
623
+ * length. Never optional and never synthesised: a line without a receipt is
624
+ * an assertion, and this module does not make assertions.
625
+ */
626
+ evidence: string;
627
+ };
628
+ type StackReadout = {
629
+ /**
630
+ * Were we in a position to see anything?
631
+ *
632
+ * False when no page we read carried a `scriptSrcs` array — which is every
633
+ * report stored before that field existed, and any run whose pages all failed
634
+ * to fetch. An empty `items` with `measured: false` means "we could not
635
+ * tell"; with `measured: true` it means "we read the markup and recognised
636
+ * nothing in it", which is a real and unremarkable answer for a hand-built
637
+ * site.
638
+ */
639
+ measured: boolean;
640
+ items: StackItem[];
641
+ /** How many pages' markup we actually read. The honest denominator for any
642
+ * sentence that starts "we did not see". */
643
+ pagesExamined: number;
644
+ /** Did we also read response headers? They carry the hosting and CDN half,
645
+ * and a run without them can say nothing about either. */
646
+ headersExamined: boolean;
647
+ };
648
+
649
+ /**
650
+ * What the domain itself says, as opposed to what the website says.
651
+ *
652
+ * Five questions, one lookup each, and none of them touches the prospect's web
653
+ * server at all — so this is the cheapest section of the whole audit and the
654
+ * one nobody else sends. "Anyone can send email as you" and "your domain
655
+ * renews in 41 days" are findings a reader acts on the same afternoon, and
656
+ * neither is visible from any amount of reading their HTML.
657
+ *
658
+ * `basics.ts` deliberately excludes TLS certificate expiry, on the grounds that
659
+ * a successful https fetch already proved the certificate valid to a real
660
+ * client. DOMAIN expiry is a different thing entirely — nothing about today's
661
+ * successful request tells you the registration lapses in six weeks — which is
662
+ * why it belongs here and that one does not.
663
+ *
664
+ * EVERY FAILURE MODE HERE IS OURS. A DNS timeout, a resolver that refuses, an
665
+ * RDAP service that is down, a TLD that publishes no expiry: each is a gap in
666
+ * our measurement and each must come back `unmeasured`. The one thing this
667
+ * module must never do is report "no SPF record" because the lookup failed —
668
+ * that is our network outage printed as their security hole.
669
+ */
670
+ type DnsFindings = {
671
+ /** False when the domain itself could not be determined — nothing below is
672
+ * then attributable to the prospect. */
673
+ measured: boolean;
674
+ domain: string | null;
675
+ /** The apex TXT record starting `v=spf1`, or null when there is none.
676
+ * `undefined` means the lookup did not answer — a different claim. */
677
+ spf: string | null | undefined;
678
+ /** The `_dmarc` TXT record starting `v=DMARC1`. Same three states. */
679
+ dmarc: string | null | undefined;
680
+ /** Mail exchangers for the apex, lowest preference first. */
681
+ mx: string[] | undefined;
682
+ /** Whether the domain in a published `mailto:` can receive mail. Null when
683
+ * the site publishes no email address to check. */
684
+ contactMx: {
685
+ domain: string;
686
+ hasMx: boolean;
687
+ } | null | undefined;
688
+ /** ISO date the registration lapses, from RDAP. Undefined when RDAP did not
689
+ * answer or the registry publishes no expiry, which many ccTLDs do not. */
690
+ expiresAt: string | undefined;
691
+ };
692
+
693
+ /** A URL that answered, that provably did not, or that we could not read.
694
+ * The third is not a failure and never enters a denominator. */
695
+ type ProbeVerdict = "ok" | "broken" | "unverified";
696
+ type UrlProbe = {
697
+ url: string;
698
+ status: number | null;
699
+ verdict: ProbeVerdict;
700
+ };
701
+ type HttpFindings = {
702
+ /** False when we had no origin to probe — nothing below is then about them. */
703
+ measured: boolean;
704
+ /** Requests this stage made, reported so the cost is visible. */
705
+ requests: number;
706
+ /** The icon a browser tab actually gets. `url` is what the page declared, or
707
+ * the conventional `/favicon.ico` when it declared nothing. */
708
+ favicon: {
709
+ url: string;
710
+ declared: boolean;
711
+ verdict: ProbeVerdict;
712
+ } | undefined;
713
+ /** What `http://` does. `hops` counts redirects; `https` is where it landed. */
714
+ httpUpgrade: {
715
+ hops: number | null;
716
+ https: boolean;
717
+ } | undefined;
718
+ /** Two homepage samples. Equal statuses mean nothing to report; different
719
+ * ones are the only evidence that would justify the word "intermittent". */
720
+ homeSamples: [number | null, number | null] | undefined;
721
+ /** A path fetched with and without its trailing slash. `settled` is true when
722
+ * the two agree on one address — either by redirect or by canonical. */
723
+ trailingSlash: {
724
+ path: string;
725
+ settled: boolean;
726
+ detail: string;
727
+ } | undefined;
728
+ /** `/index.html`, and whether it duplicates the homepage rather than
729
+ * redirecting or declaring the homepage canonical. Null when it 404s, which
730
+ * is the correct behaviour and not a finding. */
731
+ indexAlias: {
732
+ duplicate: boolean;
733
+ detail: string;
734
+ } | null | undefined;
735
+ /** The same path in a different case. Null when it 404s — also correct. */
736
+ caseAlias: {
737
+ path: string;
738
+ duplicate: boolean;
739
+ detail: string;
740
+ } | null | undefined;
741
+ /** Sampled sitemap URLs. Null when there is no sitemap to sample. */
742
+ sitemapUrls: {
743
+ checked: number;
744
+ total: number;
745
+ broken: UrlProbe[];
746
+ } | null | undefined;
747
+ /** Outbound links to other sites. Null when the site links out nowhere. */
748
+ externalLinks: {
749
+ checked: number;
750
+ total: number;
751
+ broken: UrlProbe[];
752
+ } | null | undefined;
753
+ /** The image that appears when the site is shared. Null when none is
754
+ * declared — Tier 1 owns that, and saying it twice reads as two problems. */
755
+ ogImage: {
756
+ url: string;
757
+ verdict: ProbeVerdict;
758
+ width: number | null;
759
+ height: number | null;
760
+ } | null | undefined;
761
+ /** The header logo, and the rule we used to find it, so a reader can tell
762
+ * whether we found the right image. */
763
+ logo: {
764
+ url: string;
765
+ how: string;
766
+ verdict: ProbeVerdict;
767
+ } | null | undefined;
768
+ /** Internal links reached through more than one redirect. */
769
+ redirectChains: {
770
+ checked: number;
771
+ chained: {
772
+ url: string;
773
+ hops: number;
774
+ }[];
775
+ } | undefined;
776
+ };
777
+
778
+ type FormProbe = {
779
+ /** The page we probed, so a finding has an address. */
780
+ url: string;
781
+ /**
782
+ * Whether an empty submit was refused.
783
+ *
784
+ * `undefined` means we could not tell — the form sits in a cross-origin
785
+ * iframe, or the click threw, or the browser pass never ran. It is never a
786
+ * failure: a form we could not reach is our gap, not their defect.
787
+ */
788
+ emptyRefused: boolean | undefined;
789
+ /** How it refused, in words a report can print. */
790
+ emptyHow: string | null;
791
+ /** Same, for an address that is not an address. Null when the form asks for
792
+ * no email at all, which is a form we have nothing to say about here. */
793
+ invalidEmailRefused: boolean | null | undefined;
794
+ invalidEmailHow: string | null;
795
+ /** Requests the interceptor stopped. The receipt for "we sent nothing". */
796
+ blocked: number;
797
+ };
798
+
799
+ /**
800
+ * What axe-core found, in the browser the crawl already opened.
801
+ *
802
+ * WHAT THIS IS AND IS NOT NEW. The Lighthouse stage we already run IS axe — a
803
+ * subset of roughly forty-five rules, reported as one number between 0 and 100.
804
+ * Running it ourselves does not discover a different site; it does three things
805
+ * that number cannot:
806
+ *
807
+ * - turns "accessibility: 82" into named rules with named fixes, each with an
808
+ * element to go and look at;
809
+ * - adds the rules Lighthouse omits — every `best-practice` landmark and
810
+ * heading rule, which is most of what is structurally wrong with most
811
+ * sites, and which a wcag-tags-only scan cannot see at all;
812
+ * - absorbs several checks we would otherwise have written worse by hand
813
+ * (link names, tap-target size, viewport zoom, `lang`, form labels).
814
+ *
815
+ * THE TAG SET IS THE WHOLE POINT. Our own fleet audit scans `wcag2a`,
816
+ * `wcag2aa`, `wcag21a`, `wcag21aa`, `wcag22aa` and nothing else, and every
817
+ * landmark and heading-order rule in axe is tagged `best-practice` — so a green
818
+ * run there proves nothing about structure. This runs the DEFAULT rule set,
819
+ * which is all of them.
820
+ *
821
+ * WHAT IT MAY NOT SAY. Never that a site "is inaccessible", and never a score.
822
+ * axe finds what a machine can find, which is a minority of what a disabled
823
+ * person actually encounters; a page can pass every rule here and be unusable.
824
+ * The report says: we ran N rules, these M produced findings, here is the
825
+ * element and the rule's own explanation. That is a fact with a receipt, and it
826
+ * is all this instrument earns.
827
+ */
828
+ type AxeImpact = "minor" | "moderate" | "serious" | "critical";
829
+ /** One rule that produced findings on one page. Sized for persistence: the
830
+ * node COUNT plus a single example, never every matching element. */
831
+ type AxeViolation = {
832
+ /** axe's rule id — `color-contrast`, `landmark-one-main`. Stable, and the
833
+ * thing to search for. */
834
+ id: string;
835
+ impact: AxeImpact | null;
836
+ /** axe's own one-line explanation. Quoted rather than paraphrased: it is
837
+ * written by people who know the rule better than we do. */
838
+ help: string;
839
+ helpUrl: string;
840
+ /** Elements on this page the rule matched. */
841
+ nodes: number;
842
+ /** One of them, truncated — so a reader can go and find it. */
843
+ sample: string | null;
844
+ };
845
+ type AxePageResult = {
846
+ violations: AxeViolation[];
847
+ /**
848
+ * Rules that found something to judge on this page and judged it fine.
849
+ *
850
+ * NOT "the number of rules axe has". Measured on our own report page, the
851
+ * default set is 90 rules, of which 42 passed, 47 were INAPPLICABLE — the
852
+ * page contains no table, no video, no iframe, so those rules had nothing to
853
+ * look at — and one was incomplete. Printing 90 as though we had checked 90
854
+ * things would be a number inflated on our own behalf, which is why
855
+ * `inapplicable` is carried rather than quietly folded into this one.
856
+ */
857
+ passes: number;
858
+ /** Rules axe could not decide — genuinely "we do not know", reported as such
859
+ * rather than folded into either column. */
860
+ incomplete: number;
861
+ /** WHICH rules those were. A count alone tells a reader three things need a
862
+ * human without saying what to look at, which is a worry rather than a job.
863
+ * Optional: absent on any result stored before it was captured. */
864
+ incompleteIds?: string[];
865
+ /** Rules with nothing on the page to check. Carried so the report can say
866
+ * "43 of axe's 90 rules had something to check here" and be exactly right. */
867
+ inapplicable: number;
868
+ };
869
+ type AccessibilityResult = {
870
+ /**
871
+ * Did the rules run anywhere?
872
+ *
873
+ * False when no page carried a result — every report stored before this
874
+ * existed, every run whose browser never started, and every test that stubs
875
+ * the renderer. An empty `violations` with `measured: false` means "we did
876
+ * not look"; with `measured: true` it means the rules ran and found nothing,
877
+ * which is a real and excellent answer.
878
+ */
879
+ measured: boolean;
880
+ pagesExamined: number;
881
+ /**
882
+ * Rules that had something to check and passed — the number the report may
883
+ * print as "we checked N things", and no larger one.
884
+ *
885
+ * The MAXIMUM across pages, never the sum. The same rule set runs on every
886
+ * page, so summing reports "210 checks" for 42 rules over five pages. Max is
887
+ * a deliberate under-count where pages differ: it can only understate what we
888
+ * looked at, and understating our own work is the safe direction.
889
+ */
890
+ rulesPassed: number;
891
+ rulesIncomplete: number;
892
+ /** The undecided rules by name, so the disclosure can say what to look at
893
+ * instead of only how many. Empty when none, absent-safe for old reports. */
894
+ incompleteIds: string[];
895
+ /** Rules that found nothing on these pages to apply to. Not a pass and not a
896
+ * failure — the page simply has no table, video or iframe for them. */
897
+ rulesInapplicable: number;
898
+ /** Rules that produced findings, worst impact first, each naming the pages it
899
+ * fired on. Capped — see `violationsTotal`. */
900
+ violations: (AxeViolation & {
901
+ pages: string[];
902
+ })[];
903
+ /** True number of distinct rules with findings, before the cap. A truncated
904
+ * list that looks complete is the quiet lie this codebase is built to avoid. */
905
+ violationsTotal: number;
906
+ };
907
+ /**
908
+ * What the browser itself reported while the page was open.
909
+ *
910
+ * Collected in the SAME pass as the rendered DOM and the axe run — listeners
911
+ * attached before navigation, one viewport resize after it. A resize reflows
912
+ * the page the browser has already loaded; it costs no request and puts nothing
913
+ * extra on the prospect's server, which is why the mobile-overflow measurement
914
+ * is affordable at all.
915
+ */
916
+ type PageVitals = {
917
+ /** Uncaught exceptions and `console.error` calls, capped and deduped. The
918
+ * most demonstrable "your site is broken" evidence there is. */
919
+ consoleErrors: string[];
920
+ /**
921
+ * Requests the page made that failed or came back 4xx/5xx.
922
+ *
923
+ * `firstParty` decides whether it is a finding at all. A blocked third-party
924
+ * analytics beacon is our network or an ad blocker; a 404 on the site's own
925
+ * stylesheet is theirs, and only the second belongs in a report.
926
+ */
927
+ failedRequests: {
928
+ url: string;
929
+ status: number | null;
930
+ firstParty: boolean;
931
+ }[];
932
+ /**
933
+ * Pixels the document overflows a 375px viewport, or null when we could not
934
+ * measure. Zero is the answer a correct site gives.
935
+ */
936
+ overflowAt375: number | null;
937
+ /** Text nodes rendering below 12px, with one example. Null = not measured. */
938
+ tinyText: {
939
+ count: number;
940
+ sample: string | null;
941
+ } | null;
942
+ /** Images downloaded far larger than they are drawn — the bytes a visitor
943
+ * pays for and never sees. */
944
+ oversizedImages: {
945
+ src: string;
946
+ naturalWidth: number;
947
+ renderedWidth: number;
948
+ }[];
949
+ };
950
+
951
+ /**
952
+ * The things a careful person would check with a browser and ten minutes.
953
+ *
954
+ * Every check here is a pure function of what the crawl already stored. Nothing
955
+ * in this file makes a request, which is the point: it can be run over every
956
+ * audit already in the database without touching a prospect's server, and a bug
957
+ * found by that replay costs nothing to fix.
958
+ *
959
+ * FOUR STATES, NOT THREE. The rest of this codebase already knows that our
960
+ * failed measurement must never render as the site's defect, which is why
961
+ * `unmeasured` exists. This file adds the state that review kept catching:
962
+ * `not-applicable`. A site with no `LocalBusiness` schema, no HSTS header and no
963
+ * RTL content must not silently PASS the checks about those — that inflates
964
+ * "34 of 35 passed" with checks that never ran, which is the same dishonesty as
965
+ * a false failure wearing nicer clothes. Both non-verdicts leave the
966
+ * denominator; see `tally`.
967
+ *
968
+ * THE FLOOR IS BARE HTML. A hand-written page with a title, a heading and a
969
+ * paragraph should pass almost all of this. A check that a careful, ordinary
970
+ * site fails is not measuring care, it is measuring fashion, and it makes the
971
+ * report an argument rather than a document. Where a bar had to be set, it is
972
+ * set generously and the reasoning is written down next to it.
973
+ *
974
+ * WHAT PASSES IS ONE LINE. Most of these will be green on most sites, and that
975
+ * is by design — they cost one row inside a collapsed section and are invisible
976
+ * until the day one fails, at which point it is the most useful row on the
977
+ * page. That is what makes a trivial check worth having, and it is the only
978
+ * thing that makes it worth having.
979
+ */
980
+ type CheckStatus = "pass" | "fail" | "unmeasured" | "not-applicable";
981
+ type SiteCheck = {
982
+ key: string;
983
+ /** What was checked, in the reader's terms rather than ours. */
984
+ label: string;
985
+ status: CheckStatus;
986
+ /**
987
+ * What we found — the receipt, on a pass as much as on a fail. A green row
988
+ * saying "we looked and it is fine" is worth more when it says what it
989
+ * looked at.
990
+ */
991
+ evidence: string | null;
992
+ /** Why it matters to them. Printed only on a fail; a cleared check needs no
993
+ * argument. */
994
+ why: string;
995
+ scope: Scope;
996
+ };
997
+ declare function tally(checks: SiteCheck[]): {
998
+ passed: number;
999
+ failed: number;
1000
+ total: number;
1001
+ };
1002
+
585
1003
  /**
586
1004
  * Can a visitor actually get from where they landed to a way of contacting you?
587
1005
  *
@@ -674,6 +1092,18 @@ type PageAnchor = {
674
1092
  href: string;
675
1093
  text: string;
676
1094
  rel: string;
1095
+ /** As authored, lower-cased — `_blank` on a link that opens a new tab.
1096
+ * Optional: absent on reports stored before it was captured, which reads as
1097
+ * "not measured" rather than "opens in this tab". */
1098
+ target?: string;
1099
+ /** `aria-label` as authored, empty when there is none. This is the label a
1100
+ * screen reader actually announces, so a link whose visible text is "Learn
1101
+ * more" is not necessarily a link that announces "Learn more".
1102
+ *
1103
+ * Optional for the same reason `target` is: a report stored before it was
1104
+ * captured has it absent everywhere, and absent must read as "we did not
1105
+ * look", never as "there is none". */
1106
+ ariaLabel?: string;
677
1107
  };
678
1108
  /**
679
1109
  * What a form is FOR, inferred from its shape.
@@ -698,6 +1128,16 @@ type FormKind = "enquiry" | "subscribe" | "other";
698
1128
  /** One `<form>`, in enough detail to tell an enquiry form from a newsletter box
699
1129
  * or a search field. That distinction is the whole point: a site nobody can
700
1130
  * actually reach must not score as though it has a conversion path. */
1131
+ /** One control a visitor fills in. Just the three attributes that decide
1132
+ * whether the form is easy to complete on a phone. */
1133
+ type FormField = {
1134
+ /** Lower-cased, defaulting to "text" for a bare `<input>` exactly as a
1135
+ * browser does; `textarea` and `select` carry their tag name. */
1136
+ type: string;
1137
+ name: string | null;
1138
+ autocomplete: string | null;
1139
+ required: boolean;
1140
+ };
701
1141
  type FormShape = {
702
1142
  kind: FormKind;
703
1143
  /** As authored, or null for a form that posts to its own URL. */
@@ -711,6 +1151,9 @@ type FormShape = {
711
1151
  * and this is what separates "can be contacted" from "can be searched". */
712
1152
  hasContactField: boolean;
713
1153
  hasSubmit: boolean;
1154
+ /** Per-field detail. Optional: absent on reports stored before it existed,
1155
+ * and absence must read as "not measured", never as "no fields". */
1156
+ fields?: FormField[];
714
1157
  };
715
1158
  type PageExtract = {
716
1159
  title: string | null;
@@ -754,6 +1197,58 @@ type PageExtract = {
754
1197
  /** `src` of each `<img>`, as authored. Same resolution note as `anchors`. */
755
1198
  imageSrcs?: string[];
756
1199
  forms?: FormShape[];
1200
+ /**
1201
+ * Every `<meta>` that is not `og:`/`twitter:` — those are in `social`, and
1202
+ * carrying them twice would double the biggest part of a persisted extract.
1203
+ * Keyed by lower-cased `name` or `property`, so `robots`, `generator`,
1204
+ * `charset` and `viewport` are all reachable by the name they are written by.
1205
+ *
1206
+ * `metaDescription` and `hasViewportMeta` remain projected separately because
1207
+ * older stored reports have them and nothing should have to know that
1208
+ * `metas.description` is the same string.
1209
+ *
1210
+ * Optional: absent on reports stored before it existed, and absence must read
1211
+ * as "not measured", never as "this page declares no metas".
1212
+ */
1213
+ metas?: Record<string, string>;
1214
+ /**
1215
+ * Every `<link>` in the document, as authored.
1216
+ *
1217
+ * The elements were ALREADY collected by the extractor — only the canonical
1218
+ * one was ever projected onto this type — so this is a projection change
1219
+ * rather than a new traversal.
1220
+ *
1221
+ * Optional: absent on reports stored before it existed. Absence means "not
1222
+ * measured", never "this page declares no favicon".
1223
+ */
1224
+ links?: {
1225
+ rel: string;
1226
+ href: string;
1227
+ hreflang?: string;
1228
+ type?: string;
1229
+ }[];
1230
+ /**
1231
+ * `src` of each `<script src>`, as authored, CAPPED — see `scriptCount` for
1232
+ * the true total, exactly as `anchors`/`anchorCount` do.
1233
+ *
1234
+ * These are the receipts the stack readout is built from: `/wp-content/
1235
+ * plugins/<name>/`, `/_next/`, `hs-scripts.com`. Inline scripts are not
1236
+ * collected — only ones with a `src`, because a src is an address a reader
1237
+ * can go and check and an inline blob is not.
1238
+ */
1239
+ scriptSrcs?: string[];
1240
+ /**
1241
+ * URLs NAMED inside inline scripts — a different claim from a script that
1242
+ * is loaded, and the one that matters for a site which defers its tags.
1243
+ *
1244
+ * A page that injects `googletagmanager.com` only after the first scroll has
1245
+ * no analytics `src` in the DOM at crawl time. Reading that as "no analytics"
1246
+ * is our missing measurement reported as their defect. Absent on reports
1247
+ * stored before this existed, which reads as "not measured".
1248
+ */
1249
+ inlineScriptUrls?: string[];
1250
+ /** True number of `<script src>` on the page, before `scriptSrcs` was capped. */
1251
+ scriptCount?: number;
757
1252
  };
758
1253
  type PageCapture = {
759
1254
  url: string;
@@ -764,6 +1259,31 @@ type PageCapture = {
764
1259
  /** Extract of the Playwright-rendered DOM (what a browser sees). */
765
1260
  rendered: PageExtract | null;
766
1261
  error: string | null;
1262
+ /**
1263
+ * What the axe rule set found on this page, collected in the SAME browser
1264
+ * pass that produced `rendered` — never a second navigation.
1265
+ *
1266
+ * Null means the rules did not run: an older stored report, a page that
1267
+ * failed to render, or a scan that threw. It must never read as "no
1268
+ * violations", which is the opposite claim.
1269
+ */
1270
+ axe?: AxePageResult | null;
1271
+ /**
1272
+ * What the browser itself reported while this page was open — console errors,
1273
+ * failed requests, mobile overflow. Same pass as `rendered` and `axe`.
1274
+ *
1275
+ * Null means nothing was measured. It must never read as "nothing went
1276
+ * wrong", which is the opposite claim.
1277
+ */
1278
+ vitals?: PageVitals | null;
1279
+ /**
1280
+ * What happened when we pressed this page's enquiry-form submit button.
1281
+ *
1282
+ * Present on at most ONE page per crawl, and null everywhere else — absence
1283
+ * means "we did not interact here", never "the form is fine". Optional for
1284
+ * reports stored before any of this existed.
1285
+ */
1286
+ formProbe?: FormProbe | null;
767
1287
  };
768
1288
  type CrawlResult = {
769
1289
  /** Normalized origin, e.g. "https://example.com". */
@@ -771,9 +1291,27 @@ type CrawlResult = {
771
1291
  robotsTxt: string | null;
772
1292
  /** One entry per agent in crawl.ts's ALL_AGENTS (6 AI + 2 classical). */
773
1293
  agentAccess: RobotsAgentAccess[];
1294
+ /** `sample` is a capped slice of the URLs, absent on reports stored before
1295
+ * it existed — read absence as "not measured", never as "the sitemap is
1296
+ * empty". `urlCount` is the true total either way. */
774
1297
  sitemap: {
775
1298
  present: boolean;
776
1299
  urlCount: number;
1300
+ sample?: string[];
1301
+ /**
1302
+ * True when we stopped reading before the sitemap ended, which makes
1303
+ * `urlCount` a FLOOR rather than a count.
1304
+ *
1305
+ * A sitemap index is followed only so far — see `MAX_SITEMAP_CHILDREN` —
1306
+ * and viget.com publishes 35 children holding 1,636 URLs between them. We
1307
+ * read the first few, counted 66, and told them 179 of their own pages were
1308
+ * missing from their sitemap. Our ceiling, reported as their gap, which is
1309
+ * the one mistake this report keeps having to be stopped from making.
1310
+ *
1311
+ * Optional: absent on reports stored before it was tracked, and absent must
1312
+ * read as "we do not know", never as "we read the whole thing".
1313
+ */
1314
+ truncated?: boolean;
777
1315
  };
778
1316
  llmsTxt: {
779
1317
  present: boolean;
@@ -1106,10 +1644,55 @@ type ProspectAuditResult = {
1106
1644
  * single named goal rather than a generic template — see goals.ts. Optional
1107
1645
  * for reports stored before it existed. */
1108
1646
  goalFit?: StageResult<GoalFit>;
1647
+ /**
1648
+ * What they are running — platform, theme, plugins, host, forms, analytics.
1649
+ *
1650
+ * NOT a check and never part of any denominator: nothing in it passes or
1651
+ * fails. It opens the report because naming a reader's own stack back to them
1652
+ * answers "do these people know what they are talking about" before the first
1653
+ * finding. Optional for reports stored before the stage existed.
1654
+ */
1655
+ stack?: StageResult<StackReadout>;
1656
+ /**
1657
+ * The Tier 0 battery — the things a careful person would check with a
1658
+ * browser and ten minutes, every one of them a pure function of the crawl.
1659
+ *
1660
+ * Four states per check, and only `pass`/`fail` count toward the denominator:
1661
+ * a site with no schema has not failed the schema checks, and printing
1662
+ * "34 of 40" where six were never applicable would be a number inflated on
1663
+ * our own behalf. Optional for reports stored before the stage existed.
1664
+ */
1665
+ siteChecks?: StageResult<SiteCheck[]>;
1666
+ /**
1667
+ * The axe rule set, run against the rendered DOM.
1668
+ *
1669
+ * Named rules with named fixes, where the Lighthouse accessibility score is
1670
+ * one opaque number over a subset of the same rules. Optional for reports
1671
+ * stored before it existed — and absence means the rules did not run, never
1672
+ * that nothing was found.
1673
+ */
1674
+ accessibility?: StageResult<AccessibilityResult>;
1675
+ /**
1676
+ * What the DOMAIN says — SPF, DMARC, mail servers, registration expiry.
1677
+ *
1678
+ * None of it touches their web server, and none of it is visible from any
1679
+ * amount of reading their HTML. Optional for reports stored before it
1680
+ * existed.
1681
+ */
1682
+ dns?: StageResult<DnsFindings>;
1683
+ /**
1684
+ * What the server SERVED, as opposed to what the markup declared — the icon,
1685
+ * the share image, the sitemap's own URLs, and how many hops a link takes.
1686
+ *
1687
+ * The only part of the check battery that costs the prospect requests, which
1688
+ * is why it is capped, paced, and reports the count it made. Optional for
1689
+ * reports stored before it existed.
1690
+ */
1691
+ http?: StageResult<HttpFindings>;
1109
1692
  /** When an engine describes this business, where is it getting that from —
1110
1693
  * each statement sorted by SOURCE, never by truth. See accuracy.ts. Optional
1111
1694
  * for reports stored before the stage was wired in. */
1112
1695
  accuracy?: StageResult<AccuracyResult>;
1113
1696
  };
1114
1697
 
1115
- export { type AnalyzeResult, type AnswerSpace, type AssetCheck, type BasicsCheck, type BuyerQuestion, type ChecksResult, type ConsistencyResult, type ContactAffordance, type ContactVariant, type CrawlResult, type Fix, type FormKind, type FormShape, GOAL_LABELS, type GoalFit, type GoalRequirement, type JourneyMap, type LighthouseScores, type PageAnchor, type PageCapture, type PageExtract, type PageJourney, type ProbeAnswer, type ProbedUrl, type ProbesResult, type ProspectAuditResult, type Reachability, type RobotsAgentAccess, type Scope, type Scores, type SiteGoal, type SourceCount, type StageResult, orderRequirements };
1698
+ export { type AccessibilityResult, type AnalyzeResult, type AnswerSpace, type AssetCheck, type AxeImpact, type AxePageResult, type AxeViolation, type BasicsCheck, type BuyerQuestion, type CheckStatus, type ChecksResult, type ConsistencyResult, type ContactAffordance, type ContactVariant, type CrawlResult, type DnsFindings, type Fix, type FormField, type FormKind, type FormProbe, type FormShape, GOAL_LABELS, type GoalFit, type GoalRequirement, type HttpFindings, type JourneyMap, LAYER_LABELS, LAYER_ORDER, type LighthouseScores, type PageAnchor, type PageCapture, type PageExtract, type PageJourney, type ProbeAnswer, type ProbedUrl, type ProbesResult, type ProspectAuditResult, type Reachability, type RobotsAgentAccess, type Scope, type Scores, type SiteCheck, type SiteGoal, type SourceCount, type StackItem, type StackLayer, type StackReadout, type StageResult, orderRequirements, tally };
@@ -1,9 +1,18 @@
1
+ import {
2
+ LAYER_LABELS,
3
+ LAYER_ORDER,
4
+ tally
5
+ } from "../chunk-VELWHWOW.js";
1
6
  import {
2
7
  GOAL_LABELS,
3
8
  orderRequirements
4
9
  } from "../chunk-T7ZUUOZ2.js";
10
+ import "../chunk-RARREDQE.js";
5
11
  export {
6
12
  GOAL_LABELS,
7
- orderRequirements
13
+ LAYER_LABELS,
14
+ LAYER_ORDER,
15
+ orderRequirements,
16
+ tally
8
17
  };
9
18
  //# sourceMappingURL=types.js.map