@burdenoff/website-sdk 2026.1021.2 → 2026.1021.4

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.
@@ -88,6 +88,12 @@ interface ContentPageListData {
88
88
  interface UseContentPageOptions {
89
89
  /** Whether to use template variable replacement (resolvedContentPage) */
90
90
  resolve?: boolean;
91
+ /**
92
+ * Set to `false` to skip the request entirely — e.g. when the page was
93
+ * already fetched at build time. The hook then settles immediately with
94
+ * `data: null`, `loading: false`. Defaults to `true`.
95
+ */
96
+ enabled?: boolean;
91
97
  }
92
98
  /**
93
99
  * Hook to fetch a single public content page by slug.
@@ -165,6 +171,107 @@ interface MarkdownProps {
165
171
  */
166
172
  declare function Markdown({ children, className, inline, components, rehypePlugins, }: MarkdownProps): ReactNode;
167
173
 
174
+ /** The legal documents every product website is expected to publish. */
175
+ type LegalDocumentKey = "privacy" | "terms" | "cookies" | "refunds" | "aup" | "dpa" | "ai-terms" | "subprocessors";
176
+ interface LegalDocument {
177
+ key: LegalDocumentKey;
178
+ /** CMS slug (`ProductContentPage.slug`), shared by every product. */
179
+ slug: string;
180
+ /** Title used when the CMS row has none, or cannot be loaded. */
181
+ title: string;
182
+ /** Meta description used when the CMS row has no `seoDescription`. */
183
+ description: string;
184
+ }
185
+ /**
186
+ * Canonical CMS slugs for the legal set. Website routes may differ per site
187
+ * (`/privacy` or `/legal/privacy`); the slug never does.
188
+ */
189
+ declare const LEGAL_DOCUMENTS: Readonly<Record<LegalDocumentKey, LegalDocument>>;
190
+ /** Every legal document key, in display order. */
191
+ declare const LEGAL_DOCUMENT_KEYS: readonly LegalDocumentKey[];
192
+ /** Look up a legal document by its key or its CMS slug. */
193
+ declare function findLegalDocument(keyOrSlug: string): LegalDocument | null;
194
+ /** A legal page as fetched from the CMS (and as stored in build-time JSON). */
195
+ interface LegalContentEntry {
196
+ title: string;
197
+ slug: string;
198
+ content: string;
199
+ contentFormat: string;
200
+ seoTitle?: string | null;
201
+ seoDescription?: string | null;
202
+ seoKeywords?: string | null;
203
+ lastUpdated?: string | null;
204
+ }
205
+ /**
206
+ * Fetch one legal page for a product. `productId` may be the product UUID or
207
+ * its slug. Resolves to `null` when neither the product nor the global
208
+ * template has the page; rejects on GraphQL or transport errors, so a build
209
+ * script can keep its previous cache instead of publishing an empty page.
210
+ */
211
+ declare function fetchLegalContentPage(client: WebSDKClient, slug: string, productId: string): Promise<LegalContentEntry | null>;
212
+ interface FetchLegalContentOptions {
213
+ /** Product UUID or slug. */
214
+ productId: string;
215
+ /** Documents to fetch. Defaults to the full legal set. */
216
+ documents?: readonly LegalDocumentKey[];
217
+ }
218
+ /**
219
+ * Fetch a product's legal pages, keyed by CMS slug. Pages the CMS does not
220
+ * have are `null`. Rejects if any request fails, so callers never mistake an
221
+ * outage for a missing page.
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * // scripts/fetch-legal-content.ts (build time)
226
+ * const client = new WebSDKClient({
227
+ * gatewayUrl: 'https://graphql.burdenoff.com/global/graphql',
228
+ * productId: 'vibecontrols',
229
+ * })
230
+ * const pages = await fetchLegalContent(client, { productId: 'vibecontrols' })
231
+ * writeFileSync('src/data/legal-content.json', JSON.stringify(pages, null, 2))
232
+ * ```
233
+ */
234
+ declare function fetchLegalContent(client: WebSDKClient, options: FetchLegalContentOptions): Promise<Record<string, LegalContentEntry | null>>;
235
+ /**
236
+ * Split a leading `# Heading` off markdown content. The CMS documents start
237
+ * with their own H1; the page renders the title itself, so keeping both would
238
+ * put two H1s on the page.
239
+ */
240
+ declare function splitLeadingHeading(content: string, format: string): {
241
+ heading: string | null;
242
+ body: string;
243
+ };
244
+ /**
245
+ * Format an ISO date as "October 21, 2026". UTC, so prerendered HTML and the
246
+ * hydrated page agree whatever the visitor's time zone. Returns `null` for a
247
+ * missing or unparsable date.
248
+ */
249
+ declare function formatLegalDate(iso: string | null | undefined): string | null;
250
+ interface LegalPageProps {
251
+ /** Which legal document to show. Use `slug` instead for a non-standard page. */
252
+ document?: LegalDocumentKey;
253
+ /** CMS slug, for a page outside {@link LEGAL_DOCUMENTS}. Ignored when `document` is set. */
254
+ slug?: string;
255
+ /**
256
+ * Content fetched at build time (see {@link fetchLegalContent}). When this
257
+ * prop is present the page makes no request; pass `null` for a page the
258
+ * build found missing.
259
+ */
260
+ initialContent?: LegalContentEntry | null;
261
+ /** Overrides the registry title used when the CMS row has none. */
262
+ fallbackTitle?: string;
263
+ /** Overrides the registry description used when the CMS row has none. */
264
+ fallbackDescription?: string;
265
+ /** Product/brand name appended to the browser title. */
266
+ productName?: string;
267
+ /** Canonical URL of this page. */
268
+ canonicalUrl?: string;
269
+ /** Address shown when the document cannot be loaded. */
270
+ contactEmail?: string;
271
+ className?: string;
272
+ }
273
+ declare function LegalPage(props: LegalPageProps): react.JSX.Element;
274
+
168
275
  interface PublicInterviewRoundConfig {
169
276
  roundNumber: number;
170
277
  name: string;
@@ -427,4 +534,4 @@ interface ContractsPageProps {
427
534
  }
428
535
  declare function ContractsPage(props: ContractsPageProps): react.JSX.Element;
429
536
 
430
- export { fetchPublicJobPosting as A, BlogListPage as B, type CareersPostingOption as C, fetchPublicJobPostings as D, fetchPublicJobPostingsForSubmission as E, useContentPage as F, useContentPages as G, usePublicJobPosting as H, usePublicJobPostings as I, usePublicJobPostingsForSubmission as J, type UseContentPageOptions as K, Markdown as M, type PressKitAsset as P, type UsePublicJobPostingOptions as U, type BlogListPageProps as a, 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 MarkdownProps as t, type PressKitAssetCategory as u, PressKitPage as v, type PressKitPageProps as w, type PublicInterviewRoundConfig as x, type PublicJobPosting as y, type UsePublicJobPostingsOptions as z };
537
+ export { type PressKitAssetCategory as A, BlogListPage as B, type CareersPostingOption as C, PressKitPage as D, type PressKitPageProps as E, type FetchLegalContentOptions as F, type PublicInterviewRoundConfig as G, type PublicJobPosting as H, type UsePublicJobPostingsOptions as I, fetchLegalContent as J, fetchLegalContentPage as K, LEGAL_DOCUMENTS as L, Markdown as M, fetchPublicJobPosting as N, fetchPublicJobPostings as O, type PressKitAsset as P, fetchPublicJobPostingsForSubmission as Q, findLegalDocument as R, formatLegalDate as S, splitLeadingHeading as T, type UsePublicJobPostingOptions as U, useContentPage as V, useContentPages as W, usePublicJobPosting as X, usePublicJobPostings as Y, usePublicJobPostingsForSubmission as Z, type UseContentPageOptions as _, type BlogListPageProps as a, 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, LEGAL_DOCUMENT_KEYS as t, type LegalContentEntry as u, type LegalDocument as v, type LegalDocumentKey as w, LegalPage as x, type LegalPageProps as y, type MarkdownProps as z };
@@ -88,6 +88,12 @@ interface ContentPageListData {
88
88
  interface UseContentPageOptions {
89
89
  /** Whether to use template variable replacement (resolvedContentPage) */
90
90
  resolve?: boolean;
91
+ /**
92
+ * Set to `false` to skip the request entirely — e.g. when the page was
93
+ * already fetched at build time. The hook then settles immediately with
94
+ * `data: null`, `loading: false`. Defaults to `true`.
95
+ */
96
+ enabled?: boolean;
91
97
  }
92
98
  /**
93
99
  * Hook to fetch a single public content page by slug.
@@ -165,6 +171,107 @@ interface MarkdownProps {
165
171
  */
166
172
  declare function Markdown({ children, className, inline, components, rehypePlugins, }: MarkdownProps): ReactNode;
167
173
 
174
+ /** The legal documents every product website is expected to publish. */
175
+ type LegalDocumentKey = "privacy" | "terms" | "cookies" | "refunds" | "aup" | "dpa" | "ai-terms" | "subprocessors";
176
+ interface LegalDocument {
177
+ key: LegalDocumentKey;
178
+ /** CMS slug (`ProductContentPage.slug`), shared by every product. */
179
+ slug: string;
180
+ /** Title used when the CMS row has none, or cannot be loaded. */
181
+ title: string;
182
+ /** Meta description used when the CMS row has no `seoDescription`. */
183
+ description: string;
184
+ }
185
+ /**
186
+ * Canonical CMS slugs for the legal set. Website routes may differ per site
187
+ * (`/privacy` or `/legal/privacy`); the slug never does.
188
+ */
189
+ declare const LEGAL_DOCUMENTS: Readonly<Record<LegalDocumentKey, LegalDocument>>;
190
+ /** Every legal document key, in display order. */
191
+ declare const LEGAL_DOCUMENT_KEYS: readonly LegalDocumentKey[];
192
+ /** Look up a legal document by its key or its CMS slug. */
193
+ declare function findLegalDocument(keyOrSlug: string): LegalDocument | null;
194
+ /** A legal page as fetched from the CMS (and as stored in build-time JSON). */
195
+ interface LegalContentEntry {
196
+ title: string;
197
+ slug: string;
198
+ content: string;
199
+ contentFormat: string;
200
+ seoTitle?: string | null;
201
+ seoDescription?: string | null;
202
+ seoKeywords?: string | null;
203
+ lastUpdated?: string | null;
204
+ }
205
+ /**
206
+ * Fetch one legal page for a product. `productId` may be the product UUID or
207
+ * its slug. Resolves to `null` when neither the product nor the global
208
+ * template has the page; rejects on GraphQL or transport errors, so a build
209
+ * script can keep its previous cache instead of publishing an empty page.
210
+ */
211
+ declare function fetchLegalContentPage(client: WebSDKClient, slug: string, productId: string): Promise<LegalContentEntry | null>;
212
+ interface FetchLegalContentOptions {
213
+ /** Product UUID or slug. */
214
+ productId: string;
215
+ /** Documents to fetch. Defaults to the full legal set. */
216
+ documents?: readonly LegalDocumentKey[];
217
+ }
218
+ /**
219
+ * Fetch a product's legal pages, keyed by CMS slug. Pages the CMS does not
220
+ * have are `null`. Rejects if any request fails, so callers never mistake an
221
+ * outage for a missing page.
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * // scripts/fetch-legal-content.ts (build time)
226
+ * const client = new WebSDKClient({
227
+ * gatewayUrl: 'https://graphql.burdenoff.com/global/graphql',
228
+ * productId: 'vibecontrols',
229
+ * })
230
+ * const pages = await fetchLegalContent(client, { productId: 'vibecontrols' })
231
+ * writeFileSync('src/data/legal-content.json', JSON.stringify(pages, null, 2))
232
+ * ```
233
+ */
234
+ declare function fetchLegalContent(client: WebSDKClient, options: FetchLegalContentOptions): Promise<Record<string, LegalContentEntry | null>>;
235
+ /**
236
+ * Split a leading `# Heading` off markdown content. The CMS documents start
237
+ * with their own H1; the page renders the title itself, so keeping both would
238
+ * put two H1s on the page.
239
+ */
240
+ declare function splitLeadingHeading(content: string, format: string): {
241
+ heading: string | null;
242
+ body: string;
243
+ };
244
+ /**
245
+ * Format an ISO date as "October 21, 2026". UTC, so prerendered HTML and the
246
+ * hydrated page agree whatever the visitor's time zone. Returns `null` for a
247
+ * missing or unparsable date.
248
+ */
249
+ declare function formatLegalDate(iso: string | null | undefined): string | null;
250
+ interface LegalPageProps {
251
+ /** Which legal document to show. Use `slug` instead for a non-standard page. */
252
+ document?: LegalDocumentKey;
253
+ /** CMS slug, for a page outside {@link LEGAL_DOCUMENTS}. Ignored when `document` is set. */
254
+ slug?: string;
255
+ /**
256
+ * Content fetched at build time (see {@link fetchLegalContent}). When this
257
+ * prop is present the page makes no request; pass `null` for a page the
258
+ * build found missing.
259
+ */
260
+ initialContent?: LegalContentEntry | null;
261
+ /** Overrides the registry title used when the CMS row has none. */
262
+ fallbackTitle?: string;
263
+ /** Overrides the registry description used when the CMS row has none. */
264
+ fallbackDescription?: string;
265
+ /** Product/brand name appended to the browser title. */
266
+ productName?: string;
267
+ /** Canonical URL of this page. */
268
+ canonicalUrl?: string;
269
+ /** Address shown when the document cannot be loaded. */
270
+ contactEmail?: string;
271
+ className?: string;
272
+ }
273
+ declare function LegalPage(props: LegalPageProps): react.JSX.Element;
274
+
168
275
  interface PublicInterviewRoundConfig {
169
276
  roundNumber: number;
170
277
  name: string;
@@ -427,4 +534,4 @@ interface ContractsPageProps {
427
534
  }
428
535
  declare function ContractsPage(props: ContractsPageProps): react.JSX.Element;
429
536
 
430
- export { fetchPublicJobPosting as A, BlogListPage as B, type CareersPostingOption as C, fetchPublicJobPostings as D, fetchPublicJobPostingsForSubmission as E, useContentPage as F, useContentPages as G, usePublicJobPosting as H, usePublicJobPostings as I, usePublicJobPostingsForSubmission as J, type UseContentPageOptions as K, Markdown as M, type PressKitAsset as P, type UsePublicJobPostingOptions as U, type BlogListPageProps as a, 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 MarkdownProps as t, type PressKitAssetCategory as u, PressKitPage as v, type PressKitPageProps as w, type PublicInterviewRoundConfig as x, type PublicJobPosting as y, type UsePublicJobPostingsOptions as z };
537
+ export { type PressKitAssetCategory as A, BlogListPage as B, type CareersPostingOption as C, PressKitPage as D, type PressKitPageProps as E, type FetchLegalContentOptions as F, type PublicInterviewRoundConfig as G, type PublicJobPosting as H, type UsePublicJobPostingsOptions as I, fetchLegalContent as J, fetchLegalContentPage as K, LEGAL_DOCUMENTS as L, Markdown as M, fetchPublicJobPosting as N, fetchPublicJobPostings as O, type PressKitAsset as P, fetchPublicJobPostingsForSubmission as Q, findLegalDocument as R, formatLegalDate as S, splitLeadingHeading as T, type UsePublicJobPostingOptions as U, useContentPage as V, useContentPages as W, usePublicJobPosting as X, usePublicJobPostings as Y, usePublicJobPostingsForSubmission as Z, type UseContentPageOptions as _, type BlogListPageProps as a, 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, LEGAL_DOCUMENT_KEYS as t, type LegalContentEntry as u, type LegalDocument as v, type LegalDocumentKey as w, LegalPage as x, type LegalPageProps as y, type MarkdownProps 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-gm3pBCJT.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, M as Markdown, t as MarkdownProps, P as PressKitAsset, u as PressKitAssetCategory, v as PressKitPage, w as PressKitPageProps, x as PublicInterviewRoundConfig, y as PublicJobPosting, U as UsePublicJobPostingOptions, z as UsePublicJobPostingsOptions, A as fetchPublicJobPosting, D as fetchPublicJobPostings, E as fetchPublicJobPostingsForSubmission, F as useContentPage, G as useContentPages, H as usePublicJobPosting, I as usePublicJobPostings, J as usePublicJobPostingsForSubmission } from './index-gm3pBCJT.mjs';
5
+ import { C as CareersPostingOption } from './index-Ctws7kGs.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, F as FetchLegalContentOptions, L as LEGAL_DOCUMENTS, t as LEGAL_DOCUMENT_KEYS, u as LegalContentEntry, v as LegalDocument, w as LegalDocumentKey, x as LegalPage, y as LegalPageProps, M as Markdown, z as MarkdownProps, P as PressKitAsset, A as PressKitAssetCategory, D as PressKitPage, E as PressKitPageProps, G as PublicInterviewRoundConfig, H as PublicJobPosting, U as UsePublicJobPostingOptions, I as UsePublicJobPostingsOptions, J as fetchLegalContent, K as fetchLegalContentPage, N as fetchPublicJobPosting, O as fetchPublicJobPostings, Q as fetchPublicJobPostingsForSubmission, R as findLegalDocument, S as formatLegalDate, T as splitLeadingHeading, V as useContentPage, W as useContentPages, X as usePublicJobPosting, Y as usePublicJobPostings, Z as usePublicJobPostingsForSubmission } from './index-Ctws7kGs.mjs';
7
7
  import * as react from 'react';
8
8
  import { ComponentType, ReactNode } from 'react';
9
9
  export { NewsletterForm, NewsletterFormProps, NewsletterPage, NewsletterPageProps } from './components/newsletter.mjs';
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-ySbOLSD5.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, M as Markdown, t as MarkdownProps, P as PressKitAsset, u as PressKitAssetCategory, v as PressKitPage, w as PressKitPageProps, x as PublicInterviewRoundConfig, y as PublicJobPosting, U as UsePublicJobPostingOptions, z as UsePublicJobPostingsOptions, A as fetchPublicJobPosting, D as fetchPublicJobPostings, E as fetchPublicJobPostingsForSubmission, F as useContentPage, G as useContentPages, H as usePublicJobPosting, I as usePublicJobPostings, J as usePublicJobPostingsForSubmission } from './index-ySbOLSD5.js';
5
+ import { C as CareersPostingOption } from './index-CB0Uw1xD.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, F as FetchLegalContentOptions, L as LEGAL_DOCUMENTS, t as LEGAL_DOCUMENT_KEYS, u as LegalContentEntry, v as LegalDocument, w as LegalDocumentKey, x as LegalPage, y as LegalPageProps, M as Markdown, z as MarkdownProps, P as PressKitAsset, A as PressKitAssetCategory, D as PressKitPage, E as PressKitPageProps, G as PublicInterviewRoundConfig, H as PublicJobPosting, U as UsePublicJobPostingOptions, I as UsePublicJobPostingsOptions, J as fetchLegalContent, K as fetchLegalContentPage, N as fetchPublicJobPosting, O as fetchPublicJobPostings, Q as fetchPublicJobPostingsForSubmission, R as findLegalDocument, S as formatLegalDate, T as splitLeadingHeading, V as useContentPage, W as useContentPages, X as usePublicJobPosting, Y as usePublicJobPostings, Z as usePublicJobPostingsForSubmission } from './index-CB0Uw1xD.js';
7
7
  import * as react from 'react';
8
8
  import { ComponentType, ReactNode } from 'react';
9
9
  export { NewsletterForm, NewsletterFormProps, NewsletterPage, NewsletterPageProps } from './components/newsletter.js';
package/dist/index.js CHANGED
@@ -1317,10 +1317,15 @@ function useContentPage(slug, options) {
1317
1317
  hasProvider,
1318
1318
  productLoading
1319
1319
  );
1320
+ const enabled = options?.enabled !== false;
1320
1321
  const [data, setData] = React.useState(null);
1321
- const [loading, setLoading] = React.useState(true);
1322
+ const [loading, setLoading] = React.useState(enabled);
1322
1323
  const [error, setError] = React.useState(null);
1323
1324
  const fetchPage = React.useCallback(async () => {
1325
+ if (!enabled) {
1326
+ setLoading(false);
1327
+ return;
1328
+ }
1324
1329
  if (gate === "wait") return;
1325
1330
  if (gate === "unavailable") {
1326
1331
  setData(null);
@@ -1354,7 +1359,15 @@ function useContentPage(slug, options) {
1354
1359
  } finally {
1355
1360
  setLoading(false);
1356
1361
  }
1357
- }, [client, gate, productError, resolvedProductId, slug, options?.resolve]);
1362
+ }, [
1363
+ client,
1364
+ enabled,
1365
+ gate,
1366
+ productError,
1367
+ resolvedProductId,
1368
+ slug,
1369
+ options?.resolve
1370
+ ]);
1358
1371
  React.useEffect(() => {
1359
1372
  fetchPage();
1360
1373
  }, [fetchPage]);
@@ -3683,7 +3696,7 @@ function PricingSection(props) {
3683
3696
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: subtitle })
3684
3697
  ] }),
