@sonordev/site-kit 8.6.0 → 8.8.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 (161) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/agent-manifest.json +1 -1
  3. package/dist/{AnalyticsProvider-GVJYL6XY.js → AnalyticsProvider-7SBJ27SI.js} +6 -6
  4. package/dist/{ArticleViewTracker-AOJXEBYM.js → ArticleViewTracker-PV2RD3P6.js} +5 -5
  5. package/dist/{BlocksPopup-AJ3CKVQG.js → BlocksPopup-NEEFV2L3.js} +9 -8
  6. package/dist/{ChatWidget-EYBVB5SZ.js → ChatWidget-V46UBDOM.js} +5 -5
  7. package/dist/{FileField-UEQYIFRO.js → FileField-MJ3RCNFJ.js} +3 -3
  8. package/dist/{FormSpotlight-H73ZZ66T.js → FormSpotlight-NQPQFZS7.js} +1 -1
  9. package/dist/{FormStage-2PLWWYBW.js → FormStage-4T5PJR5K.js} +1 -1
  10. package/dist/{ManagedForm-ZQOFXEWS.js → ManagedForm-DDDVMKLJ.js} +6 -6
  11. package/dist/{ManagedNewsletterForm-5TE47YZD.js → ManagedNewsletterForm-TUYGN6O3.js} +9 -9
  12. package/dist/{SignalCore-FEFNMNYN.js → SignalCore-OW3YIH6M.js} +5 -5
  13. package/dist/SiteChat-XMKHBSEK.js +6 -0
  14. package/dist/SiteDesignReporter-BRH7ORQS.js +13 -0
  15. package/dist/{SitePopups-C44YTQ3Q.js → SitePopups-VU2C3Z6B.js} +6 -6
  16. package/dist/SitemapSync-C4H3HF3L.js +9 -0
  17. package/dist/_client/booking-widget.js +5 -5
  18. package/dist/admin-auth/index.d.ts +2 -2
  19. package/dist/admin-auth/session.d.ts +2 -2
  20. package/dist/affiliates/index.js +3 -3
  21. package/dist/analytics/content-text.d.ts +17 -0
  22. package/dist/analytics/index.js +6 -6
  23. package/dist/{article-styles-VASFBYA3.js → article-styles-D7VCDXMG.js} +2 -1
  24. package/dist/article-tables-W45BE353.js +3 -0
  25. package/dist/articles/index.js +7 -20
  26. package/dist/articles/server-ui.js +29 -55
  27. package/dist/articles/server.js +6 -5
  28. package/dist/chat/index.js +8 -8
  29. package/dist/{chunk-3RDJKIYO.js → chunk-2WDWQI7K.js} +1 -1
  30. package/dist/{chunk-KTMHRXDT.js → chunk-2WM4TNDF.js} +1 -1
  31. package/dist/chunk-3MJNBOUO.js +8 -0
  32. package/dist/{chunk-BF7TZYC3.js → chunk-52KO4LKD.js} +1 -1
  33. package/dist/chunk-64XODP7M.js +7 -0
  34. package/dist/{chunk-DSD3XZQQ.js → chunk-6BFQI3ZE.js} +3 -3
  35. package/dist/{chunk-QNOJSSQT.js → chunk-6DXQHNZK.js} +1 -1
  36. package/dist/{chunk-5D7ZISUP.js → chunk-7DYMM6SS.js} +11 -6
  37. package/dist/{chunk-QQT2T6JQ.js → chunk-7HW4TZB5.js} +1 -1
  38. package/dist/{chunk-E4PTMORJ.js → chunk-AL5UCWLI.js} +23 -8
  39. package/dist/chunk-AYQEIMGS.js +390 -0
  40. package/dist/{chunk-L6QTERSH.js → chunk-DYROJGI2.js} +1 -1
  41. package/dist/{chunk-XA7Q2VQA.js → chunk-EK7P62DT.js} +1 -1
  42. package/dist/{chunk-BUE3Z32O.js → chunk-F6WLHVMQ.js} +1 -1
  43. package/dist/{chunk-IYCSDFPN.js → chunk-FLOJUMFR.js} +2 -2
  44. package/dist/{chunk-7R2STOVD.js → chunk-G7BRCSV6.js} +3 -3
  45. package/dist/{chunk-NI6ZTP3D.js → chunk-GQN4BGK6.js} +1 -1
  46. package/dist/{chunk-GFY3E7EQ.js → chunk-GXWM5KNS.js} +15 -7
  47. package/dist/{chunk-U34O6I7W.js → chunk-HN7GVGWC.js} +3 -3
  48. package/dist/{chunk-6KU7STM3.js → chunk-HY7ZEU2C.js} +3 -1
  49. package/dist/chunk-IBHBEUZV.js +27 -0
  50. package/dist/{chunk-ZZSAWXWD.js → chunk-IDC5CVBR.js} +1 -1
  51. package/dist/{chunk-HMTPRBYJ.js → chunk-IWNJCRXW.js} +1 -1
  52. package/dist/chunk-JESLPEST.js +14 -0
  53. package/dist/{chunk-BC7JMY4Z.js → chunk-JG53YJLM.js} +58 -1
  54. package/dist/{chunk-RI2N2EBI.js → chunk-JYITKUUB.js} +13 -5
  55. package/dist/{chunk-V67OD7RW.js → chunk-KG5IEV7I.js} +113 -93
  56. package/dist/chunk-KIYACQW3.js +1 -0
  57. package/dist/{chunk-JSW42BHL.js → chunk-M5JIHQ42.js} +1 -1
  58. package/dist/{chunk-YJH6WE76.js → chunk-NFGAXWCN.js} +9 -9
  59. package/dist/{chunk-Y35BWW2C.js → chunk-ODOV76VW.js} +1 -1
  60. package/dist/{chunk-VA6IJ55K.js → chunk-OPUY4RS6.js} +1 -1
  61. package/dist/{chunk-6NXFA7HE.js → chunk-OWZWRP7Y.js} +2 -2
  62. package/dist/{chunk-JJ4JS3LD.js → chunk-PAYCK7FC.js} +1 -1
  63. package/dist/{chunk-72F44HKL.js → chunk-PSS6M4CG.js} +1 -1
  64. package/dist/{chunk-O3Z5HHFP.js → chunk-SBPUFTMP.js} +2 -2
  65. package/dist/{chunk-OFSV5TZY.js → chunk-SHAUX2MQ.js} +1 -1
  66. package/dist/{chunk-OWO3RFEX.js → chunk-SLFYTELD.js} +1 -1
  67. package/dist/{chunk-CMUD37WV.js → chunk-SVHPILS3.js} +1 -1
  68. package/dist/{chunk-YNBRVVTX.js → chunk-THXELZTQ.js} +1 -1
  69. package/dist/{chunk-IGAIQ2B3.js → chunk-TP32MRXV.js} +2 -2
  70. package/dist/{chunk-NUXXFCIT.js → chunk-URLTBVRX.js} +4 -4
  71. package/dist/chunk-XAZC2TUD.js +1119 -0
  72. package/dist/{chunk-URJN75ZG.js → chunk-XIDWNXZN.js} +3 -3
  73. package/dist/chunk-XLAHRN4B.js +1 -0
  74. package/dist/chunk-XS2ORMUG.js +32 -0
  75. package/dist/{chunk-6AWIUO2Z.js → chunk-Y6SHFILE.js} +1 -1
  76. package/dist/{chunk-FTSEP7Q3.js → chunk-YEX5RAEU.js} +3 -3
  77. package/dist/{chunk-AJG74ZZV.js → chunk-YRQS7VAH.js} +1 -1
  78. package/dist/{chunk-DLM4FFNH.js → chunk-ZQSCMYZO.js} +1 -1
  79. package/dist/client/index.js +4 -4
  80. package/dist/contracts/campaign-normalize.d.ts +2 -0
  81. package/dist/contracts/entries.d.ts +1 -1
  82. package/dist/contracts/frame-ready.d.ts +23 -0
  83. package/dist/contracts/funnels.d.ts +576 -0
  84. package/dist/contracts/inbox.d.ts +42 -0
  85. package/dist/contracts/llms.d.ts +200 -0
  86. package/dist/contracts/portfolio.d.ts +361 -8
  87. package/dist/contracts/publication-branding.d.ts +10 -2
  88. package/dist/contracts/site-cache.d.ts +2 -0
  89. package/dist/fleet/index.js +6 -6
  90. package/dist/forms/index.js +9 -9
  91. package/dist/forms/server.js +2 -2
  92. package/dist/images/index.js +4 -4
  93. package/dist/index.js +1 -1
  94. package/dist/landing/contract.d.ts +2 -13
  95. package/dist/landing/contract.js +2 -0
  96. package/dist/landing/index.js +4 -2
  97. package/dist/landing/managed-server.d.ts +21 -0
  98. package/dist/landing/managed.d.ts +11 -0
  99. package/dist/landing/metadata.js +2 -0
  100. package/dist/landing/server.d.ts +5 -0
  101. package/dist/landing/server.js +20 -2
  102. package/dist/layout/client.js +9 -9
  103. package/dist/layout/index.js +10 -10
  104. package/dist/llms/contract.js +2 -1
  105. package/dist/llms/index.js +9 -7
  106. package/dist/llms/merge.d.ts +29 -0
  107. package/dist/maps/index.js +3 -3
  108. package/dist/mcp/sonor.js +9 -8
  109. package/dist/og/pages.d.ts +3 -3
  110. package/dist/portfolio/contract.js +705 -1
  111. package/dist/press-kit/articles/article-body.d.ts +12 -0
  112. package/dist/press-kit/articles/article-heading-ids.d.ts +7 -0
  113. package/dist/press-kit/articles/article-headings.d.ts +61 -0
  114. package/dist/press-kit/articles/html-tags.d.ts +46 -0
  115. package/dist/press-kit/articles/types.d.ts +2 -0
  116. package/dist/revalidate/index.js +3 -3
  117. package/dist/runtime/index.js +3 -3
  118. package/dist/seo/client.js +6 -6
  119. package/dist/seo/index.js +14 -13
  120. package/dist/seo/llms/contract.js +2 -1
  121. package/dist/seo/llms.js +9 -7
  122. package/dist/seo/meta/contract.js +2 -1
  123. package/dist/seo/register-sitemap-cli.js +1 -1
  124. package/dist/seo/server.js +5 -4
  125. package/dist/seo/sitemap.js +7 -5
  126. package/dist/seo-meta/contract.js +2 -1
  127. package/dist/server/index.js +3 -3
  128. package/dist/{server-api-EGOKC7Q3.js → server-api-XIKDWSNO.js} +2 -2
  129. package/dist/shared/build-entries.d.ts +3 -1
  130. package/dist/shared/frame.d.ts +1 -1
  131. package/dist/shared/markdown.d.ts +7 -6
  132. package/dist/shared/source-graph.d.ts +5 -5
  133. package/dist/shared/version.d.ts +1 -1
  134. package/dist/signal/index.js +2 -2
  135. package/dist/sitemap/index.js +7 -5
  136. package/dist/sitemap/shared.d.ts +1 -2
  137. package/dist/slots/index.js +2 -2
  138. package/dist/sync/index.js +5 -5
  139. package/dist/website/contract.js +2 -1
  140. package/dist/website/images.js +4 -4
  141. package/dist/website/index.js +10 -9
  142. package/dist/website/landing/server.js +20 -2
  143. package/dist/website/landing.js +4 -2
  144. package/dist/website/popups.js +9 -8
  145. package/dist/website/slots.js +2 -2
  146. package/dist/{writeLLMsTxt-KHVHNSDM.js → writeLLMsTxt-4HHUAOA6.js} +6 -4
  147. package/docs.json +5 -0
  148. package/package.json +1 -1
  149. package/src/analytics/README.md +1 -1
  150. package/src/landing/README.md +64 -0
  151. package/src/llms/README.md +52 -20
  152. package/dist/SiteChat-3MKYL2XM.js +0 -6
  153. package/dist/SiteDesignReporter-AJDIBNHI.js +0 -12
  154. package/dist/SitemapSync-UJA6JVPW.js +0 -9
  155. package/dist/article-tables-6XHVULPU.js +0 -2
  156. package/dist/chunk-EKBEOXTH.js +0 -114
  157. package/dist/chunk-NZ4RXN3G.js +0 -145
  158. package/dist/chunk-OSYCOC3K.js +0 -21
  159. /package/dist/{chunk-7ZWO5HDQ.js → chunk-BTHEQDN4.js} +0 -0
  160. /package/dist/{chunk-BQR4IPBW.js → chunk-ISRBSTB7.js} +0 -0
  161. /package/dist/{chunk-IHG36STL.js → chunk-XHYE24HU.js} +0 -0
