@burdenoff/website-sdk 2026.1027.1 → 2026.1028.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/comparison/index.d.mts +83 -0
- package/dist/comparison/index.d.ts +83 -0
- package/dist/comparison/index.js +116 -0
- package/dist/comparison/index.js.map +1 -0
- package/dist/comparison/index.mjs +109 -0
- package/dist/comparison/index.mjs.map +1 -0
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs.map +1 -1
- package/dist/content/index.d.mts +1 -1
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +32 -4
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +32 -5
- package/dist/content/index.mjs.map +1 -1
- package/dist/{index-CB0Uw1xD.d.ts → index-87BLB-Hj.d.ts} +26 -1
- package/dist/{index-Ctws7kGs.d.mts → index-Chw0pDXS.d.mts} +26 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +144 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +138 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -1
|
@@ -45,6 +45,16 @@ interface CareersApplyFormProps {
|
|
|
45
45
|
}
|
|
46
46
|
declare function CareersApplyForm({ productName, recaptchaSiteKey, positions, postings, heroTitle, heroDescription, headingLevel, maxResumeBytes, className, seo, }: CareersApplyFormProps): react.JSX.Element;
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* A customer's written approval of a case study (BOFF-9971). Public callers
|
|
50
|
+
* receive only the approval date; who approved it and where the approval is
|
|
51
|
+
* filed stay with the people who manage content pages.
|
|
52
|
+
*/
|
|
53
|
+
interface CustomerSignOffData {
|
|
54
|
+
signedAt: string;
|
|
55
|
+
signedBy?: string | null;
|
|
56
|
+
documentRef?: string | null;
|
|
57
|
+
}
|
|
48
58
|
interface ContentPageData {
|
|
49
59
|
id: string;
|
|
50
60
|
title: string;
|
|
@@ -64,6 +74,7 @@ interface ContentPageData {
|
|
|
64
74
|
readingTime?: number | null;
|
|
65
75
|
featured?: boolean;
|
|
66
76
|
category?: string | null;
|
|
77
|
+
customerSignOff?: CustomerSignOffData | null;
|
|
67
78
|
}
|
|
68
79
|
interface ContentPageListItem {
|
|
69
80
|
id: string;
|
|
@@ -79,6 +90,7 @@ interface ContentPageListItem {
|
|
|
79
90
|
featured?: boolean;
|
|
80
91
|
category?: string | null;
|
|
81
92
|
seoDescription?: string | null;
|
|
93
|
+
customerSignOff?: CustomerSignOffData | null;
|
|
82
94
|
}
|
|
83
95
|
interface ContentPageListData {
|
|
84
96
|
items: ContentPageListItem[];
|
|
@@ -422,6 +434,19 @@ interface CaseStudyPageProps {
|
|
|
422
434
|
contactLabel?: string;
|
|
423
435
|
className?: string;
|
|
424
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* True when the customer has approved the story in writing (BOFF-9971).
|
|
439
|
+
*
|
|
440
|
+
* The CMS already refuses to serve an unapproved case study; this check is the
|
|
441
|
+
* second lock, so no website renders one even against a CMS build that predates
|
|
442
|
+
* the gate. A case study states results on a customer's behalf, and we never
|
|
443
|
+
* publish numbers a customer has not signed off.
|
|
444
|
+
*/
|
|
445
|
+
declare function hasCustomerSignOff(page: {
|
|
446
|
+
customerSignOff?: {
|
|
447
|
+
signedAt?: string | null;
|
|
448
|
+
} | null;
|
|
449
|
+
}): boolean;
|
|
425
450
|
declare function CaseStudiesListPage(props: CaseStudiesListPageProps): react.JSX.Element;
|
|
426
451
|
declare function CaseStudyPage(props: CaseStudyPageProps): react.JSX.Element;
|
|
427
452
|
|
|
@@ -534,4 +559,4 @@ interface ContractsPageProps {
|
|
|
534
559
|
}
|
|
535
560
|
declare function ContractsPage(props: ContractsPageProps): react.JSX.Element;
|
|
536
561
|
|
|
537
|
-
export { type
|
|
562
|
+
export { usePublicJobPostingsForSubmission as $, type MarkdownProps as A, BlogListPage as B, type CareersPostingOption as C, type PressKitAssetCategory as D, PressKitPage as E, type FetchLegalContentOptions as F, type PressKitPageProps as G, type PublicInterviewRoundConfig as H, type PublicJobPosting as I, type UsePublicJobPostingsOptions as J, fetchLegalContent as K, LEGAL_DOCUMENTS as L, Markdown as M, fetchLegalContentPage as N, fetchPublicJobPosting as O, type PressKitAsset as P, fetchPublicJobPostings as Q, fetchPublicJobPostingsForSubmission as R, findLegalDocument as S, formatLegalDate as T, type UsePublicJobPostingOptions as U, hasCustomerSignOff as V, splitLeadingHeading as W, useContentPage as X, useContentPages as Y, usePublicJobPosting as Z, usePublicJobPostings as _, type BlogListPageProps as a, type UseContentPageOptions as a0, BlogPostPage as b, type BlogPostPageProps as c, CareersApplyForm as d, type CareersApplyFormProps as e, CaseStudiesListPage as f, type CaseStudiesListPageProps as g, CaseStudyPage as h, type CaseStudyPageProps as i, type ChangelogEntry as j, ChangelogPage as k, type ChangelogPageProps as l, type ContentPageData as m, type ContentPageListData as n, type ContentPageListItem as o, ContentRenderer as p, type ContractAsset as q, ContractsPage as r, type ContractsPageProps as s, type CustomerSignOffData as t, LEGAL_DOCUMENT_KEYS as u, type LegalContentEntry as v, type LegalDocument as w, type LegalDocumentKey as x, LegalPage as y, type LegalPageProps as z };
|
|
@@ -45,6 +45,16 @@ interface CareersApplyFormProps {
|
|
|
45
45
|
}
|
|
46
46
|
declare function CareersApplyForm({ productName, recaptchaSiteKey, positions, postings, heroTitle, heroDescription, headingLevel, maxResumeBytes, className, seo, }: CareersApplyFormProps): react.JSX.Element;
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* A customer's written approval of a case study (BOFF-9971). Public callers
|
|
50
|
+
* receive only the approval date; who approved it and where the approval is
|
|
51
|
+
* filed stay with the people who manage content pages.
|
|
52
|
+
*/
|
|
53
|
+
interface CustomerSignOffData {
|
|
54
|
+
signedAt: string;
|
|
55
|
+
signedBy?: string | null;
|
|
56
|
+
documentRef?: string | null;
|
|
57
|
+
}
|
|
48
58
|
interface ContentPageData {
|
|
49
59
|
id: string;
|
|
50
60
|
title: string;
|
|
@@ -64,6 +74,7 @@ interface ContentPageData {
|
|
|
64
74
|
readingTime?: number | null;
|
|
65
75
|
featured?: boolean;
|
|
66
76
|
category?: string | null;
|
|
77
|
+
customerSignOff?: CustomerSignOffData | null;
|
|
67
78
|
}
|
|
68
79
|
interface ContentPageListItem {
|
|
69
80
|
id: string;
|
|
@@ -79,6 +90,7 @@ interface ContentPageListItem {
|
|
|
79
90
|
featured?: boolean;
|
|
80
91
|
category?: string | null;
|
|
81
92
|
seoDescription?: string | null;
|
|
93
|
+
customerSignOff?: CustomerSignOffData | null;
|
|
82
94
|
}
|
|
83
95
|
interface ContentPageListData {
|
|
84
96
|
items: ContentPageListItem[];
|
|
@@ -422,6 +434,19 @@ interface CaseStudyPageProps {
|
|
|
422
434
|
contactLabel?: string;
|
|
423
435
|
className?: string;
|
|
424
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* True when the customer has approved the story in writing (BOFF-9971).
|
|
439
|
+
*
|
|
440
|
+
* The CMS already refuses to serve an unapproved case study; this check is the
|
|
441
|
+
* second lock, so no website renders one even against a CMS build that predates
|
|
442
|
+
* the gate. A case study states results on a customer's behalf, and we never
|
|
443
|
+
* publish numbers a customer has not signed off.
|
|
444
|
+
*/
|
|
445
|
+
declare function hasCustomerSignOff(page: {
|
|
446
|
+
customerSignOff?: {
|
|
447
|
+
signedAt?: string | null;
|
|
448
|
+
} | null;
|
|
449
|
+
}): boolean;
|
|
425
450
|
declare function CaseStudiesListPage(props: CaseStudiesListPageProps): react.JSX.Element;
|
|
426
451
|
declare function CaseStudyPage(props: CaseStudyPageProps): react.JSX.Element;
|
|
427
452
|
|
|
@@ -534,4 +559,4 @@ interface ContractsPageProps {
|
|
|
534
559
|
}
|
|
535
560
|
declare function ContractsPage(props: ContractsPageProps): react.JSX.Element;
|
|
536
561
|
|
|
537
|
-
export { type
|
|
562
|
+
export { usePublicJobPostingsForSubmission as $, type MarkdownProps as A, BlogListPage as B, type CareersPostingOption as C, type PressKitAssetCategory as D, PressKitPage as E, type FetchLegalContentOptions as F, type PressKitPageProps as G, type PublicInterviewRoundConfig as H, type PublicJobPosting as I, type UsePublicJobPostingsOptions as J, fetchLegalContent as K, LEGAL_DOCUMENTS as L, Markdown as M, fetchLegalContentPage as N, fetchPublicJobPosting as O, type PressKitAsset as P, fetchPublicJobPostings as Q, fetchPublicJobPostingsForSubmission as R, findLegalDocument as S, formatLegalDate as T, type UsePublicJobPostingOptions as U, hasCustomerSignOff as V, splitLeadingHeading as W, useContentPage as X, useContentPages as Y, usePublicJobPosting as Z, usePublicJobPostings as _, type BlogListPageProps as a, type UseContentPageOptions as a0, BlogPostPage as b, type BlogPostPageProps as c, CareersApplyForm as d, type CareersApplyFormProps as e, CaseStudiesListPage as f, type CaseStudiesListPageProps as g, CaseStudyPage as h, type CaseStudyPageProps as i, type ChangelogEntry as j, ChangelogPage as k, type ChangelogPageProps as l, type ContentPageData as m, type ContentPageListData as n, type ContentPageListItem as o, ContentRenderer as p, type ContractAsset as q, ContractsPage as r, type ContractsPageProps as s, type CustomerSignOffData as t, LEGAL_DOCUMENT_KEYS as u, type LegalContentEntry as v, type LegalDocument as w, type LegalDocumentKey as x, LegalPage as y, type LegalPageProps as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -2,8 +2,8 @@ export { G as GraphQLResponse, W as WebSDKClient, a as WebSDKClientConfig, b as
|
|
|
2
2
|
export { W as WebSDKProvider, a as WebSDKProviderProps, u as useWebSDK, b as useWebSDKConfig } from './provider-MFV1NVqZ.mjs';
|
|
3
3
|
export { ContactCategory, ContactPage, ContactPageProps, DEFAULT_CONTACT_CATEGORIES } from './components/contact.mjs';
|
|
4
4
|
export { PartnersPage, PartnersPageProps } from './components/partners.mjs';
|
|
5
|
-
import { C as CareersPostingOption } from './index-
|
|
6
|
-
export { B as BlogListPage, a as BlogListPageProps, b as BlogPostPage, c as BlogPostPageProps, d as CareersApplyForm, e as CareersApplyFormProps, f as CaseStudiesListPage, g as CaseStudiesListPageProps, h as CaseStudyPage, i as CaseStudyPageProps, j as ChangelogEntry, k as ChangelogPage, l as ChangelogPageProps, m as ContentPageData, n as ContentPageListData, o as ContentPageListItem, p as ContentRenderer, q as ContractAsset, r as ContractsPage, s as ContractsPageProps, F as FetchLegalContentOptions, L as LEGAL_DOCUMENTS,
|
|
5
|
+
import { C as CareersPostingOption } from './index-Chw0pDXS.mjs';
|
|
6
|
+
export { B as BlogListPage, a as BlogListPageProps, b as BlogPostPage, c as BlogPostPageProps, d as CareersApplyForm, e as CareersApplyFormProps, f as CaseStudiesListPage, g as CaseStudiesListPageProps, h as CaseStudyPage, i as CaseStudyPageProps, j as ChangelogEntry, k as ChangelogPage, l as ChangelogPageProps, m as ContentPageData, n as ContentPageListData, o as ContentPageListItem, p as ContentRenderer, q as ContractAsset, r as ContractsPage, s as ContractsPageProps, t as CustomerSignOffData, F as FetchLegalContentOptions, L as LEGAL_DOCUMENTS, u as LEGAL_DOCUMENT_KEYS, v as LegalContentEntry, w as LegalDocument, x as LegalDocumentKey, y as LegalPage, z as LegalPageProps, M as Markdown, A as MarkdownProps, P as PressKitAsset, D as PressKitAssetCategory, E as PressKitPage, G as PressKitPageProps, H as PublicInterviewRoundConfig, I as PublicJobPosting, U as UsePublicJobPostingOptions, J as UsePublicJobPostingsOptions, K as fetchLegalContent, N as fetchLegalContentPage, O as fetchPublicJobPosting, Q as fetchPublicJobPostings, R as fetchPublicJobPostingsForSubmission, S as findLegalDocument, T as formatLegalDate, V as hasCustomerSignOff, W as splitLeadingHeading, X as useContentPage, Y as useContentPages, Z as usePublicJobPosting, _ as usePublicJobPostings, $ as usePublicJobPostingsForSubmission } from './index-Chw0pDXS.mjs';
|
|
7
7
|
import * as react from 'react';
|
|
8
8
|
import { ComponentType, ReactNode, Dispatch, SetStateAction, RefObject } from 'react';
|
|
9
9
|
export { NewsletterForm, NewsletterFormProps, NewsletterPage, NewsletterPageProps } from './components/newsletter.mjs';
|
|
@@ -32,6 +32,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
32
32
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
33
33
|
export { B as BrowseStoreResult, G as GroupedStoreProducts, P as ProductCard, a as ProductCategory, R as RatingDistribution, S as SearchSuggestion, b as StarRating, c as StoreBrowsePage, d as StoreBrowsePageProps, e as StoreFacet, f as StoreFacets, g as StoreHomePage, h as StoreHomePageProps, i as StorePageProps, j as StoreProduct, k as StoreProductDetailPage, l as StoreProductDetailPageProps, m as StoreProductDetails, n as StorePublisher, o as StoreReview, p as StoreTerminology, q as StoreTypeFilter, T as TypeBadge, r as formatDownloads, s as formatPrice } from './product-card-CGxETsFa.mjs';
|
|
34
34
|
export { EXPLORE_RECAPTCHA_ACTION, FOOTERLESS_ROUTES, PRODUCT_SLUG_ALIASES, PRODUCT_SLUG_EMIT, PRODUCT_SLUG_RENAMES, ProductSlugGeneration, ProductSlugRename, RecaptchaV3, cn, emitProductSlug, getRecaptchaV3Token, isFooterlessPath, isSameProductSlug, legacyProductSlug, loadRecaptchaV3, normalizeProductSlug, normalizeRoutePath, productSlugVariants, shouldRenderSiteFooter } from './utils/index.mjs';
|
|
35
|
+
export { ComparisonClaim, ComparisonSource, DEFAULT_COMPARISON_MAX_AGE_DAYS, FreshnessIssue, FreshnessOptions, FreshnessProblem, NonEmptySources, Sourced, checkComparisonFreshness, formatFreshnessReport, latestVerification, oldestVerification, parseVerifiedAt } from './comparison/index.mjs';
|
|
35
36
|
import 'clsx';
|
|
36
37
|
|
|
37
38
|
interface CareersSubmissionFormProps {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { G as GraphQLResponse, W as WebSDKClient, a as WebSDKClientConfig, b as
|
|
|
2
2
|
export { W as WebSDKProvider, a as WebSDKProviderProps, u as useWebSDK, b as useWebSDKConfig } from './provider-Dwgz4d0r.js';
|
|
3
3
|
export { ContactCategory, ContactPage, ContactPageProps, DEFAULT_CONTACT_CATEGORIES } from './components/contact.js';
|
|
4
4
|
export { PartnersPage, PartnersPageProps } from './components/partners.js';
|
|
5
|
-
import { C as CareersPostingOption } from './index-
|
|
6
|
-
export { B as BlogListPage, a as BlogListPageProps, b as BlogPostPage, c as BlogPostPageProps, d as CareersApplyForm, e as CareersApplyFormProps, f as CaseStudiesListPage, g as CaseStudiesListPageProps, h as CaseStudyPage, i as CaseStudyPageProps, j as ChangelogEntry, k as ChangelogPage, l as ChangelogPageProps, m as ContentPageData, n as ContentPageListData, o as ContentPageListItem, p as ContentRenderer, q as ContractAsset, r as ContractsPage, s as ContractsPageProps, F as FetchLegalContentOptions, L as LEGAL_DOCUMENTS,
|
|
5
|
+
import { C as CareersPostingOption } from './index-87BLB-Hj.js';
|
|
6
|
+
export { B as BlogListPage, a as BlogListPageProps, b as BlogPostPage, c as BlogPostPageProps, d as CareersApplyForm, e as CareersApplyFormProps, f as CaseStudiesListPage, g as CaseStudiesListPageProps, h as CaseStudyPage, i as CaseStudyPageProps, j as ChangelogEntry, k as ChangelogPage, l as ChangelogPageProps, m as ContentPageData, n as ContentPageListData, o as ContentPageListItem, p as ContentRenderer, q as ContractAsset, r as ContractsPage, s as ContractsPageProps, t as CustomerSignOffData, F as FetchLegalContentOptions, L as LEGAL_DOCUMENTS, u as LEGAL_DOCUMENT_KEYS, v as LegalContentEntry, w as LegalDocument, x as LegalDocumentKey, y as LegalPage, z as LegalPageProps, M as Markdown, A as MarkdownProps, P as PressKitAsset, D as PressKitAssetCategory, E as PressKitPage, G as PressKitPageProps, H as PublicInterviewRoundConfig, I as PublicJobPosting, U as UsePublicJobPostingOptions, J as UsePublicJobPostingsOptions, K as fetchLegalContent, N as fetchLegalContentPage, O as fetchPublicJobPosting, Q as fetchPublicJobPostings, R as fetchPublicJobPostingsForSubmission, S as findLegalDocument, T as formatLegalDate, V as hasCustomerSignOff, W as splitLeadingHeading, X as useContentPage, Y as useContentPages, Z as usePublicJobPosting, _ as usePublicJobPostings, $ as usePublicJobPostingsForSubmission } from './index-87BLB-Hj.js';
|
|
7
7
|
import * as react from 'react';
|
|
8
8
|
import { ComponentType, ReactNode, Dispatch, SetStateAction, RefObject } from 'react';
|
|
9
9
|
export { NewsletterForm, NewsletterFormProps, NewsletterPage, NewsletterPageProps } from './components/newsletter.js';
|
|
@@ -32,6 +32,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
32
32
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
33
33
|
export { B as BrowseStoreResult, G as GroupedStoreProducts, P as ProductCard, a as ProductCategory, R as RatingDistribution, S as SearchSuggestion, b as StarRating, c as StoreBrowsePage, d as StoreBrowsePageProps, e as StoreFacet, f as StoreFacets, g as StoreHomePage, h as StoreHomePageProps, i as StorePageProps, j as StoreProduct, k as StoreProductDetailPage, l as StoreProductDetailPageProps, m as StoreProductDetails, n as StorePublisher, o as StoreReview, p as StoreTerminology, q as StoreTypeFilter, T as TypeBadge, r as formatDownloads, s as formatPrice } from './product-card-CGxETsFa.js';
|
|
34
34
|
export { EXPLORE_RECAPTCHA_ACTION, FOOTERLESS_ROUTES, PRODUCT_SLUG_ALIASES, PRODUCT_SLUG_EMIT, PRODUCT_SLUG_RENAMES, ProductSlugGeneration, ProductSlugRename, RecaptchaV3, cn, emitProductSlug, getRecaptchaV3Token, isFooterlessPath, isSameProductSlug, legacyProductSlug, loadRecaptchaV3, normalizeProductSlug, normalizeRoutePath, productSlugVariants, shouldRenderSiteFooter } from './utils/index.js';
|
|
35
|
+
export { ComparisonClaim, ComparisonSource, DEFAULT_COMPARISON_MAX_AGE_DAYS, FreshnessIssue, FreshnessOptions, FreshnessProblem, NonEmptySources, Sourced, checkComparisonFreshness, formatFreshnessReport, latestVerification, oldestVerification, parseVerifiedAt } from './comparison/index.js';
|
|
35
36
|
import 'clsx';
|
|
36
37
|
|
|
37
38
|
interface CareersSubmissionFormProps {
|
package/dist/index.js
CHANGED
|
@@ -16628,6 +16628,10 @@ function BlogPostPage(props) {
|
|
|
16628
16628
|
] });
|
|
16629
16629
|
}
|
|
16630
16630
|
var CASE_STUDY_TAG = "case-study";
|
|
16631
|
+
function hasCustomerSignOff(page) {
|
|
16632
|
+
const signedAt = page.customerSignOff?.signedAt;
|
|
16633
|
+
return typeof signedAt === "string" && !Number.isNaN(new Date(signedAt).getTime());
|
|
16634
|
+
}
|
|
16631
16635
|
var ANIMATION_STYLES = `
|
|
16632
16636
|
@keyframes csFadeUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
|
|
16633
16637
|
@keyframes csFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-14px); } }
|
|
@@ -16657,6 +16661,9 @@ var CASE_STUDIES_QUERY = `
|
|
|
16657
16661
|
readingTime
|
|
16658
16662
|
featured
|
|
16659
16663
|
category
|
|
16664
|
+
customerSignOff {
|
|
16665
|
+
signedAt
|
|
16666
|
+
}
|
|
16660
16667
|
}
|
|
16661
16668
|
total
|
|
16662
16669
|
hasMore
|
|
@@ -16683,6 +16690,9 @@ var CASE_STUDY_QUERY = `
|
|
|
16683
16690
|
readingTime
|
|
16684
16691
|
featured
|
|
16685
16692
|
category
|
|
16693
|
+
customerSignOff {
|
|
16694
|
+
signedAt
|
|
16695
|
+
}
|
|
16686
16696
|
}
|
|
16687
16697
|
}
|
|
16688
16698
|
`;
|
|
@@ -17053,11 +17063,15 @@ function CaseStudiesListPage(props) {
|
|
|
17053
17063
|
setPosts([]);
|
|
17054
17064
|
return;
|
|
17055
17065
|
}
|
|
17056
|
-
const
|
|
17057
|
-
|
|
17066
|
+
const items = result.data?.publicContentPages?.items || [];
|
|
17067
|
+
const caseStudies = items.filter(
|
|
17068
|
+
(p) => p.tags?.includes(CASE_STUDY_TAG) && hasCustomerSignOff(p)
|
|
17058
17069
|
);
|
|
17059
17070
|
setPosts(caseStudies);
|
|
17060
|
-
|
|
17071
|
+
const dropped = items.length - caseStudies.length;
|
|
17072
|
+
setTotal(
|
|
17073
|
+
Math.max(0, (result.data?.publicContentPages?.total || 0) - dropped)
|
|
17074
|
+
);
|
|
17061
17075
|
setError(null);
|
|
17062
17076
|
} catch {
|
|
17063
17077
|
setError("load-failed");
|
|
@@ -17239,7 +17253,8 @@ function CaseStudyPage(props) {
|
|
|
17239
17253
|
setError(result.errors[0]?.message ?? "Failed to load case study");
|
|
17240
17254
|
return;
|
|
17241
17255
|
}
|
|
17242
|
-
|
|
17256
|
+
const page = result.data?.publicContentPage ?? null;
|
|
17257
|
+
setPost(page && hasCustomerSignOff(page) ? page : null);
|
|
17243
17258
|
setError(null);
|
|
17244
17259
|
} catch {
|
|
17245
17260
|
setError("Failed to load case study");
|
|
@@ -17308,6 +17323,7 @@ function CaseStudyPage(props) {
|
|
|
17308
17323
|
] }) });
|
|
17309
17324
|
}
|
|
17310
17325
|
const formattedDate = formatDate(post.publishedAt || post.lastUpdated);
|
|
17326
|
+
const approvedDate = formatDate(post.customerSignOff?.signedAt);
|
|
17311
17327
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
|
|
17312
17328
|
/* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
|
|
17313
17329
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -17345,6 +17361,17 @@ function CaseStudyPage(props) {
|
|
|
17345
17361
|
] })
|
|
17346
17362
|
] }) : null
|
|
17347
17363
|
] }),
|
|
17364
|
+
approvedDate && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17365
|
+
"p",
|
|
17366
|
+
{
|
|
17367
|
+
className: "-mt-4 mb-8 text-xs text-muted-foreground",
|
|
17368
|
+
"data-cs": "customer-signoff",
|
|
17369
|
+
children: [
|
|
17370
|
+
"Approved for publication by the customer on ",
|
|
17371
|
+
approvedDate
|
|
17372
|
+
]
|
|
17373
|
+
}
|
|
17374
|
+
),
|
|
17348
17375
|
post.thumbnail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-10 aspect-[16/9] overflow-hidden rounded-xl", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17349
17376
|
"img",
|
|
17350
17377
|
{
|
|
@@ -20637,6 +20664,112 @@ function shouldRenderSiteFooter(pathname) {
|
|
|
20637
20664
|
return !isFooterlessPath(pathname);
|
|
20638
20665
|
}
|
|
20639
20666
|
|
|
20667
|
+
// src/comparison/index.ts
|
|
20668
|
+
var DEFAULT_COMPARISON_MAX_AGE_DAYS = 180;
|
|
20669
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
20670
|
+
var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
20671
|
+
function parseVerifiedAt(value) {
|
|
20672
|
+
if (typeof value !== "string" || !ISO_DATE.test(value)) return null;
|
|
20673
|
+
const date = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
20674
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
20675
|
+
return date.toISOString().slice(0, 10) === value ? date : null;
|
|
20676
|
+
}
|
|
20677
|
+
function isHttpUrl(value) {
|
|
20678
|
+
try {
|
|
20679
|
+
const url = new URL(value);
|
|
20680
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
20681
|
+
} catch {
|
|
20682
|
+
return false;
|
|
20683
|
+
}
|
|
20684
|
+
}
|
|
20685
|
+
function checkComparisonFreshness(claims, options = {}) {
|
|
20686
|
+
const maxAgeDays = options.maxAgeDays ?? DEFAULT_COMPARISON_MAX_AGE_DAYS;
|
|
20687
|
+
const now2 = options.now ?? /* @__PURE__ */ new Date();
|
|
20688
|
+
const issues = [];
|
|
20689
|
+
for (const claim of claims) {
|
|
20690
|
+
const sources = claim.sources ?? [];
|
|
20691
|
+
if (sources.length === 0) {
|
|
20692
|
+
issues.push({
|
|
20693
|
+
path: claim.path,
|
|
20694
|
+
problem: "missing-source",
|
|
20695
|
+
detail: "no source: add { url, verifiedAt } for the vendor page this claim was checked against"
|
|
20696
|
+
});
|
|
20697
|
+
continue;
|
|
20698
|
+
}
|
|
20699
|
+
let newest = null;
|
|
20700
|
+
for (const source of sources) {
|
|
20701
|
+
if (!isHttpUrl(source.url)) {
|
|
20702
|
+
issues.push({
|
|
20703
|
+
path: claim.path,
|
|
20704
|
+
problem: "invalid-url",
|
|
20705
|
+
detail: `source URL is not an http(s) URL: ${JSON.stringify(source.url)}`
|
|
20706
|
+
});
|
|
20707
|
+
}
|
|
20708
|
+
const verified = parseVerifiedAt(source.verifiedAt);
|
|
20709
|
+
if (!verified) {
|
|
20710
|
+
issues.push({
|
|
20711
|
+
path: claim.path,
|
|
20712
|
+
problem: "invalid-date",
|
|
20713
|
+
detail: `verifiedAt must be YYYY-MM-DD, got ${JSON.stringify(source.verifiedAt)}`
|
|
20714
|
+
});
|
|
20715
|
+
continue;
|
|
20716
|
+
}
|
|
20717
|
+
if (verified.getTime() > now2.getTime() + DAY_MS) {
|
|
20718
|
+
issues.push({
|
|
20719
|
+
path: claim.path,
|
|
20720
|
+
problem: "future-date",
|
|
20721
|
+
detail: `verifiedAt ${source.verifiedAt} is in the future`
|
|
20722
|
+
});
|
|
20723
|
+
continue;
|
|
20724
|
+
}
|
|
20725
|
+
if (!newest || verified.getTime() > newest.getTime()) newest = verified;
|
|
20726
|
+
}
|
|
20727
|
+
if (newest) {
|
|
20728
|
+
const ageDays = Math.floor((now2.getTime() - newest.getTime()) / DAY_MS);
|
|
20729
|
+
if (ageDays > maxAgeDays) {
|
|
20730
|
+
issues.push({
|
|
20731
|
+
path: claim.path,
|
|
20732
|
+
problem: "stale",
|
|
20733
|
+
detail: `last verified ${newest.toISOString().slice(0, 10)} (${ageDays} days ago, limit ${maxAgeDays}): re-read the vendor page and update verifiedAt or the claim`
|
|
20734
|
+
});
|
|
20735
|
+
}
|
|
20736
|
+
}
|
|
20737
|
+
}
|
|
20738
|
+
return issues;
|
|
20739
|
+
}
|
|
20740
|
+
function latestVerification(claims) {
|
|
20741
|
+
let latest = null;
|
|
20742
|
+
for (const claim of claims) {
|
|
20743
|
+
for (const source of claim.sources ?? []) {
|
|
20744
|
+
if (parseVerifiedAt(source.verifiedAt) && (!latest || source.verifiedAt > latest)) {
|
|
20745
|
+
latest = source.verifiedAt;
|
|
20746
|
+
}
|
|
20747
|
+
}
|
|
20748
|
+
}
|
|
20749
|
+
return latest;
|
|
20750
|
+
}
|
|
20751
|
+
function oldestVerification(claims) {
|
|
20752
|
+
let oldest = null;
|
|
20753
|
+
for (const claim of claims) {
|
|
20754
|
+
for (const source of claim.sources ?? []) {
|
|
20755
|
+
if (parseVerifiedAt(source.verifiedAt) && (!oldest || source.verifiedAt < oldest)) {
|
|
20756
|
+
oldest = source.verifiedAt;
|
|
20757
|
+
}
|
|
20758
|
+
}
|
|
20759
|
+
}
|
|
20760
|
+
return oldest;
|
|
20761
|
+
}
|
|
20762
|
+
function formatFreshnessReport(issues) {
|
|
20763
|
+
if (issues.length === 0)
|
|
20764
|
+
return "Comparison sources: all claims sourced and fresh.";
|
|
20765
|
+
const lines = issues.map((i) => ` - [${i.problem}] ${i.path}: ${i.detail}`);
|
|
20766
|
+
return [
|
|
20767
|
+
`Comparison sources: ${issues.length} problem(s) found.`,
|
|
20768
|
+
...lines,
|
|
20769
|
+
"Re-check each claim against the vendor's current page, then update the claim and its verifiedAt."
|
|
20770
|
+
].join("\n");
|
|
20771
|
+
}
|
|
20772
|
+
|
|
20640
20773
|
exports.ANSWERS_PATH = ANSWERS_PATH;
|
|
20641
20774
|
exports.ANSWER_HEADING_SHIFT = ANSWER_HEADING_SHIFT;
|
|
20642
20775
|
exports.ANSWER_PATH_PREFIX = ANSWER_PATH_PREFIX;
|
|
@@ -20659,6 +20792,7 @@ exports.ConceptsPage = ConceptsPage;
|
|
|
20659
20792
|
exports.ContactPage = ContactPage;
|
|
20660
20793
|
exports.ContentRenderer = ContentRenderer;
|
|
20661
20794
|
exports.ContractsPage = ContractsPage;
|
|
20795
|
+
exports.DEFAULT_COMPARISON_MAX_AGE_DAYS = DEFAULT_COMPARISON_MAX_AGE_DAYS;
|
|
20662
20796
|
exports.DEFAULT_CONTACT_CATEGORIES = DEFAULT_CONTACT_CATEGORIES;
|
|
20663
20797
|
exports.DEFAULT_EXPLORE_EXAMPLE_PROMPTS = DEFAULT_EXPLORE_EXAMPLE_PROMPTS;
|
|
20664
20798
|
exports.DOMAINS_URL = DOMAINS_URL;
|
|
@@ -20726,6 +20860,7 @@ exports.answerSpeechText = answerSpeechText;
|
|
|
20726
20860
|
exports.answerStrings = answerStrings;
|
|
20727
20861
|
exports.availablePlanCurrencies = availablePlanCurrencies;
|
|
20728
20862
|
exports.buttonVariants = buttonVariants;
|
|
20863
|
+
exports.checkComparisonFreshness = checkComparisonFreshness;
|
|
20729
20864
|
exports.classifyExploreError = classifyExploreError;
|
|
20730
20865
|
exports.cleanPlanDisplayName = cleanPlanDisplayName;
|
|
20731
20866
|
exports.cn = cn;
|
|
@@ -20742,11 +20877,13 @@ exports.fetchPublicProduct = fetchPublicProduct;
|
|
|
20742
20877
|
exports.findLegalDocument = findLegalDocument;
|
|
20743
20878
|
exports.formatAnswerDate = formatAnswerDate;
|
|
20744
20879
|
exports.formatDownloads = formatDownloads;
|
|
20880
|
+
exports.formatFreshnessReport = formatFreshnessReport;
|
|
20745
20881
|
exports.formatLegalDate = formatLegalDate;
|
|
20746
20882
|
exports.formatPrice = formatPrice2;
|
|
20747
20883
|
exports.getAnswerBySlug = getAnswerBySlug;
|
|
20748
20884
|
exports.getConceptBySlug = getConceptBySlug;
|
|
20749
20885
|
exports.getRecaptchaV3Token = getRecaptchaV3Token;
|
|
20886
|
+
exports.hasCustomerSignOff = hasCustomerSignOff;
|
|
20750
20887
|
exports.inlineImageOrigins = inlineImageOrigins;
|
|
20751
20888
|
exports.isFooterlessPath = isFooterlessPath;
|
|
20752
20889
|
exports.isInlineImageAllowed = isInlineImageAllowed;
|
|
@@ -20755,11 +20892,14 @@ exports.isPublishedSourceUrl = isPublishedSourceUrl;
|
|
|
20755
20892
|
exports.isSameProductSlug = isSameProductSlug;
|
|
20756
20893
|
exports.isUuidLike = isUuidLike;
|
|
20757
20894
|
exports.isValidUrl = isValidUrl;
|
|
20895
|
+
exports.latestVerification = latestVerification;
|
|
20758
20896
|
exports.legacyProductSlug = legacyProductSlug;
|
|
20759
20897
|
exports.loadRecaptchaV3 = loadRecaptchaV3;
|
|
20760
20898
|
exports.looksLikeDeliverableEmail = looksLikeDeliverableEmail;
|
|
20761
20899
|
exports.normalizeProductSlug = normalizeProductSlug;
|
|
20762
20900
|
exports.normalizeRoutePath = normalizeRoutePath;
|
|
20901
|
+
exports.oldestVerification = oldestVerification;
|
|
20902
|
+
exports.parseVerifiedAt = parseVerifiedAt;
|
|
20763
20903
|
exports.planPriceIn = planPriceIn;
|
|
20764
20904
|
exports.productSlugVariants = productSlugVariants;
|
|
20765
20905
|
exports.productSlugsByOrigin = productSlugsByOrigin;
|