3685
3698
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-2", children: [
3686
- price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
3699
+ price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-baseline justify-center gap-1", children: [
3687
3700
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-extrabold", children: price }),
3688
3701
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: period })
3689
3702
  ] }),
@@ -3751,7 +3764,7 @@ function PricingSection(props) {
3751
3764
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl sm:text-2xl font-bold mb-2", children: plan.name }),
3752
3765
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm", children: plan.description })
3753
3766
  ] }),
3754
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: plan.price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-center gap-1", children: [
3767
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-8", children: plan.price === "Custom" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl sm:text-4xl font-bold", children: "Custom" }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex items-baseline justify-center gap-1", children: [
3755
3768
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl font-bold", children: plan.price }),
3756
3769
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-lg", children: plan.interval })
3757
3770
  ] }) }),
@@ -14787,6 +14800,205 @@ function RybbitAnalytics({
14787
14800
  }, [siteId, scriptUrl, enabled]);
14788
14801
  return null;
14789
14802
  }
14803
+ var LEGAL_DOCUMENTS = {
14804
+ privacy: {
14805
+ key: "privacy",
14806
+ slug: "privacy-policy",
14807
+ title: "Privacy Policy",
14808
+ description: "How we collect, use, share and protect your personal information."
14809
+ },
14810
+ terms: {
14811
+ key: "terms",
14812
+ slug: "terms-of-service",
14813
+ title: "Terms of Service",
14814
+ description: "The terms that govern your use of the service."
14815
+ },
14816
+ cookies: {
14817
+ key: "cookies",
14818
+ slug: "cookie-policy",
14819
+ title: "Cookie Policy",
14820
+ description: "The cookies and similar technologies we use, and why."
14821
+ },
14822
+ refunds: {
14823
+ key: "refunds",
14824
+ slug: "refund-policy",
14825
+ title: "Refund Policy",
14826
+ description: "How cancellations and refunds work for paid plans."
14827
+ },
14828
+ aup: {
14829
+ key: "aup",
14830
+ slug: "acceptable-use-policy",
14831
+ title: "Acceptable Use Policy",
14832
+ description: "What you may and may not do with the service."
14833
+ },
14834
+ dpa: {
14835
+ key: "dpa",
14836
+ slug: "data-processing-addendum",
14837
+ title: "Data Processing Addendum",
14838
+ description: "Our obligations as a processor of personal data on your behalf."
14839
+ },
14840
+ "ai-terms": {
14841
+ key: "ai-terms",
14842
+ slug: "ai-product-terms",
14843
+ title: "AI Product Terms",
14844
+ description: "The terms that apply to AI features, including how your data is and is not used."
14845
+ },
14846
+ subprocessors: {
14847
+ key: "subprocessors",
14848
+ slug: "subprocessors",
14849
+ title: "Subprocessors",
14850
+ description: "The third parties that help us host, secure and support the service."
14851
+ }
14852
+ };
14853
+ var LEGAL_DOCUMENT_KEYS = Object.keys(
14854
+ LEGAL_DOCUMENTS
14855
+ );
14856
+ function findLegalDocument(keyOrSlug) {
14857
+ if (Object.prototype.hasOwnProperty.call(LEGAL_DOCUMENTS, keyOrSlug)) {
14858
+ return LEGAL_DOCUMENTS[keyOrSlug];
14859
+ }
14860
+ return Object.values(LEGAL_DOCUMENTS).find((doc) => doc.slug === keyOrSlug) ?? null;
14861
+ }
14862
+ var LEGAL_CONTENT_QUERY = `
14863
+ query PublicLegalContentPage($slug: String!, $productId: String!) {
14864
+ publicResolvedContentPage(slug: $slug, productId: $productId) {
14865
+ title
14866
+ slug
14867
+ content
14868
+ contentFormat
14869
+ seoTitle
14870
+ seoDescription
14871
+ seoKeywords
14872
+ lastUpdated
14873
+ }
14874
+ }
14875
+ `;
14876
+ async function fetchLegalContentPage(client, slug, productId) {
14877
+ const result = await client.query(LEGAL_CONTENT_QUERY, { slug, productId }, { cache: false });
14878
+ if (result.errors?.length) {
14879
+ throw new Error(result.errors[0]?.message ?? "Unknown error");
14880
+ }
14881
+ return result.data?.publicResolvedContentPage ?? null;
14882
+ }
14883
+ async function fetchLegalContent(client, options) {
14884
+ const docs = (options.documents ?? LEGAL_DOCUMENT_KEYS).map(
14885
+ (key) => LEGAL_DOCUMENTS[key]
14886
+ );
14887
+ const entries = await Promise.all(
14888
+ docs.map(
14889
+ async (doc) => [
14890
+ doc.slug,
14891
+ await fetchLegalContentPage(client, doc.slug, options.productId)
14892
+ ]
14893
+ )
14894
+ );
14895
+ return Object.fromEntries(entries);
14896
+ }
14897
+ function splitLeadingHeading(content, format) {
14898
+ if (format.toLowerCase() !== "markdown")
14899
+ return { heading: null, body: content };
14900
+ const match = /^\s*#[ \t]+([^\n]+?)[ \t#]*(?:\n|$)/.exec(content);
14901
+ if (!match?.[1]) return { heading: null, body: content };
14902
+ return {
14903
+ heading: match[1].trim(),
14904
+ body: content.slice(match[0].length).replace(/^\s+/, "")
14905
+ };
14906
+ }
14907
+ function formatLegalDate(iso) {
14908
+ if (!iso) return null;
14909
+ const date = new Date(iso);
14910
+ if (Number.isNaN(date.getTime())) return null;
14911
+ return date.toLocaleDateString("en-US", {
14912
+ year: "numeric",
14913
+ month: "long",
14914
+ day: "numeric",
14915
+ timeZone: "UTC"
14916
+ });
14917
+ }
14918
+ function LegalPage(props) {
14919
+ const {
14920
+ document: document2,
14921
+ slug: slugProp,
14922
+ initialContent,
14923
+ fallbackTitle,
14924
+ fallbackDescription,
14925
+ productName,
14926
+ canonicalUrl,
14927
+ contactEmail,
14928
+ className = ""
14929
+ } = props;
14930
+ const registered = document2 ? LEGAL_DOCUMENTS[document2] : slugProp ? findLegalDocument(slugProp) : null;
14931
+ const slug = registered?.slug ?? slugProp ?? "";
14932
+ const prefetched = initialContent !== void 0;
14933
+ const { data: fetched, loading } = useContentPage(slug, {
14934
+ resolve: true,
14935
+ enabled: !prefetched && slug !== ""
14936
+ });
14937
+ const page = prefetched ? initialContent ?? null : fetched;
14938
+ const isLoading = !prefetched && slug !== "" && loading;
14939
+ const defaultTitle = fallbackTitle ?? registered?.title ?? "Legal";
14940
+ const defaultDescription = fallbackDescription ?? registered?.description ?? "";
14941
+ const { heading, body } = page ? splitLeadingHeading(page.content, page.contentFormat) : { heading: null, body: "" };
14942
+ const title = page?.title || heading || defaultTitle;
14943
+ const updated = formatLegalDate(page?.lastUpdated);
14944
+ const unavailable = !isLoading && !page;
14945
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `w-full ${className}`, children: [
14946
+ /* @__PURE__ */ jsxRuntime.jsx(ProseStyles, {}),
14947
+ /* @__PURE__ */ jsxRuntime.jsx(
14948
+ PageHead,
14949
+ {
14950
+ title: page?.seoTitle || title,
14951
+ description: page?.seoDescription?.trim() || defaultDescription,
14952
+ keywords: page?.seoKeywords ?? void 0,
14953
+ productName,
14954
+ url: canonicalUrl,
14955
+ robots: unavailable ? "noindex, follow" : void 0
14956
+ }
14957
+ ),
14958
+ /* @__PURE__ */ jsxRuntime.jsxs(
14959
+ "article",
14960
+ {
14961
+ className: "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16 sm:py-24",
14962
+ "aria-busy": isLoading,
14963
+ children: [
14964
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl sm:text-4xl font-bold tracking-tight text-foreground mb-3", children: title }),
14965
+ updated && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground mb-10", children: [
14966
+ "Last updated:",
14967
+ " ",
14968
+ /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: page?.lastUpdated ?? void 0, children: updated })
14969
+ ] }),
14970
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", "data-testid": "legal-page-loading", children: [
14971
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-3/4 rounded bg-muted animate-pulse" }),
14972
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-full rounded bg-muted animate-pulse" }),
14973
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-5/6 rounded bg-muted animate-pulse" })
14974
+ ] }) : page ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "boff-prose max-w-none", children: /* @__PURE__ */ jsxRuntime.jsx(ContentRenderer, { content: body, format: page.contentFormat }) }) : /* @__PURE__ */ jsxRuntime.jsxs(
14975
+ "div",
14976
+ {
14977
+ role: "status",
14978
+ className: "rounded-xl border border-border bg-muted/30 p-6",
14979
+ children: [
14980
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-foreground", children: "This document is not available right now." }),
14981
+ contactEmail && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-sm text-muted-foreground", children: [
14982
+ "For a copy, contact",
14983
+ " ",
14984
+ /* @__PURE__ */ jsxRuntime.jsx(
14985
+ "a",
14986
+ {
14987
+ className: "text-primary underline",
14988
+ href: `mailto:${contactEmail}`,
14989
+ children: contactEmail
14990
+ }
14991
+ ),
14992
+ "."
14993
+ ] })
14994
+ ]
14995
+ }
14996
+ )
14997
+ ]
14998
+ }
14999
+ )
15000
+ ] });
15001
+ }
14790
15002
  var PUBLIC_JOB_POSTING_FIELDS = (
14791
15003
  /* GraphQL */
14792
15004
  `
@@ -19385,8 +19597,11 @@ exports.ExplorePage = ExplorePage;
19385
19597
  exports.FALLBACK_EXPLORE_LIMITS = FALLBACK_EXPLORE_LIMITS;
19386
19598
  exports.FOOTERLESS_ROUTES = FOOTERLESS_ROUTES;
19387
19599
  exports.Input = Input;
19600
+ exports.LEGAL_DOCUMENTS = LEGAL_DOCUMENTS;
19601
+ exports.LEGAL_DOCUMENT_KEYS = LEGAL_DOCUMENT_KEYS;
19388
19602
  exports.Label = Label;
19389
19603
  exports.LaunchCountdown = LaunchCountdown;
19604
+ exports.LegalPage = LegalPage;
19390
19605
  exports.Markdown = Markdown;
19391
19606
  exports.NewsletterForm = NewsletterForm;
19392
19607
  exports.NewsletterPage = NewsletterPage;
@@ -19434,12 +19649,16 @@ exports.conceptPath = conceptPath;
19434
19649
  exports.detectElectronPlatform = detectElectronPlatform;
19435
19650
  exports.emitProductSlug = emitProductSlug;
19436
19651
  exports.exploreQuestionHref = exploreQuestionHref;
19652
+ exports.fetchLegalContent = fetchLegalContent;
19653
+ exports.fetchLegalContentPage = fetchLegalContentPage;
19437
19654
  exports.fetchPublicJobPosting = fetchPublicJobPosting;
19438
19655
  exports.fetchPublicJobPostings = fetchPublicJobPostings;
19439
19656
  exports.fetchPublicJobPostingsForSubmission = fetchPublicJobPostingsForSubmission;
19440
19657
  exports.fetchPublicProduct = fetchPublicProduct;
19658
+ exports.findLegalDocument = findLegalDocument;
19441
19659
  exports.formatAnswerDate = formatAnswerDate;
19442
19660
  exports.formatDownloads = formatDownloads;
19661
+ exports.formatLegalDate = formatLegalDate;
19443
19662
  exports.formatPrice = formatPrice2;
19444
19663
  exports.getAnswerBySlug = getAnswerBySlug;
19445
19664
  exports.getConceptBySlug = getConceptBySlug;
@@ -19463,6 +19682,7 @@ exports.productSlugsByOrigin = productSlugsByOrigin;
19463
19682
  exports.resolvePricingProductKey = resolvePricingProductKey;
19464
19683
  exports.shouldRenderSiteFooter = shouldRenderSiteFooter;
19465
19684
  exports.speechLocaleOf = speechLocaleOf;
19685
+ exports.splitLeadingHeading = splitLeadingHeading;
19466
19686
  exports.stopSpeaking = stopSpeaking;
19467
19687
  exports.useContentPage = useContentPage;
19468
19688
  exports.useContentPages = useContentPages;