@@ -1,21 +1,22 @@
1
- export { ArticleUnavailableError, FEED_DEFAULT_ITEMS, NEWS_SITEMAP_MAX_URLS, NEWS_SITEMAP_WINDOW_MS, PUBLICATION_PREVIEW_COOKIE, PUBLICATION_PREVIEW_HEADERS, SEO_LIMITS, buildNewsSitemapXml, cdata, createPublicationPreviewHandler, generateAllArticleSchemas, generateArticleCategoryMetadata, generateArticleListSchema, generateArticleMetadata, generateArticleSchema, generateArticleSitemap, generateArticleStaticParams, generateAtomFeed, generateAuthorPageMetadata, generateAuthorStaticParams, generateBreadcrumbSchema, generateCategoryStaticParams, generateClusterArticleSchema, generateClusterBreadcrumbSchema, generateClusterItemListSchema, generateClusterLandingSchema, generateClusterPageMetadata, generateFaqSchema, generateHowToSchema, generateNewsSitemap, generatePublicationMetadata, generateRssFeed, getAllArticleSlugs, getAllArticles, getAllAuthorSlugs, getArticle, getArticleCategories, getArticlePreview, getArticleResult, getAuthorBySlug, getAuthorPosts, getClusterNavigation, getPostsByCategory, getPublicationIssue, getPublicationIssues, getRelatedInsights, getTopicCluster, getTopicClusters, recentNewsPosts, requireArticle, sanitizeExcerpt, validateArticleSeo, validateMetaDescription, validateSeoTitle } from '../chunk-RI2N2EBI.js';
1
+ export { ArticleUnavailableError, FEED_DEFAULT_ITEMS, NEWS_SITEMAP_MAX_URLS, NEWS_SITEMAP_WINDOW_MS, PUBLICATION_PREVIEW_COOKIE, PUBLICATION_PREVIEW_HEADERS, SEO_LIMITS, buildNewsSitemapXml, cdata, createPublicationPreviewHandler, generateAllArticleSchemas, generateArticleCategoryMetadata, generateArticleListSchema, generateArticleMetadata, generateArticleSchema, generateArticleSitemap, generateArticleStaticParams, generateAtomFeed, generateAuthorPageMetadata, generateAuthorStaticParams, generateBreadcrumbSchema, generateCategoryStaticParams, generateClusterArticleSchema, generateClusterBreadcrumbSchema, generateClusterItemListSchema, generateClusterLandingSchema, generateClusterPageMetadata, generateFaqSchema, generateHowToSchema, generateNewsSitemap, generatePublicationMetadata, generateRssFeed, getAllArticleSlugs, getAllArticles, getAllAuthorSlugs, getArticle, getArticleCategories, getArticlePreview, getArticleResult, getAuthorBySlug, getAuthorPosts, getClusterNavigation, getPostsByCategory, getPublicationIssue, getPublicationIssues, getRelatedInsights, getTopicCluster, getTopicClusters, recentNewsPosts, requireArticle, sanitizeExcerpt, validateArticleSeo, validateMetaDescription, validateSeoTitle } from '../chunk-JYITKUUB.js';
2
2
  export { articlePhotoCss, insertArticlePhotos } from '../chunk-WHPCGQHC.js';
