@burdenoff/website-sdk 2026.1031.3 → 2026.1101.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -8
- package/dist/analytics/index.js +19 -69
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +3 -69
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/avatar-live/index.js +1 -0
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +1 -0
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.js +10 -30
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +4 -24
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/chunk-23H4JUL4.js +41 -0
- package/dist/chunk-23H4JUL4.js.map +1 -0
- package/dist/chunk-34RX5P75.mjs +25 -0
- package/dist/chunk-34RX5P75.mjs.map +1 -0
- package/dist/chunk-3LFFY7AZ.js +184 -0
- package/dist/chunk-3LFFY7AZ.js.map +1 -0
- package/dist/chunk-3X26GE4S.js +2794 -0
- package/dist/chunk-3X26GE4S.js.map +1 -0
- package/dist/chunk-45GCA4GG.js +85 -0
- package/dist/chunk-45GCA4GG.js.map +1 -0
- package/dist/chunk-4SAGVLHN.js +876 -0
- package/dist/chunk-4SAGVLHN.js.map +1 -0
- package/dist/chunk-4U6YAII5.js +218 -0
- package/dist/chunk-4U6YAII5.js.map +1 -0
- package/dist/chunk-5GMTBOO6.js +21 -0
- package/dist/chunk-5GMTBOO6.js.map +1 -0
- package/dist/chunk-5JRJ57CL.mjs +329 -0
- package/dist/chunk-5JRJ57CL.mjs.map +1 -0
- package/dist/chunk-6C2DKIC7.js +4 -0
- package/dist/chunk-6C2DKIC7.js.map +1 -0
- package/dist/chunk-6IBFBWC5.mjs +17 -0
- package/dist/chunk-6IBFBWC5.mjs.map +1 -0
- package/dist/chunk-6KY2ZRVI.js +116 -0
- package/dist/chunk-6KY2ZRVI.js.map +1 -0
- package/dist/chunk-6VJT3SM4.mjs +182 -0
- package/dist/chunk-6VJT3SM4.mjs.map +1 -0
- package/dist/chunk-6YZUFMK2.mjs +40 -0
- package/dist/chunk-6YZUFMK2.mjs.map +1 -0
- package/dist/chunk-7A6R7CGG.js +47 -0
- package/dist/chunk-7A6R7CGG.js.map +1 -0
- package/dist/chunk-7PWL4V2R.js +4 -0
- package/dist/chunk-7PWL4V2R.js.map +1 -0
- package/dist/chunk-AA33WDYS.js +161 -0
- package/dist/chunk-AA33WDYS.js.map +1 -0
- package/dist/chunk-AHRGFXV2.js +525 -0
- package/dist/chunk-AHRGFXV2.js.map +1 -0
- package/dist/chunk-B63XLDZH.mjs +109 -0
- package/dist/chunk-B63XLDZH.mjs.map +1 -0
- package/dist/chunk-BX2USEWF.mjs +35 -0
- package/dist/chunk-BX2USEWF.mjs.map +1 -0
- package/dist/chunk-BYSZT35R.js +150 -0
- package/dist/chunk-BYSZT35R.js.map +1 -0
- package/dist/chunk-C6OJIHNO.js +12 -0
- package/dist/chunk-C6OJIHNO.js.map +1 -0
- package/dist/chunk-COOELBX7.mjs +63 -0
- package/dist/chunk-COOELBX7.mjs.map +1 -0
- package/dist/chunk-D2TW7MNC.mjs +2771 -0
- package/dist/chunk-D2TW7MNC.mjs.map +1 -0
- package/dist/chunk-D5KEZ6V6.mjs +3 -0
- package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
- package/dist/chunk-E3GUGJMY.js +46 -0
- package/dist/chunk-E3GUGJMY.js.map +1 -0
- package/dist/chunk-EBZ2HGJ2.mjs +52 -0
- package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
- package/dist/chunk-EDLTUOIA.mjs +210 -0
- package/dist/chunk-EDLTUOIA.mjs.map +1 -0
- package/dist/chunk-EJQSOT4X.js +4 -0
- package/dist/chunk-EJQSOT4X.js.map +1 -0
- package/dist/chunk-EUPFS6MZ.mjs +2019 -0
- package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
- package/dist/chunk-F3XWDC3B.js +77 -0
- package/dist/chunk-F3XWDC3B.js.map +1 -0
- package/dist/chunk-FFADMVIK.mjs +5127 -0
- package/dist/chunk-FFADMVIK.mjs.map +1 -0
- package/dist/chunk-FJWS7BKT.mjs +3 -0
- package/dist/chunk-FJWS7BKT.mjs.map +1 -0
- package/dist/chunk-FJZJDUOR.mjs +3 -0
- package/dist/chunk-FJZJDUOR.mjs.map +1 -0
- package/dist/chunk-GCFLCO2Z.mjs +145 -0
- package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
- package/dist/chunk-GEXC4LRP.js +61 -0
- package/dist/chunk-GEXC4LRP.js.map +1 -0
- package/dist/chunk-GTRAXQGC.mjs +3 -0
- package/dist/chunk-GTRAXQGC.mjs.map +1 -0
- package/dist/chunk-IKOIRUKW.js +270 -0
- package/dist/chunk-IKOIRUKW.js.map +1 -0
- package/dist/chunk-IXTWOPRN.js +45 -0
- package/dist/chunk-IXTWOPRN.js.map +1 -0
- package/dist/chunk-JDXOXGOK.js +547 -0
- package/dist/chunk-JDXOXGOK.js.map +1 -0
- package/dist/chunk-JSLDWYV7.mjs +558 -0
- package/dist/chunk-JSLDWYV7.mjs.map +1 -0
- package/dist/chunk-JXLSTVBK.js +336 -0
- package/dist/chunk-JXLSTVBK.js.map +1 -0
- package/dist/chunk-KHYXK7FG.mjs +49 -0
- package/dist/chunk-KHYXK7FG.mjs.map +1 -0
- package/dist/chunk-KSMBOWQQ.js +2034 -0
- package/dist/chunk-KSMBOWQQ.js.map +1 -0
- package/dist/chunk-KU2PXHGB.mjs +83 -0
- package/dist/chunk-KU2PXHGB.mjs.map +1 -0
- package/dist/chunk-KZANIV3S.mjs +37 -0
- package/dist/chunk-KZANIV3S.mjs.map +1 -0
- package/dist/chunk-L4BP6YOB.js +20 -0
- package/dist/chunk-L4BP6YOB.js.map +1 -0
- package/dist/chunk-MRKA2OAF.mjs +75 -0
- package/dist/chunk-MRKA2OAF.mjs.map +1 -0
- package/dist/chunk-N5EC53MH.mjs +80 -0
- package/dist/chunk-N5EC53MH.mjs.map +1 -0
- package/dist/chunk-NZANQGDM.mjs +34 -0
- package/dist/chunk-NZANQGDM.mjs.map +1 -0
- package/dist/chunk-O4GS3XPZ.js +10 -0
- package/dist/chunk-O4GS3XPZ.js.map +1 -0
- package/dist/chunk-O76QDGRM.js +5154 -0
- package/dist/chunk-O76QDGRM.js.map +1 -0
- package/dist/chunk-OYEUNZJY.js +114 -0
- package/dist/chunk-OYEUNZJY.js.map +1 -0
- package/dist/chunk-PHXAGQNB.js +74 -0
- package/dist/chunk-PHXAGQNB.js.map +1 -0
- package/dist/chunk-PIHHPP7J.mjs +16 -0
- package/dist/chunk-PIHHPP7J.mjs.map +1 -0
- package/dist/chunk-PXRHVVOU.mjs +499 -0
- package/dist/chunk-PXRHVVOU.mjs.map +1 -0
- package/dist/chunk-QMA6U6AU.js +36 -0
- package/dist/chunk-QMA6U6AU.js.map +1 -0
- package/dist/chunk-R6J7IULF.js +44 -0
- package/dist/chunk-R6J7IULF.js.map +1 -0
- package/dist/chunk-R7G6TWIS.mjs +10 -0
- package/dist/chunk-R7G6TWIS.mjs.map +1 -0
- package/dist/chunk-RUMGFI6L.mjs +544 -0
- package/dist/chunk-RUMGFI6L.mjs.map +1 -0
- package/dist/chunk-S54JWSJF.js +565 -0
- package/dist/chunk-S54JWSJF.js.map +1 -0
- package/dist/chunk-SK5UGG6A.js +2041 -0
- package/dist/chunk-SK5UGG6A.js.map +1 -0
- package/dist/chunk-SKIYDKIS.mjs +70 -0
- package/dist/chunk-SKIYDKIS.mjs.map +1 -0
- package/dist/chunk-SZNS5IK6.mjs +322 -0
- package/dist/chunk-SZNS5IK6.mjs.map +1 -0
- package/dist/chunk-T7JIPZAC.js +66 -0
- package/dist/chunk-T7JIPZAC.js.map +1 -0
- package/dist/chunk-TGYQHO2E.mjs +864 -0
- package/dist/chunk-TGYQHO2E.mjs.map +1 -0
- package/dist/chunk-TT3MHN42.mjs +8 -0
- package/dist/chunk-TT3MHN42.mjs.map +1 -0
- package/dist/chunk-UM55U65W.mjs +268 -0
- package/dist/chunk-UM55U65W.mjs.map +1 -0
- package/dist/chunk-UQ2HALUD.js +72 -0
- package/dist/chunk-UQ2HALUD.js.map +1 -0
- package/dist/chunk-UXWT7G77.js +82 -0
- package/dist/chunk-UXWT7G77.js.map +1 -0
- package/dist/chunk-VNS5WDWO.mjs +7 -0
- package/dist/chunk-VNS5WDWO.mjs.map +1 -0
- package/dist/chunk-VRWZLGR7.js +4 -0
- package/dist/chunk-VRWZLGR7.js.map +1 -0
- package/dist/chunk-VZLDSNH4.mjs +22 -0
- package/dist/chunk-VZLDSNH4.mjs.map +1 -0
- package/dist/chunk-W6P3YETJ.js +92 -0
- package/dist/chunk-W6P3YETJ.js.map +1 -0
- package/dist/chunk-WPKFFQCR.mjs +90 -0
- package/dist/chunk-WPKFFQCR.mjs.map +1 -0
- package/dist/chunk-WTVTI5TO.mjs +22 -0
- package/dist/chunk-WTVTI5TO.mjs.map +1 -0
- package/dist/chunk-XJOBZ4RJ.js +44 -0
- package/dist/chunk-XJOBZ4RJ.js.map +1 -0
- package/dist/chunk-XP2JDNDU.js +325 -0
- package/dist/chunk-XP2JDNDU.js.map +1 -0
- package/dist/chunk-Y4V5CEWD.mjs +112 -0
- package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
- package/dist/chunk-YERN2UWM.js +116 -0
- package/dist/chunk-YERN2UWM.js.map +1 -0
- package/dist/chunk-ZKJQWFAA.mjs +114 -0
- package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
- package/dist/chunk-ZOON3S3Y.mjs +138 -0
- package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
- package/dist/chunk-ZP6QRAYC.mjs +2024 -0
- package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
- package/dist/chunk-ZSTFX7FV.js +9 -0
- package/dist/chunk-ZSTFX7FV.js.map +1 -0
- package/dist/client/index.js +23 -331
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -330
- package/dist/client/index.mjs.map +1 -1
- package/dist/comparison/index.js +28 -111
- package/dist/comparison/index.js.map +1 -1
- package/dist/comparison/index.mjs +2 -107
- package/dist/comparison/index.mjs.map +1 -1
- package/dist/components/contact.js +21 -931
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +12 -909
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/electron-download.js +12 -124
- package/dist/components/electron-download.js.map +1 -1
- package/dist/components/electron-download.mjs +4 -106
- package/dist/components/electron-download.mjs.map +1 -1
- package/dist/components/explore-cta.js +8 -112
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +3 -114
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.js +62 -7833
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +13 -7800
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/launch-countdown.js +8 -83
- package/dist/components/launch-countdown.js.map +1 -1
- package/dist/components/launch-countdown.mjs +3 -85
- package/dist/components/launch-countdown.mjs.map +1 -1
- package/dist/components/newsletter.js +20 -993
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +11 -971
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.js +24 -1302
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +15 -1276
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.js +49 -1249
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +8 -1226
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/problems-solved.js +8 -150
- package/dist/components/problems-solved.js.map +1 -1
- package/dist/components/problems-solved.mjs +4 -133
- package/dist/components/problems-solved.mjs.map +1 -1
- package/dist/components/resource-links.js +8 -304
- package/dist/components/resource-links.js.map +1 -1
- package/dist/components/resource-links.mjs +5 -308
- package/dist/components/resource-links.mjs.map +1 -1
- package/dist/components/social-links.js +8 -343
- package/dist/components/social-links.js.map +1 -1
- package/dist/components/social-links.mjs +5 -347
- package/dist/components/social-links.mjs.map +1 -1
- package/dist/components/unsubscribe.js +10 -627
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +6 -611
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.js +21 -1001
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +12 -982
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/components/website-switcher.js +12 -175
- package/dist/components/website-switcher.js.map +1 -1
- package/dist/components/website-switcher.mjs +4 -157
- package/dist/components/website-switcher.mjs.map +1 -1
- package/dist/content/index.js +118 -3616
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +12 -3567
- package/dist/content/index.mjs.map +1 -1
- package/dist/hooks/index.js +37 -2321
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -2318
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.js +4145 -20599
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3766 -20567
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.js +20 -393
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +5 -394
- package/dist/product/index.mjs.map +1 -1
- package/dist/seo/index.js +8 -76
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +3 -78
- package/dist/seo/index.mjs.map +1 -1
- package/dist/store/index.js +51 -2650
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +13 -2619
- package/dist/store/index.mjs.map +1 -1
- package/dist/utils/index.js +69 -134
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +5 -122
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1,109 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
4
|
-
var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
5
|
-
function parseVerifiedAt(value) {
|
|
6
|
-
if (typeof value !== "string" || !ISO_DATE.test(value)) return null;
|
|
7
|
-
const date = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
8
|
-
if (Number.isNaN(date.getTime())) return null;
|
|
9
|
-
return date.toISOString().slice(0, 10) === value ? date : null;
|
|
10
|
-
}
|
|
11
|
-
function isHttpUrl(value) {
|
|
12
|
-
try {
|
|
13
|
-
const url = new URL(value);
|
|
14
|
-
return url.protocol === "https:" || url.protocol === "http:";
|
|
15
|
-
} catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function checkComparisonFreshness(claims, options = {}) {
|
|
20
|
-
const maxAgeDays = options.maxAgeDays ?? DEFAULT_COMPARISON_MAX_AGE_DAYS;
|
|
21
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
22
|
-
const issues = [];
|
|
23
|
-
for (const claim of claims) {
|
|
24
|
-
const sources = claim.sources ?? [];
|
|
25
|
-
if (sources.length === 0) {
|
|
26
|
-
issues.push({
|
|
27
|
-
path: claim.path,
|
|
28
|
-
problem: "missing-source",
|
|
29
|
-
detail: "no source: add { url, verifiedAt } for the vendor page this claim was checked against"
|
|
30
|
-
});
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
let newest = null;
|
|
34
|
-
for (const source of sources) {
|
|
35
|
-
if (!isHttpUrl(source.url)) {
|
|
36
|
-
issues.push({
|
|
37
|
-
path: claim.path,
|
|
38
|
-
problem: "invalid-url",
|
|
39
|
-
detail: `source URL is not an http(s) URL: ${JSON.stringify(source.url)}`
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
const verified = parseVerifiedAt(source.verifiedAt);
|
|
43
|
-
if (!verified) {
|
|
44
|
-
issues.push({
|
|
45
|
-
path: claim.path,
|
|
46
|
-
problem: "invalid-date",
|
|
47
|
-
detail: `verifiedAt must be YYYY-MM-DD, got ${JSON.stringify(source.verifiedAt)}`
|
|
48
|
-
});
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (verified.getTime() > now.getTime() + DAY_MS) {
|
|
52
|
-
issues.push({
|
|
53
|
-
path: claim.path,
|
|
54
|
-
problem: "future-date",
|
|
55
|
-
detail: `verifiedAt ${source.verifiedAt} is in the future`
|
|
56
|
-
});
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (!newest || verified.getTime() > newest.getTime()) newest = verified;
|
|
60
|
-
}
|
|
61
|
-
if (newest) {
|
|
62
|
-
const ageDays = Math.floor((now.getTime() - newest.getTime()) / DAY_MS);
|
|
63
|
-
if (ageDays > maxAgeDays) {
|
|
64
|
-
issues.push({
|
|
65
|
-
path: claim.path,
|
|
66
|
-
problem: "stale",
|
|
67
|
-
detail: `last verified ${newest.toISOString().slice(0, 10)} (${ageDays} days ago, limit ${maxAgeDays}): re-read the vendor page and update verifiedAt or the claim`
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return issues;
|
|
73
|
-
}
|
|
74
|
-
function latestVerification(claims) {
|
|
75
|
-
let latest = null;
|
|
76
|
-
for (const claim of claims) {
|
|
77
|
-
for (const source of claim.sources ?? []) {
|
|
78
|
-
if (parseVerifiedAt(source.verifiedAt) && (!latest || source.verifiedAt > latest)) {
|
|
79
|
-
latest = source.verifiedAt;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return latest;
|
|
84
|
-
}
|
|
85
|
-
function oldestVerification(claims) {
|
|
86
|
-
let oldest = null;
|
|
87
|
-
for (const claim of claims) {
|
|
88
|
-
for (const source of claim.sources ?? []) {
|
|
89
|
-
if (parseVerifiedAt(source.verifiedAt) && (!oldest || source.verifiedAt < oldest)) {
|
|
90
|
-
oldest = source.verifiedAt;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return oldest;
|
|
95
|
-
}
|
|
96
|
-
function formatFreshnessReport(issues) {
|
|
97
|
-
if (issues.length === 0)
|
|
98
|
-
return "Comparison sources: all claims sourced and fresh.";
|
|
99
|
-
const lines = issues.map((i) => ` - [${i.problem}] ${i.path}: ${i.detail}`);
|
|
100
|
-
return [
|
|
101
|
-
`Comparison sources: ${issues.length} problem(s) found.`,
|
|
102
|
-
...lines,
|
|
103
|
-
"Re-check each claim against the vendor's current page, then update the claim and its verifiedAt."
|
|
104
|
-
].join("\n");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export { DEFAULT_COMPARISON_MAX_AGE_DAYS, checkComparisonFreshness, formatFreshnessReport, latestVerification, oldestVerification, parseVerifiedAt };
|
|
1
|
+
export { DEFAULT_COMPARISON_MAX_AGE_DAYS, checkComparisonFreshness, formatFreshnessReport, latestVerification, oldestVerification, parseVerifiedAt } from '../chunk-B63XLDZH.mjs';
|
|
2
|
+
import '../chunk-VNS5WDWO.mjs';
|
|
108
3
|
//# sourceMappingURL=index.mjs.map
|
|
109
4
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/comparison/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @burdenoff/website-sdk - Comparison page sources and freshness (BOFF-9971)\n *\n * Every product website with a /vs (competitor comparison) page makes claims\n * about other vendors. Those claims age fast: a vendor ships the feature we\n * said it lacked, or changes its prices. This module gives every site the same\n * contract and the same check:\n *\n * - each claim carries at least one source `{ url, verifiedAt }` (enforced by\n * the `Sourced` type at compile time), and\n * - `checkComparisonFreshness` fails when a claim has no source, a source is\n * not an http(s) URL, a date is malformed or in the future, or the newest\n * verification is older than `maxAgeDays` (180 by default).\n *\n * Sites run the check in their PR Check workflow, so a stale or unsourced\n * comparison blocks the next change until someone re-reads the vendor's page.\n *\n * @example\n * ```ts\n * import { checkComparisonFreshness, formatFreshnessReport } from '@burdenoff/website-sdk/comparison'\n *\n * const issues = checkComparisonFreshness(collectClaims(COMPETITORS))\n * if (issues.length) {\n * console.error(formatFreshnessReport(issues))\n * process.exit(1)\n * }\n * ```\n */\n\n/** Where a comparison claim was checked, and when (ISO date, YYYY-MM-DD). */\nexport interface ComparisonSource {\n /** The vendor page (or docs page) the claim was checked against. */\n url: string;\n /** The day a person last read that page and confirmed the claim. */\n verifiedAt: string;\n /** Optional short label shown with the link, e.g. \"Pricing\". */\n label?: string;\n}\n\n/** A non-empty list: a sourced claim always has at least one source. */\nexport type NonEmptySources = readonly [\n ComparisonSource,\n ...ComparisonSource[],\n];\n\n/** Mix-in for any comparison claim (table row, profile, pricing row). */\nexport interface Sourced {\n sources: NonEmptySources;\n}\n\n/** One claim to check, located by a human-readable path for the report. */\nexport interface ComparisonClaim {\n /** Where the claim lives, e.g. \"zapier.featureRows[Workflow versioning]\". */\n path: string;\n /** Its sources; missing or empty fails the check. */\n sources?: readonly ComparisonSource[] | null;\n}\n\nexport type FreshnessProblem =\n \"missing-source\" | \"invalid-url\" | \"invalid-date\" | \"future-date\" | \"stale\";\n\nexport interface FreshnessIssue {\n path: string;\n problem: FreshnessProblem;\n detail: string;\n}\n\nexport interface FreshnessOptions {\n /** Oldest acceptable verification, in days. Default 180. */\n maxAgeDays?: number;\n /** The clock to check against. Default: now. */\n now?: Date;\n}\n\n/** Default staleness threshold for comparison claims. */\nexport const DEFAULT_COMPARISON_MAX_AGE_DAYS = 180;\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parse a strict YYYY-MM-DD date as UTC midnight, or null when invalid. */\nexport function parseVerifiedAt(value: string): Date | null {\n if (typeof value !== \"string\" || !ISO_DATE.test(value)) return null;\n const date = new Date(`${value}T00:00:00.000Z`);\n if (Number.isNaN(date.getTime())) return null;\n // Reject rollovers such as 2026-02-31 → 2026-03-03.\n return date.toISOString().slice(0, 10) === value ? date : null;\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" || url.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\n/**\n * Check every claim and return the problems found (empty when all is well).\n * A claim passes on its newest valid source, so adding a fresh source to an\n * old claim is enough to clear a \"stale\" issue.\n */\nexport function checkComparisonFreshness(\n claims: readonly ComparisonClaim[],\n options: FreshnessOptions = {},\n): FreshnessIssue[] {\n const maxAgeDays = options.maxAgeDays ?? DEFAULT_COMPARISON_MAX_AGE_DAYS;\n const now = options.now ?? new Date();\n const issues: FreshnessIssue[] = [];\n\n for (const claim of claims) {\n const sources = claim.sources ?? [];\n if (sources.length === 0) {\n issues.push({\n path: claim.path,\n problem: \"missing-source\",\n detail:\n \"no source: add { url, verifiedAt } for the vendor page this claim was checked against\",\n });\n continue;\n }\n\n let newest: Date | null = null;\n for (const source of sources) {\n if (!isHttpUrl(source.url)) {\n issues.push({\n path: claim.path,\n problem: \"invalid-url\",\n detail: `source URL is not an http(s) URL: ${JSON.stringify(source.url)}`,\n });\n }\n const verified = parseVerifiedAt(source.verifiedAt);\n if (!verified) {\n issues.push({\n path: claim.path,\n problem: \"invalid-date\",\n detail: `verifiedAt must be YYYY-MM-DD, got ${JSON.stringify(source.verifiedAt)}`,\n });\n continue;\n }\n if (verified.getTime() > now.getTime() + DAY_MS) {\n issues.push({\n path: claim.path,\n problem: \"future-date\",\n detail: `verifiedAt ${source.verifiedAt} is in the future`,\n });\n continue;\n }\n if (!newest || verified.getTime() > newest.getTime()) newest = verified;\n }\n\n if (newest) {\n const ageDays = Math.floor((now.getTime() - newest.getTime()) / DAY_MS);\n if (ageDays > maxAgeDays) {\n issues.push({\n path: claim.path,\n problem: \"stale\",\n detail: `last verified ${newest.toISOString().slice(0, 10)} (${ageDays} days ago, limit ${maxAgeDays}): re-read the vendor page and update verifiedAt or the claim`,\n });\n }\n }\n }\n\n return issues;\n}\n\n/** Newest valid verification date across all claims, or null. */\nexport function latestVerification(\n claims: readonly ComparisonClaim[],\n): string | null {\n let latest: string | null = null;\n for (const claim of claims) {\n for (const source of claim.sources ?? []) {\n if (\n parseVerifiedAt(source.verifiedAt) &&\n (!latest || source.verifiedAt > latest)\n ) {\n latest = source.verifiedAt;\n }\n }\n }\n return latest;\n}\n\n/** Oldest valid verification date across all claims, or null. */\nexport function oldestVerification(\n claims: readonly ComparisonClaim[],\n): string | null {\n let oldest: string | null = null;\n for (const claim of claims) {\n for (const source of claim.sources ?? []) {\n if (\n parseVerifiedAt(source.verifiedAt) &&\n (!oldest || source.verifiedAt < oldest)\n ) {\n oldest = source.verifiedAt;\n }\n }\n }\n return oldest;\n}\n\n/** A plain-text report for CI logs, one line per issue. */\nexport function formatFreshnessReport(\n issues: readonly FreshnessIssue[],\n): string {\n if (issues.length === 0)\n return \"Comparison sources: all claims sourced and fresh.\";\n const lines = issues.map((i) => ` - [${i.problem}] ${i.path}: ${i.detail}`);\n return [\n `Comparison sources: ${issues.length} problem(s) found.`,\n ...lines,\n \"Re-check each claim against the vendor's current page, then update the claim and its verifiedAt.\",\n ].join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
|