@sonordev/site-kit 8.5.2 → 8.7.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 (170) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/agent-manifest.json +1 -1
  3. package/dist/{AnalyticsProvider-HSJMHYBA.js → AnalyticsProvider-3UIQMIRW.js} +6 -6
  4. package/dist/{ArticleViewTracker-XEA6ZY67.js → ArticleViewTracker-KNXP2DXL.js} +5 -5
  5. package/dist/{BlocksPopup-DUTOENMQ.js → BlocksPopup-DFE32GPX.js} +9 -8
  6. package/dist/{ChatWidget-PMVQTSEU.js → ChatWidget-L74L4YAD.js} +6 -6
  7. package/dist/FileField-P3PLEAON.js +237 -0
  8. package/dist/{FormSpotlight-RTPJPLQ3.js → FormSpotlight-DNBSBRIE.js} +2 -2
  9. package/dist/{FormStage-UANGD66Z.js → FormStage-I4JGLT57.js} +2 -2
  10. package/dist/{ManagedForm-4AACRHEY.js → ManagedForm-GJRGBM6W.js} +8 -8
  11. package/dist/{ManagedNewsletterForm-RWKFWZ72.js → ManagedNewsletterForm-AUZ6YB7G.js} +11 -11
  12. package/dist/{SignalCore-XULLPKPX.js → SignalCore-UV7K7PT4.js} +5 -5
  13. package/dist/SiteChat-6UWE545R.js +6 -0
  14. package/dist/SiteDesignReporter-ZSU76DZJ.js +13 -0
  15. package/dist/{SitePopups-US6VQN7X.js → SitePopups-XHWU7GNF.js} +6 -6
  16. package/dist/SitemapSync-ELWV6W7Y.js +9 -0
  17. package/dist/_client/booking-widget.js +5 -5
  18. package/dist/affiliates/index.js +3 -3
  19. package/dist/analytics/content-text.d.ts +17 -0
  20. package/dist/analytics/index.js +6 -6
  21. package/dist/{article-styles-VASFBYA3.js → article-styles-D7VCDXMG.js} +2 -1
  22. package/dist/article-tables-W45BE353.js +3 -0
  23. package/dist/articles/index.js +7 -20
  24. package/dist/articles/server-ui.js +29 -55
  25. package/dist/articles/server.js +6 -5
  26. package/dist/chat/index.js +9 -9
  27. package/dist/{chunk-3RDJKIYO.js → chunk-2WDWQI7K.js} +1 -1
  28. package/dist/{chunk-DLTLXYOV.js → chunk-36ZMNBXZ.js} +4 -9
  29. package/dist/chunk-3MJNBOUO.js +8 -0
  30. package/dist/{chunk-IXA7ZB6U.js → chunk-3MLWIBHX.js} +1 -1
  31. package/dist/{chunk-BF7TZYC3.js → chunk-52KO4LKD.js} +1 -1
  32. package/dist/{chunk-ZDUTXHM6.js → chunk-5WHBYQJ4.js} +3 -3
  33. package/dist/{chunk-5D7ZISUP.js → chunk-7DYMM6SS.js} +11 -6
  34. package/dist/{chunk-HNYECNCU.js → chunk-7YB3QA4P.js} +15 -7
  35. package/dist/{chunk-N4ZCNYWF.js → chunk-A2MDVP47.js} +1 -1
  36. package/dist/{chunk-E4PTMORJ.js → chunk-AL5UCWLI.js} +23 -8
  37. package/dist/{chunk-ANVHRT35.js → chunk-BAJUXQTH.js} +2 -2
  38. package/dist/{chunk-ITQND6IB.js → chunk-BJYX6SFI.js} +2 -2
  39. package/dist/{chunk-TYAVHRWH.js → chunk-CVADNHJ4.js} +8 -2
  40. package/dist/{chunk-5BRHTBPO.js → chunk-DJIGFX33.js} +1 -1
  41. package/dist/{chunk-ZSU7KTIE.js → chunk-DPYGC7WE.js} +2 -2
  42. package/dist/{chunk-XA7Q2VQA.js → chunk-EK7P62DT.js} +1 -1
  43. package/dist/chunk-FXR5PJDL.js +420 -0
  44. package/dist/{chunk-GLLFIE2R.js → chunk-G5FD7UXK.js} +2 -2
  45. package/dist/{chunk-OFQ3BU52.js → chunk-HM7CDWFK.js} +1 -1
  46. package/dist/{chunk-Q4Y3WIHE.js → chunk-HWQPCZOX.js} +1 -1
  47. package/dist/{chunk-6KU7STM3.js → chunk-HY7ZEU2C.js} +3 -1
  48. package/dist/chunk-IBHBEUZV.js +27 -0
  49. package/dist/{chunk-7HRYSLY2.js → chunk-IXZBOOAD.js} +4 -4
  50. package/dist/chunk-JESLPEST.js +14 -0
  51. package/dist/{chunk-BC7JMY4Z.js → chunk-JG53YJLM.js} +58 -1
  52. package/dist/{chunk-FVXCQC7W.js → chunk-JWCCY72G.js} +1 -1
  53. package/dist/{chunk-RI2N2EBI.js → chunk-JYITKUUB.js} +13 -5
  54. package/dist/chunk-KIYACQW3.js +1 -0
  55. package/dist/{chunk-YW3LNSBM.js → chunk-KLIDCNML.js} +1 -1
  56. package/dist/{chunk-3PT6SO65.js → chunk-L2KSHJS2.js} +5 -5
  57. package/dist/{chunk-27LYZVPP.js → chunk-LGWN6KCN.js} +2 -2
  58. package/dist/{chunk-GTVOB5RN.js → chunk-MJW6YIDJ.js} +1 -1
  59. package/dist/{chunk-CFSIGEUW.js → chunk-MNORERMF.js} +1 -1
  60. package/dist/{chunk-Y35BWW2C.js → chunk-ODOV76VW.js} +1 -1
  61. package/dist/{chunk-EQHGPB3J.js → chunk-OHLCE4GC.js} +2 -2
  62. package/dist/{chunk-OEA7U4HJ.js → chunk-P5PRR2BB.js} +1 -1
  63. package/dist/{chunk-JJ4JS3LD.js → chunk-PAYCK7FC.js} +1 -1
  64. package/dist/{chunk-DLM4FFNH.js → chunk-QTBAKOCZ.js} +1 -1
  65. package/dist/{chunk-2QFIYYMP.js → chunk-RHT7OZNF.js} +1 -1
  66. package/dist/{chunk-UGZ3B5AK.js → chunk-RWWSOYJO.js} +9 -9
  67. package/dist/{chunk-T6U6J3QK.js → chunk-T6TWPKBL.js} +1 -1
  68. package/dist/{chunk-5SQK3D53.js → chunk-TYSIAWC3.js} +1 -0
  69. package/dist/{chunk-24NM64IV.js → chunk-U4OQJQK6.js} +3 -3
  70. package/dist/{chunk-GDQKDL3E.js → chunk-UYOE3HA4.js} +2 -2
  71. package/dist/chunk-VEJD5EJ2.js +344 -0
  72. package/dist/{chunk-LNSF6BJC.js → chunk-VPZJ7ZTL.js} +2 -2
  73. package/dist/chunk-XAZC2TUD.js +1119 -0
  74. package/dist/{chunk-A7RZQI6H.js → chunk-XC2TWYDV.js} +3 -3
  75. package/dist/{chunk-URJN75ZG.js → chunk-XIDWNXZN.js} +3 -3
  76. package/dist/chunk-XLAHRN4B.js +1 -0
  77. package/dist/{chunk-URZTBPZO.js → chunk-XZFOQ2V4.js} +113 -93
  78. package/dist/{chunk-I4LMZNBN.js → chunk-YYPYLYZM.js} +1 -1
  79. package/dist/client/index.js +4 -4
  80. package/dist/contracts/broadcast-layouts.d.ts +133 -0
  81. package/dist/contracts/business-claims.d.ts +456 -0
  82. package/dist/contracts/campaign-normalize.d.ts +2 -0
  83. package/dist/contracts/entries.d.ts +1 -1
  84. package/dist/contracts/funnels.d.ts +576 -0
  85. package/dist/contracts/inbox.d.ts +42 -0
  86. package/dist/contracts/llms.d.ts +200 -0
  87. package/dist/contracts/outreach.d.ts +267 -0
  88. package/dist/contracts/page-titles.d.ts +140 -0
  89. package/dist/contracts/portfolio.d.ts +170 -0
  90. package/dist/contracts/public-url-host.d.ts +10 -0
  91. package/dist/contracts/publication-branding.d.ts +151 -0
  92. package/dist/contracts/publication-charter.d.ts +14 -0
  93. package/dist/contracts/site-cache.d.ts +2 -0
  94. package/dist/fleet/index.js +6 -6
  95. package/dist/forms/FileField.d.ts +3 -1
  96. package/dist/forms/PrivateFileField.d.ts +3 -0
  97. package/dist/forms/index.js +11 -11
  98. package/dist/forms/private-file-rules.d.ts +8 -0
  99. package/dist/forms/server.js +5 -5
  100. package/dist/forms/static.js +3 -3
  101. package/dist/forms/styles.css +17 -1
  102. package/dist/forms/types.d.ts +4 -0
  103. package/dist/images/index.js +4 -4
  104. package/dist/index.js +1 -1
  105. package/dist/landing/contract.d.ts +2 -13
  106. package/dist/landing/contract.js +2 -0
  107. package/dist/landing/index.js +4 -2
  108. package/dist/landing/managed-server.d.ts +21 -0
  109. package/dist/landing/managed.d.ts +11 -0
  110. package/dist/landing/metadata.js +2 -0
  111. package/dist/landing/server.d.ts +5 -0
  112. package/dist/landing/server.js +19 -2
  113. package/dist/layout/client.js +9 -9
  114. package/dist/layout/index.js +10 -10
  115. package/dist/llms/contract.js +2 -1
  116. package/dist/llms/index.js +9 -7
  117. package/dist/llms/merge.d.ts +29 -0
  118. package/dist/maps/index.js +3 -3
  119. package/dist/mcp/sonor.js +12 -11
  120. package/dist/portfolio/contract.js +1 -1
  121. package/dist/press-kit/articles/article-body.d.ts +13 -0
  122. package/dist/press-kit/articles/article-heading-ids.d.ts +7 -0
  123. package/dist/press-kit/articles/article-headings.d.ts +61 -0
  124. package/dist/press-kit/articles/html-tags.d.ts +46 -0
  125. package/dist/press-kit/articles/types.d.ts +2 -0
  126. package/dist/revalidate/index.js +3 -3
  127. package/dist/runtime/index.js +3 -3
  128. package/dist/seo/client.js +6 -6
  129. package/dist/seo/index.js +14 -13
  130. package/dist/seo/llms/contract.js +2 -1
  131. package/dist/seo/llms.js +9 -7
  132. package/dist/seo/meta/contract.js +2 -1
  133. package/dist/seo/register-sitemap-cli.js +1 -1
  134. package/dist/seo/server.js +5 -4
  135. package/dist/seo/sitemap.js +7 -5
  136. package/dist/seo-meta/contract.js +2 -1
  137. package/dist/server/index.js +3 -3
  138. package/dist/{server-api-EGOKC7Q3.js → server-api-XIKDWSNO.js} +2 -2
  139. package/dist/shared/build-entries.d.ts +3 -1
  140. package/dist/shared/markdown.d.ts +7 -6
  141. package/dist/shared/version.d.ts +1 -1
  142. package/dist/signal/index.js +2 -2
  143. package/dist/sitemap/index.js +7 -5
  144. package/dist/slots/index.js +2 -2
  145. package/dist/sync/index.js +5 -5
  146. package/dist/website/contract.js +2 -1
  147. package/dist/website/images.js +4 -4
  148. package/dist/website/index.js +10 -9
  149. package/dist/website/landing/server.js +19 -2
  150. package/dist/website/landing.js +4 -2
  151. package/dist/website/popups.js +9 -8
  152. package/dist/website/slots.js +2 -2
  153. package/dist/{writeLLMsTxt-BAK5XMRR.js → writeLLMsTxt-QN2HIVT4.js} +6 -4
  154. package/docs.json +5 -0
  155. package/package.json +1 -1
  156. package/src/analytics/README.md +1 -1
  157. package/src/forms/README.md +22 -0
  158. package/src/landing/README.md +64 -0
  159. package/src/llms/README.md +52 -20
  160. package/dist/FileField-F7BG4XAZ.js +0 -83
  161. package/dist/SiteChat-P4CRBRJZ.js +0 -6
  162. package/dist/SiteDesignReporter-QDVP4WMK.js +0 -12
  163. package/dist/SitemapSync-5FBRDGHG.js +0 -9
  164. package/dist/article-tables-6XHVULPU.js +0 -2
  165. package/dist/chunk-EKBEOXTH.js +0 -114
  166. package/dist/chunk-NZ4RXN3G.js +0 -145
  167. package/dist/chunk-OSYCOC3K.js +0 -21
  168. /package/dist/{chunk-7ZWO5HDQ.js → chunk-BTHEQDN4.js} +0 -0
  169. /package/dist/{chunk-BQR4IPBW.js → chunk-ISRBSTB7.js} +0 -0
  170. /package/dist/{chunk-IHG36STL.js → chunk-XHYE24HU.js} +0 -0