3
- export { articleTableCss, wrapArticleTables } from '../chunk-E4PTMORJ.js';
3
+ export { articleTableCss, wrapArticleTables } from '../chunk-AL5UCWLI.js';
4
4
  export { articleImageSchema, authorEntityId, generateArticleAuthorNode, generateAuthorPersonSchema, generateAuthorSchema, resolveArticleArtwork, resolveRepresentativeImage, withArticleAuthorIdentity } from '../chunk-UVYUTBT4.js';
5
5
  import '../chunk-H72ZFO3F.js';
6
+ import '../chunk-IBHBEUZV.js';
6
7
  export { articleCategorySlug, createPublicationRoutes, resolveArticleUrl, resolvePublicationUrl } from '../chunk-4K735O3J.js';
7
8
  import '../chunk-34ZYJTQR.js';
8
- import '../chunk-JSW42BHL.js';
9
+ import '../chunk-M5JIHQ42.js';
9
10
  import '../chunk-UAYAJPEP.js';
10
11
  import '../chunk-VWWITFWM.js';
11
12
  import '../chunk-4KZ6W3ZH.js';
12
13
  import '../chunk-MJP63SSS.js';
13
- import '../chunk-6KU7STM3.js';
14
+ import '../chunk-HY7ZEU2C.js';
14
15
  import '../chunk-C7TLGL3R.js';
15
16
  import '../chunk-MH2DSGC6.js';
16
17
  import '../chunk-YVKRYQRG.js';
17
18
  import '../chunk-FESKKJAS.js';
18
19
  import '../chunk-A4CC4ZG3.js';
19
20
  import '../chunk-W7OPEMGJ.js';
20
- import '../chunk-BUE3Z32O.js';
21
+ import '../chunk-F6WLHVMQ.js';
21
22
  import '../chunk-PKBMQBKP.js';
@@ -1,10 +1,10 @@
1
1
  'use client';
2
- export { ChatWidget, DEFAULT_LAUNCHER_OFFSET_BOTTOM, EchoChat, chatLauncherStyles, isChatEnabled } from '../chunk-IGAIQ2B3.js';
3
- export { SiteChat } from '../chunk-O3Z5HHFP.js';
2
+ export { ChatWidget, DEFAULT_LAUNCHER_OFFSET_BOTTOM, EchoChat, chatLauncherStyles, isChatEnabled } from '../chunk-TP32MRXV.js';
3
+ export { SiteChat } from '../chunk-SBPUFTMP.js';
4
4
  import '../chunk-6T6CQVNL.js';
5
5
  import '../chunk-JE4A5S4F.js';
6
6
  import '../chunk-27FISYK4.js';
7
- import '../chunk-HMTPRBYJ.js';
7
+ import '../chunk-IWNJCRXW.js';
8
8
  import '../chunk-Q6E4OPGG.js';
9
9
  import '../chunk-5ROTTMBL.js';
10
10
  import '../chunk-C2FZBUSS.js';
@@ -15,12 +15,12 @@ import '../chunk-S22FSH7C.js';
15
15
  import '../chunk-L2V5PUFN.js';
16
16
  import '../chunk-GJWI74ZZ.js';
17
17
  import '../chunk-5TM3WPCI.js';
18
- import '../chunk-DLM4FFNH.js';
19
- import '../chunk-EKBEOXTH.js';
20
- import '../chunk-IYCSDFPN.js';
21
- import '../chunk-QQT2T6JQ.js';
18
+ import '../chunk-ZQSCMYZO.js';
19
+ import '../chunk-64XODP7M.js';
20
+ import '../chunk-FLOJUMFR.js';
21
+ import '../chunk-7HW4TZB5.js';
22
22
  import '../chunk-W7OPEMGJ.js';
23
- import '../chunk-BUE3Z32O.js';
23
+ import '../chunk-F6WLHVMQ.js';
24
24
  import '../chunk-PKBMQBKP.js';
25
25
 
26
26
  // src/chat/echo-config.ts
@@ -1,6 +1,6 @@
1
1
  import { SLOTS_CONTRACT_VERSION, verifySlotContent, canonicalJson } from './chunk-VCJYLYJV.js';
2
2
  import { isValidSlotId, SLOT_CONTENT_MAX_LENGTH, parseSlotRichText, normalizeSlotValue } from './chunk-LPH5FANE.js';
3
- import { SITE_CACHE_TAGS } from './chunk-6KU7STM3.js';
3
+ import { SITE_CACHE_TAGS } from './chunk-HY7ZEU2C.js';
4
4
  import { resolveServerApiConfig } from './chunk-C7TLGL3R.js';
5
5
  import 'server-only';
6
6
  import * as React from 'react';
@@ -1,4 +1,4 @@
1
- import { generateLLMsTxt, generateLLMsFullTxt, appendAgentAccessSection } from './chunk-V67OD7RW.js';
1
+ import { generateLLMsTxt, generateLLMsFullTxt, appendAgentAccessSection } from './chunk-KG5IEV7I.js';
2
2
  import { createHash } from 'crypto';
3
3
  import { existsSync, readFileSync } from 'fs';
4
4
  import { join } from 'path';