@@ -0,0 +1,420 @@
1
+ import { LandingPage } from './chunk-PAYCK7FC.js';
2
+ import { normalizeCampaignSlug } from './chunk-3MJNBOUO.js';
3
+ import { landingPageMetadata } from './chunk-JESLPEST.js';
4
+ import { ServerForm } from './chunk-DPYGC7WE.js';
5
+ import { serverApiFetch } from './chunk-C7TLGL3R.js';
6
+ import { normalizeSiteHost } from './chunk-A4CC4ZG3.js';
7
+ import 'server-only';
8
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
+
10
+ // src/contracts/public-url-host.ts
11
+ function isPrivateIpv4(a, b) {
12
+ return a === 127 || a === 10 || a === 0 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31 || a === 169 && b === 254 || a === 100 && b >= 64 && b <= 127 || a >= 224;
13
+ }
14
+ function ipv6Words(host) {
15
+ if (!host.includes(":") || !/^[a-f\d:.]+$/.test(host)) return null;
16
+ let canonical;
17
+ try {
18
+ canonical = new URL(`http://[${host}]/`).hostname.slice(1, -1);
19
+ } catch {
20
+ return null;
21
+ }
22
+ const sides = canonical.split("::");
23
+ const left = sides[0] ? sides[0].split(":") : [];
24
+ const right = sides.length > 1 && sides[1] ? sides[1].split(":") : [];
25
+ return [...left, ...Array(sides.length > 1 ? 8 - left.length - right.length : 0).fill("0"), ...right].map((word) => parseInt(word, 16));
26
+ }
27
+ function isBlockedPublicUrlHost(rawHost) {
28
+ const host = rawHost.toLowerCase().replace(/^\[|\]$/g, "");
29
+ const blockedName = host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal");
30
+ if (host.includes(":") && host.includes("%")) return true;
31
+ const ipv4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
32
+ const blockedIp = !!ipv4 && isPrivateIpv4(Number(ipv4[1]), Number(ipv4[2]));
33
+ const words = ipv6Words(host);
34
+ const blockedIpv6 = !!words && // Unspecified/loopback, unique-local, link-local, deprecated site-local
35
+ // and multicast destinations aren't public logo/webhook endpoints.
36
+ (words.every((word) => word === 0) || words.slice(0, 7).every((word) => word === 0) && words[7] === 1 || (words[0] & 65024) === 64512 || (words[0] & 65472) === 65152 || (words[0] & 65472) === 65216 || (words[0] & 65280) === 65280 || // IPv4-mapped and older IPv4-compatible forms carry an IPv4 destination.
37
+ words.slice(0, 5).every((word) => word === 0) && [0, 65535].includes(words[5]) && isPrivateIpv4(words[6] >> 8, words[6] & 255));
38
+ return blockedName || blockedIp || blockedIpv6;
39
+ }
40
+
41
+ // src/contracts/publication-branding.ts
42
+ var PUBLICATION_COLOR_FIELDS = ["primary", "secondary", "accent", "background", "surface", "text", "muted", "border"];
43
+ var GENERIC_FONT_FAMILIES = /* @__PURE__ */ new Set(["serif", "sans-serif", "monospace", "system-ui", "cursive", "fantasy", "ui-serif", "ui-sans-serif", "ui-monospace"]);
44
+ function containsControlCharacters(value) {
45
+ for (const character of value) {
46
+ const code = character.charCodeAt(0);
47
+ if (code <= 31 || code === 127) return true;
48
+ }
49
+ return false;
50
+ }
51
+ function safeFontStack(value) {
52
+ if (typeof value !== "string" || value.length > 512 || containsControlCharacters(value) || /[;{}<>\\]|(?:var|url|expression)\s*\(/i.test(value)) return void 0;
53
+ const names = value.split(",").map((v) => v.trim().replace(/^(['"])(.*)\1$/, "$2")).filter(Boolean).filter((v) => !/^(?:heading|body|mono)$/i.test(v));
54
+ if (!names.length || names.some((v) => !/^[\p{L}\p{N} _.-]+$/u.test(v))) return void 0;
55
+ return names.map((v) => GENERIC_FONT_FAMILIES.has(v.toLowerCase()) ? v.toLowerCase() : `'${v}'`).join(",");
56
+ }
57
+
58
+ // src/contracts/funnels.ts
59
+ var MANAGED_FUNNEL_PATH_PREFIX = "/f";
60
+ var FUNNEL_TEMPLATES = ["newsletter", "requested_guide", "inquiry"];
61
+ var string = (maxLength, minLength = 1) => ({ type: "string", minLength, maxLength });
62
+ var literal = (value) => ({ const: value });
63
+ var enumeration = (...values) => ({ type: "string", enum: values });
64
+ var object = (properties, required = Object.keys(properties)) => ({ type: "object", properties, required, additionalProperties: false });
65
+ var array = (items, maxItems, minItems = 0) => ({ type: "array", items, maxItems, minItems });
66
+ var integer = (minimum, maximum = Number.MAX_SAFE_INTEGER) => ({ type: "integer", minimum, maximum });
67
+ var nullable = (shape) => ({ oneOf: [shape, { type: "null" }] });
68
+ var isoDate = { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$" };
69
+ 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}$" };
70
+ var slug = { ...string(80), pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$" };
71
+ var fieldSlug = { ...string(80), pattern: "^[a-zA-Z0-9_-]+$" };
72
+ var path = { ...string(512), pattern: "^/(?!/)[a-zA-Z0-9/_-]*$" };
73
+ var site = { ...string(253), pattern: "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$" };
74
+ var color = { ...string(9), pattern: "^#[a-fA-F0-9]{6}$" };
75
+ var colors = object(Object.fromEntries(PUBLICATION_COLOR_FIELDS.map((key) => [key, color])));
76
+ var typography = object({ headingFamily: string(200), bodyFamily: string(200) });
77
+ var override = object({
78
+ displayName: nullable(string(120)),
79
+ tagline: nullable(string(300, 0)),
80
+ fullLogoAssetId: nullable(uuid),
81
+ squareLogoAssetId: nullable(uuid),
82
+ colors: object(Object.fromEntries(PUBLICATION_COLOR_FIELDS.map((key) => [key, nullable(color)])), []),
83
+ typography: object({ headingFamily: nullable(string(200)), bodyFamily: nullable(string(200)) }, [])
84
+ }, []);
85
+ var bodyBlock = { oneOf: [object({ type: literal("paragraph"), text: string(2e3) }), object({ type: literal("bullets"), items: array(string(500), 8, 1) })] };
86
+ var body = array(bodyBlock, 12);
87
+ var publicBrand = object({ displayName: string(120), tagline: string(300, 0), fullLogoUrl: nullable(string(2048)), squareLogoUrl: nullable(string(2048)), colors, typography, scheme: enumeration("light", "dark") });
88
+ var step = { oneOf: [
89
+ object({ id: uuid, type: literal("wait"), durationSeconds: integer(60, 2592e3) }),
90
+ object({ id: uuid, type: literal("send_email"), templateId: uuid, purpose: enumeration("requested_fulfillment", "marketing"), guideAssetId: nullable(uuid) }, ["id", "type", "templateId", "purpose"]),
91
+ object({ id: uuid, type: literal("add_tag"), target: literal("subscriber"), tag: string(80) }),
92
+ object({ id: uuid, type: literal("create_task"), ownerContactId: uuid, title: string(160), dueAfterSeconds: integer(0, 2592e3) })
93
+ ] };
94
+ var definitionShape = object({
95
+ schemaVersion: literal(1),
96
+ name: string(120),
97
+ template: enumeration(...FUNNEL_TEMPLATES),
98
+ site,
99
+ entry: object({ formId: uuid, mode: literal("future_accepted_form_only") }),
100
+ goal: object({ type: enumeration("newsletter_confirmed", "guide_requested", "inquiry_received", "booking_confirmed"), bookingTypeId: nullable(uuid) }, ["type"]),
101
+ 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"]),
102
+ permission: object({ mode: enumeration("requested_only", "marketing_opt_in"), optInFieldSlug: nullable(fieldSlug) }, ["mode"]),
103
+ brandOverride: override,
104
+ steps: array(step, 8),
105
+ exits: object({ unsubscribe: literal(true), manualStop: literal(true), confirmedBooking: object({ enabled: { type: "boolean" }, bookingTypeId: nullable(uuid) }) }, ["unsubscribe", "manualStop"])
106
+ }, ["schemaVersion", "name", "template", "site", "entry", "goal", "page", "permission", "steps", "exits"]);
107
+ var draftDefinitionShape = JSON.parse(JSON.stringify(definitionShape));
108
+ var draftProps = draftDefinitionShape.properties;
109
+ draftProps.name = string(120, 0);
110
+ draftProps.site = nullable(site);
111
+ draftProps.entry.properties.formId = nullable(uuid);
112
+ draftProps.page.properties.path = nullable(path);
113
+ draftProps.page.properties.formId = nullable(uuid);
114
+ draftProps.permission.properties.optInFieldSlug = nullable(fieldSlug);
115
+ draftProps.steps.items.oneOf = draftProps.steps.items.oneOf.map((item) => {
116
+ const fields = item.properties;
117
+ if (fields.templateId) fields.templateId = nullable(uuid);
118
+ if (fields.guideAssetId) fields.guideAssetId = nullable(uuid);
119
+ if (fields.ownerContactId) fields.ownerContactId = nullable(uuid);
120
+ if (fields.title) fields.title = string(160, 0);
121
+ if (fields.tag) fields.tag = string(80, 0);
122
+ return item;
123
+ });
124
+ 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);
125
+ var FUNNEL_DRAFT_DEFINITION_JSON_SCHEMA = { $schema: "http://json-schema.org/draft-07/schema#", ...draftDefinitionShape };
126
+ var FUNNEL_DEFINITION_JSON_SCHEMA = { $schema: "http://json-schema.org/draft-07/schema#", ...definitionShape };
127
+ var publishedShape = object({
128
+ schemaVersion: literal(1),
129
+ id: uuid,
130
+ versionId: uuid,
131
+ revision: integer(1),
132
+ site,
133
+ path,
134
+ content: object({ headline: string(160), body, offer: object({ label: string(120), href: string(2048) }) }, ["headline", "body"]),
135
+ brand: publicBrand,
136
+ conversion: object({ formSlug: fieldSlug }),
137
+ tracking: object({ campaignSlug: slug })
138
+ });
139
+ var PUBLISHED_FUNNEL_PAGE_JSON_SCHEMA = { $schema: "http://json-schema.org/draft-07/schema#", ...publishedShape };
140
+ var FUNNEL_LAUNCH_JSON_SCHEMA = object({ expectedRevision: integer(1), previewId: uuid, idempotencyKey: string(200), confirm: literal(true) });
141
+ var FUNNEL_LIFECYCLE_JSON_SCHEMA = object({ expectedActiveVersionId: uuid, idempotencyKey: string(200), confirm: literal(true) });
142
+ var FUNNEL_ROLLBACK_JSON_SCHEMA = object({ expectedActiveVersionId: uuid, targetVersionId: uuid, idempotencyKey: string(200), confirm: literal(true) });
143
+ var FunnelValidationError = class extends Error {
144
+ constructor(path2, reason) {
145
+ super(`${path2}: ${reason}`);
146
+ this.path = path2;
147
+ this.name = "FunnelValidationError";
148
+ }
149
+ };
150
+ function fail(path2, reason) {
151
+ throw new FunnelValidationError(path2, reason);
152
+ }
153
+ function check(value, shape, at) {
154
+ if (shape.oneOf) {
155
+ let matches = 0;
156
+ for (const candidate of shape.oneOf) {
157
+ try {
158
+ check(value, candidate, at);
159
+ matches++;
160
+ } catch (error) {
161
+ if (!(error instanceof FunnelValidationError)) throw error;
162
+ }
163
+ }
164
+ if (matches !== 1) fail(at, "must match one supported shape");
165
+ return;
166
+ }
167
+ if ("const" in shape && value !== shape.const) fail(at, "unexpected value");
168
+ if (shape.enum && !shape.enum.includes(value)) fail(at, "unsupported value");
169
+ if (shape.type === "null" && value !== null) fail(at, "must be null");
170
+ if (shape.type === "boolean" && typeof value !== "boolean") fail(at, "must be boolean");
171
+ if (shape.type === "string") {
172
+ if (typeof value !== "string") fail(at, "must be a string");
173
+ 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");
174
+ if (shape.pattern) {
175
+ const match = new RegExp(shape.pattern).exec(value);
176
+ if (!match || match[0] !== value) fail(at, "invalid format");
177
+ }
178
+ }
179
+ if (shape.type === "integer" && (typeof value !== "number" || !Number.isSafeInteger(value) || value < (shape.minimum ?? -Infinity) || value > (shape.maximum ?? Infinity))) fail(at, "invalid integer");
180
+ if (shape.type === "array") {
181
+ if (!Array.isArray(value) || value.length < (shape.minItems ?? 0) || value.length > (shape.maxItems ?? Infinity)) fail(at, "invalid array length");
182
+ value.forEach((item, i) => check(item, shape.items, `${at}[${i}]`));
183
+ }
184
+ if (shape.type === "object") {
185
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(at, "must be an object");
186
+ const row = value;
187
+ if (![Object.prototype, null].includes(Object.getPrototypeOf(row))) fail(at, "must be plain JSON");
188
+ for (const key of shape.required ?? []) if (!Object.prototype.hasOwnProperty.call(row, key)) fail(`${at}.${key}`, "required");
189
+ for (const key of Object.keys(row)) {
190
+ if (!Object.prototype.hasOwnProperty.call(shape.properties, key)) fail(`${at}.${key}`, "unknown field");
191
+ check(row[key], shape.properties[key], `${at}.${key}`);
192
+ }
193
+ }
194
+ }
195
+ function isSafeFunnelPublicUrl(value) {
196
+ try {
197
+ const url = new URL(value);
198
+ return value.startsWith("https://") && url.protocol === "https:" && !url.username && !url.password && (!url.port || url.port === "443") && !isBlockedPublicUrlHost(url.hostname) && !/[\s\x00-\x1f\\]/.test(value);
199
+ } catch {
200
+ return false;
201
+ }
202
+ }
203
+ function managedFunnelPath(value) {
204
+ check(value, slug, "slug");
205
+ if (normalizeCampaignSlug(value) !== value) fail("slug", "must be canonical");
206
+ return `${MANAGED_FUNNEL_PATH_PREFIX}/${value}`;
207
+ }
208
+ function checkFonts(value, at) {
209
+ for (const [key, font] of Object.entries(value.typography ?? {})) if (font !== null && safeFontStack(font) === void 0) fail(`${at}.typography.${key}`, "unsafe font stack");
210
+ }
211
+ function checkBody(blocks, at) {
212
+ 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");
213
+ }
214
+ function parsePublishedFunnelPage(value) {
215
+ check(value, publishedShape, "page");
216
+ const result = JSON.parse(JSON.stringify(value));
217
+ const suffix = result.path.slice(MANAGED_FUNNEL_PATH_PREFIX.length + 1);
218
+ if (managedFunnelPath(suffix) !== result.path) fail("page.path", "invalid managed route");
219
+ for (const key of ["fullLogoUrl", "squareLogoUrl"]) if (result.brand[key] && !isSafeFunnelPublicUrl(result.brand[key])) fail(`page.brand.${key}`, "unsafe public URL");
220
+ checkFonts(result.brand, "page.brand");
221
+ checkBody(result.content.body, "page.content.body");
222
+ if (result.content.offer && !isSafeFunnelPublicUrl(result.content.offer.href)) fail("page.content.offer.href", "unsafe public URL");
223
+ return result;
224
+ }
225
+ var FUNNEL_CREATE_JSON_SCHEMA = object({ idempotencyKey: string(200), definition: draftDefinitionShape });
226
+ var FUNNEL_UPDATE_JSON_SCHEMA = object({ expectedRevision: integer(1), idempotencyKey: string(200), definition: draftDefinitionShape });
227
+ var FUNNEL_REVISION_JSON_SCHEMA = object({ expectedRevision: integer(1) });
228
+ var FUNNEL_RETRY_JSON_SCHEMA = object({ expectedAttempt: integer(0, 100), idempotencyKey: string(200), confirm: literal(true) });
229
+ var FUNNEL_DRAFT_JSON_SCHEMAS = { create: FUNNEL_CREATE_JSON_SCHEMA, update: FUNNEL_UPDATE_JSON_SCHEMA };
230
+ var fingerprint = { type: "string", pattern: "^[a-f0-9]{64}$" };
231
+ 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"]);
232
+ 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" } }) });
233
+ 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 });
234
+ var FUNNEL_DETAIL_JSON_SCHEMA = funnelDetailShape;
235
+ var FUNNEL_OPERATION_RECEIPT_JSON_SCHEMA = object({
236
+ operationId: uuid,
237
+ projectId: uuid,
238
+ orgId: uuid,
239
+ funnelId: uuid,
240
+ operation: enumeration("create", "update", "launch", "pause", "resume", "retry", "rollback"),
241
+ idempotencyKey: string(200),
242
+ requestDigest: fingerprint,
243
+ versionId: nullable(uuid),
244
+ lifecycle: enumeration("draft", "active", "paused"),
245
+ replayed: { type: "boolean" },
246
+ armedFutureEntrantsOnly: literal(true),
247
+ enrolledExisting: literal(0),
248
+ messagesSent: literal(0),
249
+ pagePublication: enumeration("not_requested", "published", "revalidation_pending"),
250
+ health: enumeration("healthy", "waiting", "needs_attention", "unknown"),
251
+ revision: integer(1),
252
+ commandState: literal("committed"),
253
+ funnel: nullable(funnelDetailShape),
254
+ actor: string(200),
255
+ armedAt: nullable(isoDate),
256
+ effectScope: enumeration("configuration_only", "safe_retry_armed"),
257
+ target: object({ funnelId: nullable(uuid), enrollmentId: nullable(uuid), executionId: nullable(uuid), targetVersionId: nullable(uuid) }),
258
+ ledgerProposalId: uuid
259
+ });
260
+ var provenancePaths = ["displayName", "tagline", "fullLogoUrl", "squareLogoUrl", "scheme", ...PUBLICATION_COLOR_FIELDS.map((key) => `colors.${key}`), "typography.headingFamily", "typography.bodyFamily"];
261
+ var brandSnapshotShape = object({
262
+ page: publicBrand,
263
+ email: publicBrand,
264
+ 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")]))), []),
265
+ assetRefs: array(object({ id: nullable(uuid), version: nullable(string(128)) }), 32),
266
+ capturedAt: isoDate,
267
+ fingerprint
268
+ });
269
+ var FUNNEL_BRAND_SNAPSHOT_JSON_SCHEMA = brandSnapshotShape;
270
+ var choicePage = (item) => object({ items: array(item, 100), nextCursor: nullable(string(500)) });
271
+ var choiceFields = { id: uuid, label: string(120) };
272
+ var catalogShape = object({
273
+ templates: array(object({ id: enumeration(...FUNNEL_TEMPLATES), label: string(120), defaults: draftDefinitionShape }), 3),
274
+ sites: array(object({ host: site, label: string(200) }), 200),
275
+ brand: nullable(brandSnapshotShape),
276
+ choices: object({
277
+ 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) })),
278
+ lists: choicePage(object({ ...choiceFields, active: { type: "boolean" }, doubleOptIn: { type: "boolean" } })),
279
+ emailTemplates: choicePage(object({ ...choiceFields, subject: string(998, 0), updatedAt: nullable(isoDate) })),
280
+ assets: choicePage(object({ ...choiceFields, label: string(160), mimeType: string(100), sizeBytes: integer(0, 52428800), visibility: literal("public"), publicUrl: string(2048), version: nullable(string(200)) })),
281
+ owners: choicePage(object({ ...choiceFields, active: literal(true) })),
282
+ bookingTypes: choicePage(object({ ...choiceFields, site: nullable(site), active: literal(true), requiresConfirmation: { type: "boolean" } }))
283
+ }, []),
284
+ capabilities: object({ managedPage: { type: "boolean" } })
285
+ });
286
+ var FUNNEL_CATALOG_JSON_SCHEMA = catalogShape;
287
+ 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 });
288
+ var FUNNEL_LAUNCH_EVIDENCE_JSON_SCHEMA = launchEvidenceShape;
289
+ var FUNNEL_ROLLBACK_EVIDENCE_JSON_SCHEMA = object({ orgId: uuid, projectId: uuid, funnelId: uuid, targetVersionId: uuid, definitionRevision: integer(1), definitionDigest: fingerprint, brandFingerprint: fingerprint, resourceFingerprint: fingerprint });
290
+ var FUNNEL_PREVIEW_JSON_SCHEMA = object({
291
+ previewId: uuid,
292
+ versionId: uuid,
293
+ expiresAt: isoDate,
294
+ definitionRevision: integer(1),
295
+ brand: brandSnapshotShape,
296
+ readiness: FUNNEL_READINESS_JSON_SCHEMA,
297
+ page: nullable(publishedShape),
298
+ launchEvidence: launchEvidenceShape,
299
+ emailPreviews: array(object({ stepId: uuid, ready: { type: "boolean" }, subject: nullable(string(998, 0)), contentHtml: nullable(string(2e5, 0)), issues: array(issue, 50) }), 8)
300
+ });
301
+ var summaryShape = object({
302
+ id: uuid,
303
+ name: string(120, 0),
304
+ template: enumeration(...FUNNEL_TEMPLATES),
305
+ site: nullable(site),
306
+ revision: integer(1),
307
+ activeVersionId: nullable(uuid),
308
+ lifecycle: enumeration("draft", "active", "paused"),
309
+ health: enumeration("healthy", "waiting", "needs_attention", "unknown"),
310
+ hasDraftChanges: { type: "boolean" },
311
+ counts: object({ entered: integer(0), waiting: integer(0), running: integer(0), completed: integer(0), exited: integer(0), needsAttention: integer(0) }),
312
+ countsBasis: literal("all_versions_all_time"),
313
+ lastEventAt: nullable(isoDate)
314
+ });
315
+ var FUNNEL_SUMMARY_JSON_SCHEMA = summaryShape;
316
+ var FUNNEL_ENROLLMENT_JSON_SCHEMA = object({
317
+ id: uuid,
318
+ versionId: uuid,
319
+ state: enumeration("waiting", "running", "completed", "exited", "needs_attention"),
320
+ effectiveStatus: enumeration("paused", "waiting", "running", "completed", "exited", "needs_attention"),
321
+ source: object({ submissionId: uuid, acceptedAt: isoDate }),
322
+ subject: object({ kind: enumeration("subscriber", "contact", "submission"), id: uuid, displayLabel: string(200) }),
323
+ enteredAt: isoDate,
324
+ nextRunAt: nullable(isoDate),
325
+ exitCode: nullable(string(80)),
326
+ stepsCompleted: integer(0, 8),
327
+ stepsTotal: integer(0, 8),
328
+ lastReceiptAt: nullable(isoDate)
329
+ });
330
+ var FUNNEL_TIMELINE_JSON_SCHEMA = object({
331
+ 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),
332
+ nextCursor: nullable(string(500)),
333
+ exit: nullable(object({ code: string(80), sourceType: string(80), sourceId: nullable(uuid), occurredAt: isoDate }))
334
+ });
335
+ function freezeSchema(value) {
336
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) return;
337
+ for (const child of Object.values(value)) freezeSchema(child);
338
+ Object.freeze(value);
339
+ }
340
+ 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);
341
+ var FUNNEL_VERSION_JSON_SCHEMA = object({ versionId: uuid, definitionRevision: integer(1), publishedAt: isoDate, brandFingerprint: fingerprint, resourceFingerprint: fingerprint, active: { type: "boolean" } });
342
+ var FUNNEL_VERSION_DETAIL_JSON_SCHEMA = object({ ...FUNNEL_VERSION_JSON_SCHEMA.properties, rollbackEvidence: FUNNEL_ROLLBACK_EVIDENCE_JSON_SCHEMA });
343
+ 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);
344
+ function FunnelPageContent({ page: input, conversion, preview = false }) {
345
+ const page = parsePublishedFunnelPage(input);
346
+ const { brand, content } = page;
347
+ const tokens = {
348
+ "--sk-primary": brand.colors.primary,
349
+ "--sk-secondary": brand.colors.secondary,
350
+ "--sk-bg": brand.colors.background,
351
+ "--sk-surface": brand.colors.surface,
352
+ "--sk-text-primary": brand.colors.text,
353
+ "--sk-text-secondary": brand.colors.muted,
354
+ "--sk-border": brand.colors.border,
355
+ "--sk-font": brand.typography.bodyFamily,
356
+ colorScheme: brand.scheme,
357
+ color: "var(--sk-text-primary)",
358
+ background: "var(--sk-bg)",
359
+ fontFamily: "var(--sk-font)",
360
+ overflowWrap: "anywhere"
361
+ };
362
+ return /* @__PURE__ */ jsxs("article", { className: "sk-funnel", style: tokens, "data-sk-private-preview": preview ? "" : void 0, "data-sk-funnel-revision": page.revision, children: [
363
+ preview && /* @__PURE__ */ jsx("meta", { name: "robots", content: "noindex, nofollow" }),
364
+ /* @__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}` }),
365
+ /* @__PURE__ */ jsxs("div", { className: "sk-funnel-inner", children: [
366
+ /* @__PURE__ */ jsxs("header", { className: "sk-funnel-brand", children: [
367
+ brand.fullLogoUrl && /* @__PURE__ */ jsx("img", { src: brand.fullLogoUrl, alt: brand.displayName, width: 256, height: 80, fetchPriority: "high" }),
368
+ /* @__PURE__ */ jsxs("div", { children: [
369
+ !brand.fullLogoUrl && /* @__PURE__ */ jsx("strong", { children: brand.displayName }),
370
+ brand.tagline && /* @__PURE__ */ jsx("p", { children: brand.tagline })
371
+ ] })
372
+ ] }),
373
+ /* @__PURE__ */ jsxs("div", { className: "sk-funnel-grid", children: [
374
+ /* @__PURE__ */ jsxs("section", { "aria-label": content.headline, children: [
375
+ /* @__PURE__ */ jsx("h1", { style: { fontFamily: brand.typography.headingFamily }, children: content.headline }),
376
+ 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)),
377
+ 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 }) }))
378
+ ] }),
379
+ /* @__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 })
380
+ ] })
381
+ ] })
382
+ ] });
383
+ }
384
+ async function getPublishedFunnelPage(slug2, options) {
385
+ let path2;
386
+ try {
387
+ path2 = managedFunnelPath(slug2);
388
+ } catch (error) {
389
+ if (error instanceof FunnelValidationError) return null;
390
+ throw error;
391
+ }
392
+ const site2 = normalizeSiteHost(options.site);
393
+ if (!site2 || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/.test(site2)) throw new Error("An explicit canonical registered site is required");
394
+ const data = await serverApiFetch(`/api/public/funnels/${encodeURIComponent(slug2)}?site=${encodeURIComponent(site2)}`, {
395
+ apiKey: options.apiKey,
396
+ apiUrl: options.apiUrl,
397
+ revalidate: 0,
398
+ init: { cache: "no-store" },
399
+ logPrefix: "@sonordev/site-kit/landing"
400
+ });
401
+ if (data === null) return null;
402
+ const page = parsePublishedFunnelPage(data);
403
+ if (page.site !== site2 || page.path !== path2) throw new Error("Published funnel page scope mismatch");
404
+ return page;
405
+ }
406
+ function managedFunnelMetadata(page) {
407
+ const safe = parsePublishedFunnelPage(page);
408
+ return landingPageMetadata({ title: safe.content.headline, alternates: { canonical: `https://${safe.site}${safe.path}` } });
409
+ }
410
+ async function ManagedFunnelPage({ page: input, preview = false, configOptions }) {
411
+ const page = parsePublishedFunnelPage(input);
412
+ if (preview) return /* @__PURE__ */ jsx(FunnelPageContent, { page, preview: true });
413
+ const form = await ServerForm({ formId: page.conversion.formSlug, defer: false, enter: "none", configOptions });
414
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
415
+ /* @__PURE__ */ jsx(LandingPage, { campaign: page.tracking.campaignSlug, robotsMeta: false }),
416
+ /* @__PURE__ */ jsx(FunnelPageContent, { page, conversion: form })
417
+ ] });
418
+ }
419
+
420
+ export { FunnelPageContent, ManagedFunnelPage, getPublishedFunnelPage, managedFunnelMetadata };
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
  import { shellValuesToCarry } from './chunk-C2FZBUSS.js';