@@ -0,0 +1,8 @@
1
+ // src/contracts/campaign-normalize.ts
2
+ function normalizeCampaignSlug(input) {
3
+ if (!input || typeof input !== "string") return null;
4
+ const slug = input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
5
+ return slug || null;
6
+ }
7
+
8
+ export { normalizeCampaignSlug };
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { normalizeDesignTokens, designTokenCssVars, sanitizePopupHtml, isSafePopupUrl } from './chunk-IHG36STL.js';
2
+ import { normalizeDesignTokens, designTokenCssVars, sanitizePopupHtml, isSafePopupUrl } from './chunk-XHYE24HU.js';
3
3
  import { useId, useRef, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -0,0 +1,7 @@
1
+ // src/contracts/frame-ready.ts
2
+ var FRAME_READY_MESSAGE = "sonor:frame-ready";
3
+ function isFrameReadyMessage(data) {
4
+ return !!data && typeof data === "object" && data.type === FRAME_READY_MESSAGE && data.v === 1;
5
+ }
6
+
7
+ export { FRAME_READY_MESSAGE, isFrameReadyMessage };
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- import { getSiteDesignTokens, tokensForReport, shouldReportDesign, sendSiteDesign } from './chunk-NUXXFCIT.js';
3
- import { designTokensHash } from './chunk-IHG36STL.js';
2
+ import { getSiteDesignTokens, tokensForReport, shouldReportDesign, sendSiteDesign } from './chunk-URLTBVRX.js';
3
+ import { designTokensHash } from './chunk-XHYE24HU.js';
4
4
  import { useDeferredActivation } from './chunk-YC7ELZS3.js';
5
- import { resolveAnalyticsTarget } from './chunk-DLM4FFNH.js';
5
+ import { resolveAnalyticsTarget } from './chunk-ZQSCMYZO.js';
6
6
  import { useRef, useEffect } from 'react';
7
7
 
8
8
  var STAMP_KEY = (site) => `_sk_design_sent:${site}`;
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { detectTimezone, releaseSlotHold, fetchBookingTypeDetails, fetchBookingTypes, fetchAvailability, createSlotHold, createBooking, formatDuration, formatDate, formatTime, prepareMeeting } from './chunk-72F44HKL.js';
2
+ import { detectTimezone, releaseSlotHold, fetchBookingTypeDetails, fetchBookingTypes, fetchAvailability, createSlotHold, createBooking, formatDuration, formatDate, formatTime, prepareMeeting } from './chunk-PSS6M4CG.js';
3
3
  import { readPublishedApiConfig, DEFAULT_SONOR_API_URL } from './chunk-W7OPEMGJ.js';
4
4
  import { useState, useRef, useMemo, useCallback, useEffect, useId } from 'react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -1,3 +1,5 @@
1
+ import { htmlTags, isClosingTag, htmlTagName, openTagAttributes } from './chunk-IBHBEUZV.js';
2
+
1
3
  // packages/press-kit/src/articles/processArticleHtml.ts
2
4
  function linkClassificationHost(siteUrl) {
3
5
  try {
@@ -24,20 +26,23 @@ function addExternalLinkTargets(html, siteUrl) {
24
26
  if (!html || !siteUrl) return html;
25
27
  const siteHost = linkClassificationHost(siteUrl);
26
28
  if (!siteHost) return html;
27
- return html.replace(/<a\b([^>]*)>/gi, (_full, attrs) => {
29
+ return html.replace(htmlTags(), (tag) => {
30
+ if (isClosingTag(tag) || htmlTagName(tag) !== "a") return tag;
31
+ const attrs = openTagAttributes(tag);
32
+ const unchanged = `<a${attrs}>`;
28
33
  const hrefMatch = attrs.match(/\bhref\s*=\s*(["'])([^"']*)\1/i);
29
- if (!hrefMatch) return `<a${attrs}>`;
34
+ if (!hrefMatch) return unchanged;
30
35
  const href = hrefMatch[2].trim();
31
- if (!/^https?:\/\//i.test(href)) return `<a${attrs}>`;
36
+ if (!/^https?:\/\//i.test(href)) return unchanged;
32
37
  try {
33
38
  const url = new URL(href);
34
39
  const linkHost = url.hostname.replace(/^www\./i, "").toLowerCase();
35
- if (linkHost === siteHost) return `<a${attrs}>`;
36
- if (/\btarget\s*=/i.test(attrs)) return `<a${attrs}>`;
40
+ if (linkHost === siteHost) return unchanged;
41
+ if (/\btarget\s*=/i.test(attrs)) return unchanged;
37
42
  const trimmed = attrs.trim();
38
43
  return `<a ${trimmed} target="_blank" rel="noopener noreferrer">`;
39
44
  } catch {
40
- return `<a${attrs}>`;
45
+ return unchanged;
41
46
  }
42
47
  });
43
48
  }
@@ -1,4 +1,4 @@
1
- import { SITE_KIT_VERSION } from './chunk-BUE3Z32O.js';
1
+ import { SITE_KIT_VERSION } from './chunk-F6WLHVMQ.js';
2
2
 
3
3
  // src/shared/siteToken.ts
4
4
  var SITE_TOKEN_PREFIX = "st1.";
@@ -1,3 +1,5 @@
1
+ import { htmlTags, htmlTagName, isClosingTag } from './chunk-IBHBEUZV.js';
2
+
1
3
  // packages/press-kit/src/articles/article-tables.ts
2
4
  var tableScrollClass = "sk-article-table-scroll";
3
5
  var legacyTableScrollClass = "sk-blog-table-scroll";
@@ -18,9 +20,6 @@ var voidElements = /* @__PURE__ */ new Set([
18
20
  "wbr"
19
21
  ]);
20
22
  var preservedElements = /* @__PURE__ */ new Set(["pre", "code", "script", "style", "textarea", "xmp", "template"]);
21
- function htmlTags() {
22
- return /<!--[\s\S]*?(?:-->|$)|<\/?[a-z][\w:-]*(?=[\s/>])(?:[^"'<>]|"[^"]*"|'[^']*')*>/gi;
23
- }
24
23
  function hasScrollClass(tag) {
25
24
  const attributes = tag.replace(/^<[\w:-]+/, "").replace(/\/?>$/, "");
26
25
  const pattern = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
@@ -31,14 +30,30 @@ function hasScrollClass(tag) {
31
30
  return false;
32
31
  }
33
32
  function labelText(html) {
34
- return html.replace(/<!--[\s\S]*?(?:-->|$)/g, "").replace(htmlTags(), " ").replace(/\s+/g, " ").trim();
33
+ return html.replace(htmlTags(), (token) => token.startsWith("<!--") ? "" : " ").replace(/\s+/g, " ").trim();
35
34
  }
36
35
  function escapeLabel(text) {
37
36
  return text.replace(/&(?!(?:#\d+|#x[\da-f]+|[a-z][\da-z]+);)/gi, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
38
37
  }
38
+ function elementContents(table, name) {
39
+ const tags = htmlTags();
40
+ const contents = [];
41
+ let from = -1;
42
+ let token;
43
+ while (token = tags.exec(table)) {
44
+ if (htmlTagName(token[0]) !== name) continue;
45
+ if (!isClosingTag(token[0])) {
46
+ if (from < 0) from = tags.lastIndex;
47
+ } else if (from >= 0) {
48
+ contents.push(table.slice(from, token.index));
49
+ from = -1;
50
+ }
51
+ }
52
+ return contents;
53
+ }
39
54
  function tableLabel(table, number) {
40
- const caption = table.match(/<caption\b(?:[^"'<>]|"[^"]*"|'[^']*')*>([\s\S]*?)<\/caption\s*>/i)?.[1];
41
- const headers = [...table.matchAll(/<th\b(?:[^"'<>]|"[^"]*"|'[^']*')*>([\s\S]*?)<\/th\s*>/gi)].slice(0, 4).map((match) => labelText(match[1])).filter(Boolean).join(", ");
55
+ const caption = elementContents(table, "caption")[0];
56
+ const headers = elementContents(table, "th").slice(0, 4).map(labelText).filter(Boolean).join(", ");
42
57
  const summary = labelText(caption || headers).slice(0, 180);
43
58
  return escapeLabel(`Table ${number}${summary ? `: ${summary}` : ""}`);
44
59
  }
@@ -50,8 +65,8 @@ function wrapArticleTables(html) {
50
65
  let token;
51
66
  while (token = tags.exec(html)) {
52
67
  const tag = token[0];
53
- if (tag.startsWith("<!--")) continue;
54
- const tagName = /^<\/?([\w:-]+)/.exec(tag)[1].toLowerCase();
68
+ const tagName = htmlTagName(tag);
69
+ if (!tagName) continue;
55
70
  if (tag.startsWith("</")) {
56
71
  const index = stack.map((frame3) => frame3.tag).lastIndexOf(tagName);
57
72
  if (index < 0) continue;
@@ -0,0 +1,390 @@
1
+ import { isBlockedPublicUrlHost } from './chunk-XS2ORMUG.js';
2
+ import { LandingPage } from './chunk-PAYCK7FC.js';
3
+ import { normalizeCampaignSlug } from './chunk-3MJNBOUO.js';
4
+ import { landingPageMetadata } from './chunk-JESLPEST.js';
5
+ import { ServerForm } from './chunk-YRQS7VAH.js';
6
+ import { serverApiFetch } from './chunk-C7TLGL3R.js';
7
+ import { normalizeSiteHost } from './chunk-A4CC4ZG3.js';
8
+ import 'server-only';
9
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
+
11
+ // src/contracts/publication-branding.ts
12
+ var PUBLICATION_COLOR_FIELDS = ["primary", "secondary", "accent", "background", "surface", "text", "muted", "border"];
13
+ var GENERIC_FONT_FAMILIES = /* @__PURE__ */ new Set(["serif", "sans-serif", "monospace", "system-ui", "cursive", "fantasy", "ui-serif", "ui-sans-serif", "ui-monospace"]);
14
+ function containsControlCharacters(value) {
15
+ for (const character of value) {
16
+ const code = character.charCodeAt(0);
17
+ if (code <= 31 || code === 127) return true;
18
+ }
19
+ return false;
20
+ }
21
+ function safeFontStack(value) {
22
+ if (typeof value !== "string" || value.length > 512 || containsControlCharacters(value) || /[;{}<>\\]|(?:var|url|expression)\s*\(/i.test(value)) return void 0;
23
+ const names = value.split(",").map((v) => v.trim().replace(/^(['"])(.*)\1$/, "$2")).filter(Boolean).filter((v) => !/^(?:heading|body|mono)$/i.test(v));
24
+ if (!names.length || names.some((v) => !/^[\p{L}\p{N} _.-]+$/u.test(v))) return void 0;
25
+ return names.map((v) => GENERIC_FONT_FAMILIES.has(v.toLowerCase()) ? v.toLowerCase() : `'${v}'`).join(",");
26
+ }
27
+
28
+ // src/contracts/funnels.ts
29
+ var MANAGED_FUNNEL_PATH_PREFIX = "/f";
30
+ var FUNNEL_TEMPLATES = ["newsletter", "requested_guide", "inquiry"];
31
+ var string = (maxLength, minLength = 1) => ({ type: "string", minLength, maxLength });
32
+ var literal = (value) => ({ const: value });
33
+ var enumeration = (...values) => ({ type: "string", enum: values });
34
+ var object = (properties, required = Object.keys(properties)) => ({ type: "object", properties, required, additionalProperties: false });
35
+ var array = (items, maxItems, minItems = 0) => ({ type: "array", items, maxItems, minItems });
36
+ var integer = (minimum, maximum = Number.MAX_SAFE_INTEGER) => ({ type: "integer", minimum, maximum });
37
+ var nullable = (shape) => ({ oneOf: [shape, { type: "null" }] });
38
+ var isoDate = { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$" };
39
+ var uuid = { type: "string", pattern: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" };
40
+ var slug = { ...string(80), pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$" };
41
+ var fieldSlug = { ...string(80), pattern: "^[a-zA-Z0-9_-]+$" };
42
+ var path = { ...string(512), pattern: "^/(?!/)[a-zA-Z0-9/_-]*$" };
43
+ var site = { ...string(253), pattern: "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$" };
44
+ var color = { ...string(9), pattern: "^#[a-fA-F0-9]{6}$" };
45
+ var colors = object(Object.fromEntries(PUBLICATION_COLOR_FIELDS.map((key) => [key, color])));
46
+ var typography = object({ headingFamily: string(200), bodyFamily: string(200) });
47
+ var override = object({
48
+ displayName: nullable(string(120)),
49
+ tagline: nullable(string(300, 0)),
50
+ fullLogoAssetId: nullable(uuid),
51
+ squareLogoAssetId: nullable(uuid),
52
+ colors: object(Object.fromEntries(PUBLICATION_COLOR_FIELDS.map((key) => [key, nullable(color)])), []),
53
+ typography: object({ headingFamily: nullable(string(200)), bodyFamily: nullable(string(200)) }, [])
54
+ }, []);
55
+ var bodyBlock = { oneOf: [object({ type: literal("paragraph"), text: string(2e3) }), object({ type: literal("bullets"), items: array(string(500), 8, 1) })] };
56
+ var body = array(bodyBlock, 12);
57
+ var publicBrand = object({ displayName: string(120), tagline: string(300, 0), fullLogoUrl: nullable(string(2048)), squareLogoUrl: nullable(string(2048)), colors, typography, scheme: enumeration("light", "dark") });
58
+ var step = { oneOf: [
59
+ object({ id: uuid, type: literal("wait"), durationSeconds: integer(60, 2592e3) }),
60
+ object({ id: uuid, type: literal("send_email"), templateId: uuid, purpose: enumeration("requested_fulfillment", "marketing"), guideAssetId: nullable(uuid) }, ["id", "type", "templateId", "purpose"]),
61
+ object({ id: uuid, type: literal("add_tag"), target: literal("subscriber"), tag: string(80) }),
62
+ object({ id: uuid, type: literal("create_task"), ownerContactId: uuid, title: string(160), dueAfterSeconds: integer(0, 2592e3) })
63
+ ] };
64
+ var definitionShape = object({
65
+ schemaVersion: literal(1),
66
+ name: string(120),
67
+ template: enumeration(...FUNNEL_TEMPLATES),
68
+ site,
69
+ entry: object({ formId: uuid, mode: literal("future_accepted_form_only") }),
70
+ goal: object({ type: enumeration("newsletter_confirmed", "guide_requested", "inquiry_received", "booking_confirmed"), bookingTypeId: nullable(uuid) }, ["type"]),
71
+ page: object({ mode: enumeration("existing", "managed"), path, slug: nullable(slug), headline: string(160, 0), body, formId: uuid, offerAssetId: nullable(uuid) }, ["mode", "path", "headline", "body", "formId"]),
72
+ permission: object({ mode: enumeration("requested_only", "marketing_opt_in"), optInFieldSlug: nullable(fieldSlug) }, ["mode"]),
73
+ brandOverride: override,
74
+ steps: array(step, 8),
75
+ exits: object({ unsubscribe: literal(true), manualStop: literal(true), confirmedBooking: object({ enabled: { type: "boolean" }, bookingTypeId: nullable(uuid) }) }, ["unsubscribe", "manualStop"])
76
+ }, ["schemaVersion", "name", "template", "site", "entry", "goal", "page", "permission", "steps", "exits"]);
77
+ var draftDefinitionShape = JSON.parse(JSON.stringify(definitionShape));
78
+ var draftProps = draftDefinitionShape.properties;
79
+ draftProps.name = string(120, 0);
80
+ draftProps.site = nullable(site);
81
+ draftProps.entry.properties.formId = nullable(uuid);
82
+ draftProps.page.properties.path = nullable(path);
83
+ draftProps.page.properties.formId = nullable(uuid);
84
+ draftProps.permission.properties.optInFieldSlug = nullable(fieldSlug);
85
+ draftProps.steps.items.oneOf = draftProps.steps.items.oneOf.map((item) => {
86
+ const fields = item.properties;
87
+ if (fields.templateId) fields.templateId = nullable(uuid);
88
+ if (fields.guideAssetId) fields.guideAssetId = nullable(uuid);
89
+ if (fields.ownerContactId) fields.ownerContactId = nullable(uuid);
90
+ if (fields.title) fields.title = string(160, 0);
91
+ if (fields.tag) fields.tag = string(80, 0);
92
+ return item;
93
+ });
94
+ draftProps.page.properties.body = array({ oneOf: [object({ type: literal("paragraph"), text: string(2e3, 0) }), object({ type: literal("bullets"), items: array(string(500, 0), 8, 1) })] }, 12);
95
+ var FUNNEL_DRAFT_DEFINITION_JSON_SCHEMA = { $schema: "http://json-schema.org/draft-07/schema#", ...draftDefinitionShape };
96
+ var FUNNEL_DEFINITION_JSON_SCHEMA = { $schema: "http://json-schema.org/draft-07/schema#", ...definitionShape };
97
+ var publishedShape = object({
98
+ schemaVersion: literal(1),
99
+ id: uuid,
100
+ versionId: uuid,
101
+ revision: integer(1),
102
+ site,
103
+ path,
104
+ content: object({ headline: string(160), body, offer: object({ label: string(120), href: string(2048) }) }, ["headline", "body"]),
105
+ brand: publicBrand,
106
+ conversion: object({ formSlug: fieldSlug }),
107
+ tracking: object({ campaignSlug: slug })
108
+ });
109
+ var PUBLISHED_FUNNEL_PAGE_JSON_SCHEMA = { $schema: "http://json-schema.org/draft-07/schema#", ...publishedShape };
110
+ var FUNNEL_LAUNCH_JSON_SCHEMA = object({ expectedRevision: integer(1), previewId: uuid, idempotencyKey: string(200), confirm: literal(true) });
111
+ var FUNNEL_LIFECYCLE_JSON_SCHEMA = object({ expectedActiveVersionId: uuid, idempotencyKey: string(200), confirm: literal(true) });
112
+ var FUNNEL_ROLLBACK_JSON_SCHEMA = object({ expectedActiveVersionId: uuid, targetVersionId: uuid, idempotencyKey: string(200), confirm: literal(true) });
113
+ var FunnelValidationError = class extends Error {
114
+ constructor(path2, reason) {
115
+ super(`${path2}: ${reason}`);
116
+ this.path = path2;
117
+ this.name = "FunnelValidationError";
118
+ }
119
+ };
120
+ function fail(path2, reason) {
121
+ throw new FunnelValidationError(path2, reason);
122
+ }
123
+ function check(value, shape, at) {
124
+ if (shape.oneOf) {
125
+ let matches = 0;
126
+ for (const candidate of shape.oneOf) {
127
+ try {
128
+ check(value, candidate, at);
129
+ matches++;
130
+ } catch (error) {
131
+ if (!(error instanceof FunnelValidationError)) throw error;
132
+ }
133
+ }
134
+ if (matches !== 1) fail(at, "must match one supported shape");
135
+ return;
136
+ }
137
+ if ("const" in shape && value !== shape.const) fail(at, "unexpected value");
138
+ if (shape.enum && !shape.enum.includes(value)) fail(at, "unsupported value");
139
+ if (shape.type === "null" && value !== null) fail(at, "must be null");
140
+ if (shape.type === "boolean" && typeof value !== "boolean") fail(at, "must be boolean");
141
+ if (shape.type === "string") {
142
+ if (typeof value !== "string") fail(at, "must be a string");
143
+ if (value.length < (shape.minLength ?? 0) || value.length > (shape.maxLength ?? Infinity) || (shape.minLength ?? 0) > 0 && !value.trim() || /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(value)) fail(at, "invalid string length or control character");
144
+ if (shape.pattern) {
145
+ const match = new RegExp(shape.pattern).exec(value);
146
+ if (!match || match[0] !== value) fail(at, "invalid format");
147
+ }
148
+ }
149
+ if (shape.type === "integer" && (typeof value !== "number" || !Number.isSafeInteger(value) || value < (shape.minimum ?? -Infinity) || value > (shape.maximum ?? Infinity))) fail(at, "invalid integer");
150
+ if (shape.type === "array") {
151
+ if (!Array.isArray(value) || value.length < (shape.minItems ?? 0) || value.length > (shape.maxItems ?? Infinity)) fail(at, "invalid array length");
152
+ value.forEach((item, i) => check(item, shape.items, `${at}[${i}]`));
153
+ }
154
+ if (shape.type === "object") {
155
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(at, "must be an object");
156
+ const row = value;
157
+ if (![Object.prototype, null].includes(Object.getPrototypeOf(row))) fail(at, "must be plain JSON");
158
+ for (const key of shape.required ?? []) if (!Object.prototype.hasOwnProperty.call(row, key)) fail(`${at}.${key}`, "required");
159
+ for (const key of Object.keys(row)) {
160
+ if (!Object.prototype.hasOwnProperty.call(shape.properties, key)) fail(`${at}.${key}`, "unknown field");
161
+ check(row[key], shape.properties[key], `${at}.${key}`);
162
+ }
163
+ }
164
+ }
165
+ function isSafeFunnelPublicUrl(value) {
166
+ try {
167
+ const url = new URL(value);
168
+ return value.startsWith("https://") && url.protocol === "https:" && !url.username && !url.password && (!url.port || url.port === "443") && !isBlockedPublicUrlHost(url.hostname) && !/[\s\x00-\x1f\\]/.test(value);
169
+ } catch {
170
+ return false;
171
+ }
172
+ }
173
+ function managedFunnelPath(value) {
174
+ check(value, slug, "slug");
175
+ if (normalizeCampaignSlug(value) !== value) fail("slug", "must be canonical");
176
+ return `${MANAGED_FUNNEL_PATH_PREFIX}/${value}`;
177
+ }
178
+ function checkFonts(value, at) {
179
+ for (const [key, font] of Object.entries(value.typography ?? {})) if (font !== null && safeFontStack(font) === void 0) fail(`${at}.typography.${key}`, "unsafe font stack");
180
+ }
181
+ function checkBody(blocks, at) {
182
+ if (blocks.reduce((total, block) => total + (block.type === "paragraph" ? block.text.length : block.items.join("").length), 0) > 12e3) fail(at, "total text exceeds 12000 characters");
183
+ }
184
+ function parsePublishedFunnelPage(value) {
185
+ check(value, publishedShape, "page");
186
+ const result = JSON.parse(JSON.stringify(value));
187
+ const suffix = result.path.slice(MANAGED_FUNNEL_PATH_PREFIX.length + 1);
188
+ if (managedFunnelPath(suffix) !== result.path) fail("page.path", "invalid managed route");
189
+ for (const key of ["fullLogoUrl", "squareLogoUrl"]) if (result.brand[key] && !isSafeFunnelPublicUrl(result.brand[key])) fail(`page.brand.${key}`, "unsafe public URL");
190
+ checkFonts(result.brand, "page.brand");
191
+ checkBody(result.content.body, "page.content.body");
192
+ if (result.content.offer && !isSafeFunnelPublicUrl(result.content.offer.href)) fail("page.content.offer.href", "unsafe public URL");
193
+ return result;
194
+ }
195
+ var FUNNEL_CREATE_JSON_SCHEMA = object({ idempotencyKey: string(200), definition: draftDefinitionShape });
196
+ var FUNNEL_UPDATE_JSON_SCHEMA = object({ expectedRevision: integer(1), idempotencyKey: string(200), definition: draftDefinitionShape });
197
+ var FUNNEL_REVISION_JSON_SCHEMA = object({ expectedRevision: integer(1) });
198
+ var FUNNEL_RETRY_JSON_SCHEMA = object({ expectedAttempt: integer(0, 100), idempotencyKey: string(200), confirm: literal(true) });
199
+ var FUNNEL_DRAFT_JSON_SCHEMAS = { create: FUNNEL_CREATE_JSON_SCHEMA, update: FUNNEL_UPDATE_JSON_SCHEMA };
200
+ var fingerprint = { type: "string", pattern: "^[a-f0-9]{64}$" };
201
+ var issue = object({ code: { ...string(80), pattern: "^[A-Z][A-Z0-9_]*$" }, path: string(300), message: string(500), fixRef: object({ section: string(80), field: string(80), resourceId: uuid }, ["section"]) }, ["code", "path", "message"]);
202
+ var FUNNEL_READINESS_JSON_SCHEMA = object({ definitionRevision: integer(1), brandFingerprint: fingerprint, resourceFingerprint: fingerprint, ready: { type: "boolean" }, blockers: array(issue, 50), warnings: array(issue, 50), capabilities: object({ managedPage: { type: "boolean" } }) });
203
+ var funnelDetailShape = object({ id: uuid, revision: integer(1), definition: draftDefinitionShape, activeVersionId: nullable(uuid), lifecycle: enumeration("draft", "active", "paused"), health: enumeration("healthy", "waiting", "needs_attention", "unknown"), hasDraftChanges: { type: "boolean" }, readiness: FUNNEL_READINESS_JSON_SCHEMA });
204
+ var FUNNEL_DETAIL_JSON_SCHEMA = funnelDetailShape;
205
+ var FUNNEL_OPERATION_RECEIPT_JSON_SCHEMA = object({
206
+ operationId: uuid,
207
+ projectId: uuid,
208
+ orgId: uuid,
209
+ funnelId: uuid,
210
+ operation: enumeration("create", "update", "launch", "pause", "resume", "retry", "rollback"),
211
+ idempotencyKey: string(200),
212
+ requestDigest: fingerprint,
213
+ versionId: nullable(uuid),
214
+ lifecycle: enumeration("draft", "active", "paused"),
215
+ replayed: { type: "boolean" },
216
+ armedFutureEntrantsOnly: literal(true),
217
+ enrolledExisting: literal(0),
218
+ messagesSent: literal(0),
219
+ pagePublication: enumeration("not_requested", "published", "revalidation_pending"),
220
+ health: enumeration("healthy", "waiting", "needs_attention", "unknown"),
221
+ revision: integer(1),
222
+ commandState: literal("committed"),
223
+ funnel: nullable(funnelDetailShape),
224
+ actor: string(200),
225
+ armedAt: nullable(isoDate),
226
+ effectScope: enumeration("configuration_only", "safe_retry_armed"),
227
+ target: object({ funnelId: nullable(uuid), enrollmentId: nullable(uuid), executionId: nullable(uuid), targetVersionId: nullable(uuid) }),
228
+ ledgerProposalId: uuid
229
+ });
230
+ var provenancePaths = ["displayName", "tagline", "fullLogoUrl", "squareLogoUrl", "scheme", ...PUBLICATION_COLOR_FIELDS.map((key) => `colors.${key}`), "typography.headingFamily", "typography.bodyFamily"];
231
+ var brandSnapshotShape = object({
232
+ page: publicBrand,
233
+ email: publicBrand,
234
+ provenance: object(Object.fromEntries(["page", "email"].flatMap((channel) => provenancePaths.map((key) => [`${channel}.${key}`, enumeration("project", "brand_profile", "site_design", "publication_branding", "platforms.publication", "platforms.email", "funnel_override", "default")]))), []),
235
+ assetRefs: array(object({ id: nullable(uuid), version: nullable(string(128)) }), 32),
236
+ capturedAt: isoDate,
237
+ fingerprint
238
+ });
239
+ var FUNNEL_BRAND_SNAPSHOT_JSON_SCHEMA = brandSnapshotShape;
240
+ var choicePage = (item) => object({ items: array(item, 100), nextCursor: nullable(string(500)) });
241
+ var choiceFields = { id: uuid, label: string(120) };
242
+ var catalogShape = object({
243
+ templates: array(object({ id: enumeration(...FUNNEL_TEMPLATES), label: string(120), defaults: draftDefinitionShape }), 3),
244
+ sites: array(object({ host: site, label: string(200) }), 200),
245
+ brand: nullable(brandSnapshotShape),
246
+ choices: object({
247
+ forms: choicePage(object({ ...choiceFields, slug: string(120), site: nullable(site), formType: enumeration("prospect", "contact", "support", "feedback", "newsletter", "custom"), active: { type: "boolean" }, newsletterListIds: array(uuid, 20), fields: array(object({ slug: fieldSlug, type: string(80), label: string(120), required: { type: "boolean" } }), 100) })),
248
+ lists: choicePage(object({ ...choiceFields, active: { type: "boolean" }, doubleOptIn: { type: "boolean" } })),
249
+ emailTemplates: choicePage(object({ ...choiceFields, subject: string(998, 0), updatedAt: nullable(isoDate) })),
250
+ assets: choicePage(object({ ...choiceFields, label: string(160), mimeType: string(100), sizeBytes: integer(0, 52428800), visibility: literal("public"), publicUrl: string(2048), version: nullable(string(200)) })),
251
+ owners: choicePage(object({ ...choiceFields, active: literal(true) })),
252
+ bookingTypes: choicePage(object({ ...choiceFields, site: nullable(site), active: literal(true), requiresConfirmation: { type: "boolean" } }))
253
+ }, []),
254
+ capabilities: object({ managedPage: { type: "boolean" } })
255
+ });
256
+ var FUNNEL_CATALOG_JSON_SCHEMA = catalogShape;
257
+ var launchEvidenceShape = object({ projectId: uuid, orgId: uuid, funnelId: uuid, previewId: uuid, versionId: uuid, definitionRevision: integer(1), definitionDigest: fingerprint, brandFingerprint: fingerprint, resourceFingerprint: fingerprint, entryDigest: fingerprint, permissionDigest: fingerprint, expiresAt: isoDate });
258
+ var FUNNEL_LAUNCH_EVIDENCE_JSON_SCHEMA = launchEvidenceShape;
259
+ var FUNNEL_ROLLBACK_EVIDENCE_JSON_SCHEMA = object({ orgId: uuid, projectId: uuid, funnelId: uuid, targetVersionId: uuid, definitionRevision: integer(1), definitionDigest: fingerprint, brandFingerprint: fingerprint, resourceFingerprint: fingerprint });
260
+ var FUNNEL_PREVIEW_JSON_SCHEMA = object({
261
+ previewId: uuid,
262
+ versionId: uuid,
263
+ expiresAt: isoDate,
264
+ definitionRevision: integer(1),
265
+ brand: brandSnapshotShape,
266
+ readiness: FUNNEL_READINESS_JSON_SCHEMA,
267
+ page: nullable(publishedShape),
268
+ launchEvidence: launchEvidenceShape,
269
+ emailPreviews: array(object({ stepId: uuid, ready: { type: "boolean" }, subject: nullable(string(998, 0)), contentHtml: nullable(string(2e5, 0)), issues: array(issue, 50) }), 8)
270
+ });
271
+ var summaryShape = object({
272
+ id: uuid,
273
+ name: string(120, 0),
274
+ template: enumeration(...FUNNEL_TEMPLATES),
275
+ site: nullable(site),
276
+ revision: integer(1),
277
+ activeVersionId: nullable(uuid),
278
+ lifecycle: enumeration("draft", "active", "paused"),
279
+ health: enumeration("healthy", "waiting", "needs_attention", "unknown"),
280
+ hasDraftChanges: { type: "boolean" },
281
+ counts: object({ entered: integer(0), waiting: integer(0), running: integer(0), completed: integer(0), exited: integer(0), needsAttention: integer(0) }),
282
+ countsBasis: literal("all_versions_all_time"),
283
+ lastEventAt: nullable(isoDate)
284
+ });
285
+ var FUNNEL_SUMMARY_JSON_SCHEMA = summaryShape;
286
+ var FUNNEL_ENROLLMENT_JSON_SCHEMA = object({
287
+ id: uuid,
288
+ versionId: uuid,
289
+ state: enumeration("waiting", "running", "completed", "exited", "needs_attention"),
290
+ effectiveStatus: enumeration("paused", "waiting", "running", "completed", "exited", "needs_attention"),
291
+ source: object({ submissionId: uuid, acceptedAt: isoDate }),
292
+ subject: object({ kind: enumeration("subscriber", "contact", "submission"), id: uuid, displayLabel: string(200) }),
293
+ enteredAt: isoDate,
294
+ nextRunAt: nullable(isoDate),
295
+ exitCode: nullable(string(80)),
296
+ stepsCompleted: integer(0, 8),
297
+ stepsTotal: integer(0, 8),
298
+ lastReceiptAt: nullable(isoDate)
299
+ });
300
+ var FUNNEL_TIMELINE_JSON_SCHEMA = object({
301
+ items: array(object({ executionId: uuid, stepId: uuid, type: enumeration("wait", "send_email", "add_tag", "create_task"), state: enumeration("pending", "claimed", "queued", "admitted", "succeeded", "skipped", "uncertain", "failed"), dueAt: nullable(isoDate), occurredAt: nullable(isoDate), attempt: integer(0, 100), reasonCode: nullable(string(80)), message: string(500, 0), receipt: nullable(object({ domain: enumeration("campaign_send", "subscriber_tag", "crm_task", "wait"), id: nullable(uuid), state: string(80) })), canRetry: { type: "boolean" } }), 100),
302
+ nextCursor: nullable(string(500)),
303
+ exit: nullable(object({ code: string(80), sourceType: string(80), sourceId: nullable(uuid), occurredAt: isoDate }))
304
+ });
305
+ function freezeSchema(value) {
306
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) return;
307
+ for (const child of Object.values(value)) freezeSchema(child);
308
+ Object.freeze(value);
309
+ }
310
+ for (const schema of [FUNNEL_DEFINITION_JSON_SCHEMA, FUNNEL_DRAFT_DEFINITION_JSON_SCHEMA, PUBLISHED_FUNNEL_PAGE_JSON_SCHEMA, FUNNEL_LAUNCH_JSON_SCHEMA, FUNNEL_LIFECYCLE_JSON_SCHEMA, FUNNEL_ROLLBACK_JSON_SCHEMA, FUNNEL_CREATE_JSON_SCHEMA, FUNNEL_UPDATE_JSON_SCHEMA, FUNNEL_REVISION_JSON_SCHEMA, FUNNEL_RETRY_JSON_SCHEMA, FUNNEL_DRAFT_JSON_SCHEMAS, FUNNEL_READINESS_JSON_SCHEMA, FUNNEL_DETAIL_JSON_SCHEMA, FUNNEL_OPERATION_RECEIPT_JSON_SCHEMA, FUNNEL_BRAND_SNAPSHOT_JSON_SCHEMA, FUNNEL_CATALOG_JSON_SCHEMA, FUNNEL_PREVIEW_JSON_SCHEMA, FUNNEL_SUMMARY_JSON_SCHEMA, FUNNEL_ENROLLMENT_JSON_SCHEMA, FUNNEL_TIMELINE_JSON_SCHEMA]) freezeSchema(schema);
311
+ var FUNNEL_VERSION_JSON_SCHEMA = object({ versionId: uuid, definitionRevision: integer(1), publishedAt: isoDate, brandFingerprint: fingerprint, resourceFingerprint: fingerprint, active: { type: "boolean" } });
312
+ var FUNNEL_VERSION_DETAIL_JSON_SCHEMA = object({ ...FUNNEL_VERSION_JSON_SCHEMA.properties, rollbackEvidence: FUNNEL_ROLLBACK_EVIDENCE_JSON_SCHEMA });
313
+ for (const schema of [FUNNEL_LAUNCH_EVIDENCE_JSON_SCHEMA, FUNNEL_ROLLBACK_EVIDENCE_JSON_SCHEMA, FUNNEL_VERSION_JSON_SCHEMA, FUNNEL_VERSION_DETAIL_JSON_SCHEMA]) freezeSchema(schema);
314
+ function FunnelPageContent({ page: input, conversion, preview = false }) {
315
+ const page = parsePublishedFunnelPage(input);
316
+ const { brand, content } = page;
317
+ const tokens = {
318
+ "--sk-primary": brand.colors.primary,
319
+ "--sk-secondary": brand.colors.secondary,
320
+ "--sk-bg": brand.colors.background,
321
+ "--sk-surface": brand.colors.surface,
322
+ "--sk-text-primary": brand.colors.text,
323
+ "--sk-text-secondary": brand.colors.muted,
324
+ "--sk-border": brand.colors.border,
325
+ "--sk-font": brand.typography.bodyFamily,
326
+ colorScheme: brand.scheme,
327
+ color: "var(--sk-text-primary)",
328
+ background: "var(--sk-bg)",
329
+ fontFamily: "var(--sk-font)",
330
+ overflowWrap: "anywhere"
331
+ };
332
+ return /* @__PURE__ */ jsxs("article", { className: "sk-funnel", style: tokens, "data-sk-private-preview": preview ? "" : void 0, "data-sk-funnel-revision": page.revision, children: [
333
+ preview && /* @__PURE__ */ jsx("meta", { name: "robots", content: "noindex, nofollow" }),
334
+ /* @__PURE__ */ jsx("style", { children: `.sk-funnel{padding:clamp(1rem,5vw,4rem);line-height:1.65}.sk-funnel-inner{max-width:68rem;margin-inline:auto}.sk-funnel-brand{display:flex;align-items:center;gap:1rem;margin-bottom:2rem}.sk-funnel-brand img{max-width:min(16rem,60vw);height:auto;max-height:5rem;object-fit:contain}.sk-funnel-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,24rem),1fr));gap:clamp(1.5rem,5vw,4rem);align-items:start}.sk-funnel h1{font-size:clamp(2rem,5vw,3.5rem);line-height:1.12;margin:0 0 1.5rem}.sk-funnel p,.sk-funnel li{max-width:65ch}.sk-funnel-conversion{padding:clamp(1rem,3vw,2rem);border:1px solid var(--sk-border);border-radius:var(--sk-radius-lg);background:var(--sk-surface)}.sk-funnel a{color:var(--sk-primary);text-decoration:underline}.sk-funnel a:focus-visible{outline:3px solid var(--sk-primary);outline-offset:4px}.sk-funnel-preview{font-weight:600}.sk-funnel ul{padding-inline-start:1.25rem}` }),
335
+ /* @__PURE__ */ jsxs("div", { className: "sk-funnel-inner", children: [
336
+ /* @__PURE__ */ jsxs("header", { className: "sk-funnel-brand", children: [
337
+ brand.fullLogoUrl && /* @__PURE__ */ jsx("img", { src: brand.fullLogoUrl, alt: brand.displayName, width: 256, height: 80, fetchPriority: "high" }),
338
+ /* @__PURE__ */ jsxs("div", { children: [
339
+ !brand.fullLogoUrl && /* @__PURE__ */ jsx("strong", { children: brand.displayName }),
340
+ brand.tagline && /* @__PURE__ */ jsx("p", { children: brand.tagline })
341
+ ] })
342
+ ] }),
343
+ /* @__PURE__ */ jsxs("div", { className: "sk-funnel-grid", children: [
344
+ /* @__PURE__ */ jsxs("section", { "aria-label": content.headline, children: [
345
+ /* @__PURE__ */ jsx("h1", { style: { fontFamily: brand.typography.headingFamily }, children: content.headline }),
346
+ content.body.map((block, index) => block.type === "paragraph" ? /* @__PURE__ */ jsx("p", { children: block.text }, index) : /* @__PURE__ */ jsx("ul", { children: block.items.map((item, i) => /* @__PURE__ */ jsx("li", { children: item }, i)) }, index)),
347
+ content.offer && (preview ? /* @__PURE__ */ jsx("p", { children: content.offer.label }) : /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", { href: content.offer.href, rel: "noopener noreferrer", children: content.offer.label }) }))
348
+ ] }),
349
+ /* @__PURE__ */ jsx("section", { className: "sk-funnel-conversion", "aria-label": "Response form", children: preview ? /* @__PURE__ */ jsx("p", { className: "sk-funnel-preview", role: "status", children: "Form preview. Submissions are disabled." }) : conversion })
350
+ ] })
351
+ ] })
352
+ ] });
353
+ }
354
+ async function getPublishedFunnelPage(slug2, options) {
355
+ let path2;
356
+ try {
357
+ path2 = managedFunnelPath(slug2);
358
+ } catch (error) {
359
+ if (error instanceof FunnelValidationError) return null;
360
+ throw error;
361
+ }
362
+ const site2 = normalizeSiteHost(options.site);
363
+ if (!site2 || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/.test(site2)) throw new Error("An explicit canonical registered site is required");
364
+ const data = await serverApiFetch(`/api/public/funnels/${encodeURIComponent(slug2)}?site=${encodeURIComponent(site2)}`, {
365
+ apiKey: options.apiKey,
366
+ apiUrl: options.apiUrl,
367
+ revalidate: 0,
368
+ init: { cache: "no-store" },
369
+ logPrefix: "@sonordev/site-kit/landing"
370
+ });
371
+ if (data === null) return null;
372
+ const page = parsePublishedFunnelPage(data);
373
+ if (page.site !== site2 || page.path !== path2) throw new Error("Published funnel page scope mismatch");
374
+ return page;
375
+ }
376
+ function managedFunnelMetadata(page) {
377
+ const safe = parsePublishedFunnelPage(page);
378
+ return landingPageMetadata({ title: safe.content.headline, alternates: { canonical: `https://${safe.site}${safe.path}` } });
379
+ }
380
+ async function ManagedFunnelPage({ page: input, preview = false, configOptions }) {
381
+ const page = parsePublishedFunnelPage(input);
382
+ if (preview) return /* @__PURE__ */ jsx(FunnelPageContent, { page, preview: true });
383
+ const form = await ServerForm({ formId: page.conversion.formSlug, defer: false, enter: "none", configOptions });
384
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
385
+ /* @__PURE__ */ jsx(LandingPage, { campaign: page.tracking.campaignSlug, robotsMeta: false }),
386
+ /* @__PURE__ */ jsx(FunnelPageContent, { page, conversion: form })
387
+ ] });
388
+ }
389
+
390
+ export { FunnelPageContent, ManagedFunnelPage, getPublishedFunnelPage, managedFunnelMetadata };
@@ -1,4 +1,4 @@
1
- import { sonorFetch } from './chunk-IYCSDFPN.js';
1
+ import { sonorFetch } from './chunk-FLOJUMFR.js';
2
2
  import { readPublishedApiConfig } from './chunk-W7OPEMGJ.js';
3
3
 
4
4
  // src/forms/formsApi.ts