3
3
  import { useDeferredActivation } from './chunk-YC7ELZS3.js';
4
- import { normalizeFormConfig } from './chunk-5SQK3D53.js';
4
+ import { normalizeFormConfig } from './chunk-TYSIAWC3.js';
5
5
  import { DEFAULT_HONEYPOT_FIELD } from './chunk-NJCTQH2P.js';
6
6
  import React, { useRef, useEffect, useState, useMemo, useCallback } from 'react';
7
7
  import { jsx, jsxs } from 'react/jsx-runtime';
8
8
 
9
- var loadInteractiveForm = () => import('./ManagedForm-4AACRHEY.js').then((m) => m.ManagedForm);
9
+ var loadInteractiveForm = () => import('./ManagedForm-GJRGBM6W.js').then((m) => m.ManagedForm);
10
10
  var REPLAY_WAIT_MS = 8e3;
11
11
  function FormEnhancer({
12
12
  formId,
@@ -1,4 +1,4 @@
1
- import { generateLLMsTxt, generateLLMsFullTxt, appendAgentAccessSection } from './chunk-URZTBPZO.js';
1
+ import { generateLLMsTxt, generateLLMsFullTxt, appendAgentAccessSection } from './chunk-XZFOQ2V4.js';
2
2
  import { createHash } from 'crypto';
3
3
  import { existsSync, readFileSync } from 'fs';
4
4
  import { join } from 'path';
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { sonorFetch } from './chunk-GDQKDL3E.js';
2
+ import { sonorFetch } from './chunk-UYOE3HA4.js';
3
3
  import { useState, useCallback, useEffect } from 'react';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
 
@@ -12,7 +12,9 @@ var SITE_CACHE_TAGS = {
12
12
  /** Portfolio items (agency-site-kit). */
13
13
  portfolio: "portfolio",
14
14
  /** Products, services, events and membership plans (commerce-kit). */
15
- commerce: "commerce"
15
+ commerce: "commerce",
16
+ /** Published managed landing pages. Draft previews never enter this cache. */
17
+ funnels: "sonor-funnels"
16
18
  };
17
19
  function isSiteRevalidatePong(body) {
18
20
  return typeof body === "object" && body !== null && body.ok === true && body.ping === true;
@@ -0,0 +1,27 @@
1
+ // packages/press-kit/src/articles/html-tags.ts
2
+ var blank = "\\t\\n\\f\\r ";
3
+ var tagName = `[a-z][^${blank}/>]*(?![^${blank}/>])`;
4
+ var attributeName = `[^${blank}/>][^${blank}/>=]*(?![^${blank}/>=])`;
5
+ var attributeValue = `"[^"]*"|'[^']*'|[^${blank}>"'][^${blank}>]*(?![^${blank}>])|(?=>)`;
6
+ var attribute = `${attributeName}(?:[${blank}]*=[${blank}]*(?:${attributeValue})|(?![${blank}]*=))`;
7
+ var tagBody = `(?:[${blank}/]|${attribute})*>`;
8
+ var comments = "<!--(?:-?>|[^]*?(?:--!?>|$))|<[!?][^>]*(?:>|$)|<\\/(?:>|[^a-z][^>]*(?:>|$))";
9
+ var tokens = new RegExp(`${comments}|<\\/?${tagName}${tagBody}|<\\/?[a-z][^]*`, "gi");
10
+ var completeTag = new RegExp(`^<\\/?(${tagName})${tagBody}$`, "i");
11
+ function htmlTags() {
12
+ return new RegExp(tokens);
13
+ }
14
+ function htmlTagName(token) {
15
+ return completeTag.exec(token)?.[1].toLowerCase() ?? "";
16
+ }
17
+ function isClosingTag(tag) {
18
+ return tag.startsWith("</");
19
+ }
20
+ function openTagAttributes(tag) {
21
+ return tag.slice(1 + htmlTagName(tag).length, -1);
22
+ }
23
+ function stripHtmlTags(html) {
24
+ return html.replace(htmlTags(), "");
25
+ }
26
+
27
+ export { htmlTagName, htmlTags, isClosingTag, openTagAttributes, stripHtmlTags };
@@ -1,8 +1,8 @@
1
- import { toHexColor, normalizeDesignTokens, designTokensHash } from './chunk-IHG36STL.js';
1
+ import { toHexColor, normalizeDesignTokens, designTokensHash } from './chunk-XHYE24HU.js';
2
2
  import { contrastRatio, parseColor, relativeLuminance } from './chunk-S22FSH7C.js';
3
- import { resolveAnalyticsTarget } from './chunk-DLM4FFNH.js';
4
- import { sonorFetch } from './chunk-GDQKDL3E.js';
5
- import { SITE_KIT_VERSION } from './chunk-CFSIGEUW.js';
3
+ import { resolveAnalyticsTarget } from './chunk-QTBAKOCZ.js';
4
+ import { sonorFetch } from './chunk-UYOE3HA4.js';
5
+ import { SITE_KIT_VERSION } from './chunk-MNORERMF.js';
6
6
 
7
7
  // src/website/design.ts
8
8
  var SITE_DESIGN_PATH = "/api/public/site/design";
@@ -0,0 +1,14 @@
1
+ // src/landing/metadata.ts
2
+ function landingPageMetadata(meta = {}) {
3
+ const callerRobots = meta.robots && typeof meta.robots === "object" ? meta.robots : void 0;
4
+ return {
5
+ ...meta,
6
+ robots: {
7
+ index: false,
8
+ follow: false,
9
+ ...callerRobots
10
+ }
11
+ };
12
+ }
13
+
14
+ export { landingPageMetadata };
@@ -1,3 +1,4 @@
1
+ import { htmlTags, htmlTagName, isClosingTag, openTagAttributes, stripHtmlTags } from './chunk-IBHBEUZV.js';
1
2
  import { createPublicationRoutes } from './chunk-4K735O3J.js';
2
3
  import { resolveTrailingSlash, applyTrailingSlash } from './chunk-YVKRYQRG.js';
3
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -344,4 +345,60 @@ function readingTimeMinutes(post) {
344
345
  return typeof value === "number" && value > 0 ? value : null;
345
346
  }
346
347
 
347
- export { ClusterNavigation, ServiceCallout, ServiceCallouts, getAuthorSocialLinks, readingTimeMinutes };
348
+ // packages/press-kit/src/articles/article-headings.ts
349
+ function openTagAttribute(attributes, name) {
350
+ const wanted = name.toLowerCase();
351
+ const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'<>=`]+)))?/g;
352
+ let match;
353
+ while (match = attribute.exec(attributes)) {
354
+ if (match[1].toLowerCase() === wanted) return match[2] ?? match[3] ?? match[4] ?? "";
355
+ }
356
+ return null;
357
+ }
358
+ function slugify(text) {
359
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
360
+ }
361
+ function scanHeadings(html) {
362
+ const tags = htmlTags();
363
+ const lineBreak = /[\n\r\u2028\u2029]/g;
364
+ const headings = [];
365
+ let waiting = [];
366
+ let nextBreak = -1;
367
+ let token;
368
+ while (token = tags.exec(html)) {
369
+ const level = /^h([2-4])$/.exec(htmlTagName(token[0]))?.[1];
370
+ if (!level) continue;
371
+ if (!isClosingTag(token[0])) {
372
+ waiting.push({ level: Number(level), attributes: openTagAttributes(token[0]), start: token.index, innerStart: tags.lastIndex });
373
+ continue;
374
+ }
375
+ for (const open of waiting) {
376
+ if (nextBreak < open.innerStart) {
377
+ lineBreak.lastIndex = open.innerStart;
378
+ nextBreak = lineBreak.exec(html)?.index ?? Infinity;
379
+ }
380
+ if (nextBreak < token.index) continue;
381
+ headings.push({
382
+ level: open.level,
383
+ attributes: open.attributes,
384
+ inner: html.slice(open.innerStart, token.index),
385
+ start: open.start,
386
+ end: tags.lastIndex
387
+ });
388
+ break;
389
+ }
390
+ waiting = [];
391
+ }
392
+ return headings;
393
+ }
394
+ function ownId(attributes) {
395
+ return openTagAttribute(attributes, "id");
396
+ }
397
+ function generateTableOfContents(html) {
398
+ return scanHeadings(html).map(({ level, attributes, inner }) => {
399
+ const text = stripHtmlTags(inner);
400
+ return { id: ownId(attributes) || slugify(text), text, level };
401
+ });
402
+ }
403
+
404
+ export { ClusterNavigation, ServiceCallout, ServiceCallouts, generateTableOfContents, getAuthorSocialLinks, ownId, readingTimeMinutes, scanHeadings, slugify };
@@ -1,5 +1,5 @@
1
1
  import { resolveSiteHost } from './chunk-FESKKJAS.js';
2
- import { configureSiteCredential, currentCredential } from './chunk-OEA7U4HJ.js';
2
+ import { configureSiteCredential, currentCredential } from './chunk-P5PRR2BB.js';
3
3
 
4
4
  // src/shared/publishCredential.ts
5
5
  function publishSiteCredential({