@sonordev/site-kit 5.8.3 → 6.0.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 (537) hide show
  1. package/README.md +11 -11
  2. package/agent-manifest.json +17 -11
  3. package/dist/{AnalyticsProvider-IGOD64MY.mjs → AnalyticsProvider-6QVM2B6O.mjs} +6 -6
  4. package/dist/{AnalyticsProvider-IGOD64MY.mjs.map → AnalyticsProvider-6QVM2B6O.mjs.map} +1 -1
  5. package/dist/{AnalyticsProvider-DPXR6FSV.js → AnalyticsProvider-AZQXLRWN.js} +10 -10
  6. package/dist/{AnalyticsProvider-DPXR6FSV.js.map → AnalyticsProvider-AZQXLRWN.js.map} +1 -1
  7. package/dist/ArticleFaqSection-3KZ24MFS.js +13 -0
  8. package/dist/ArticleFaqSection-3KZ24MFS.js.map +1 -0
  9. package/dist/ArticleFaqSection-XZPXJVH4.mjs +11 -0
  10. package/dist/ArticleFaqSection-XZPXJVH4.mjs.map +1 -0
  11. package/dist/{BlogViewTracker-Q7Y4MN4B.mjs → ArticleViewTracker-AEWJMXQQ.mjs} +13 -13
  12. package/dist/ArticleViewTracker-AEWJMXQQ.mjs.map +1 -0
  13. package/dist/{BlogViewTracker-S3CSRBIZ.js → ArticleViewTracker-CFBTQ46B.js} +16 -16
  14. package/dist/ArticleViewTracker-CFBTQ46B.js.map +1 -0
  15. package/dist/ChatWidget-6626OPXO.mjs +15 -0
  16. package/dist/{ChatWidget-G3KOUDX5.mjs.map → ChatWidget-6626OPXO.mjs.map} +1 -1
  17. package/dist/ChatWidget-U24AYLXF.js +24 -0
  18. package/dist/{ChatWidget-76R7NOWD.js.map → ChatWidget-U24AYLXF.js.map} +1 -1
  19. package/dist/EngageWidget-TR47W2ZI.mjs +12 -0
  20. package/dist/{EngageWidget-UJ47PQFG.mjs.map → EngageWidget-TR47W2ZI.mjs.map} +1 -1
  21. package/dist/EngageWidget-XUTDFRF3.js +21 -0
  22. package/dist/{EngageWidget-HD4F2ZVE.js.map → EngageWidget-XUTDFRF3.js.map} +1 -1
  23. package/dist/{FileField-WNMEA3PK.mjs → FileField-DNNC5IPS.mjs} +5 -5
  24. package/dist/{FileField-WNMEA3PK.mjs.map → FileField-DNNC5IPS.mjs.map} +1 -1
  25. package/dist/{FileField-FV6WSZMX.js → FileField-R4RQG77G.js} +6 -6
  26. package/dist/{FileField-FV6WSZMX.js.map → FileField-R4RQG77G.js.map} +1 -1
  27. package/dist/{FormSpotlight-2VRQXEFI.js → FormSpotlight-7ILRAD3W.js} +9 -9
  28. package/dist/{FormSpotlight-2VRQXEFI.js.map → FormSpotlight-7ILRAD3W.js.map} +1 -1
  29. package/dist/{FormSpotlight-NGWEPGI4.mjs → FormSpotlight-MXY4I46H.mjs} +3 -3
  30. package/dist/{FormSpotlight-NGWEPGI4.mjs.map → FormSpotlight-MXY4I46H.mjs.map} +1 -1
  31. package/dist/{FormStage-Q3KQVFVW.js → FormStage-CVD3I3SE.js} +9 -9
  32. package/dist/{FormStage-Q3KQVFVW.js.map → FormStage-CVD3I3SE.js.map} +1 -1
  33. package/dist/{FormStage-7L4BBOLC.mjs → FormStage-FDJGRGEU.mjs} +3 -3
  34. package/dist/{FormStage-7L4BBOLC.mjs.map → FormStage-FDJGRGEU.mjs.map} +1 -1
  35. package/dist/ManagedForm-5675CLWS.mjs +16 -0
  36. package/dist/{ManagedForm-Z7QE4KYI.mjs.map → ManagedForm-5675CLWS.mjs.map} +1 -1
  37. package/dist/ManagedForm-KYIK54SL.js +25 -0
  38. package/dist/{ManagedForm-4ZGU4R3G.js.map → ManagedForm-KYIK54SL.js.map} +1 -1
  39. package/dist/{ManagedNewsletterForm-4TVO2VL4.mjs → ManagedNewsletterForm-DFLDGNQU.mjs} +11 -11
  40. package/dist/ManagedNewsletterForm-DFLDGNQU.mjs.map +1 -0
  41. package/dist/{ManagedNewsletterForm-22EPZB7Z.js → ManagedNewsletterForm-IH3MRJOD.js} +13 -13
  42. package/dist/ManagedNewsletterForm-IH3MRJOD.js.map +1 -0
  43. package/dist/{SignalCore-2N7UZGF3.js → SignalCore-6LYIZFXK.js} +11 -11
  44. package/dist/{SignalCore-2N7UZGF3.js.map → SignalCore-6LYIZFXK.js.map} +1 -1
  45. package/dist/{SignalCore-57OK3DHA.mjs → SignalCore-ZH3R6XD3.mjs} +5 -5
  46. package/dist/{SignalCore-57OK3DHA.mjs.map → SignalCore-ZH3R6XD3.mjs.map} +1 -1
  47. package/dist/SitemapSync-PKBXWVI6.mjs +9 -0
  48. package/dist/{SitemapSync-WIUEYOBS.mjs.map → SitemapSync-PKBXWVI6.mjs.map} +1 -1
  49. package/dist/SitemapSync-PZ76V5MX.js +18 -0
  50. package/dist/{SitemapSync-GF4OKN4Z.js.map → SitemapSync-PZ76V5MX.js.map} +1 -1
  51. package/dist/analytics/index.js +9 -9
  52. package/dist/analytics/index.mjs +4 -4
  53. package/dist/{blog-styles-7AKRUMV7.mjs → article-styles-DACUAOVT.mjs} +124 -124
  54. package/dist/article-styles-DACUAOVT.mjs.map +1 -0
  55. package/dist/{blog-styles-6EY3BS5Y.js → article-styles-JHHTNHOK.js} +125 -125
  56. package/dist/article-styles-JHHTNHOK.js.map +1 -0
  57. package/dist/article-tables-EWXG4LNP.js +17 -0
  58. package/dist/article-tables-EWXG4LNP.js.map +1 -0
  59. package/dist/article-tables-OGE52LNT.mjs +4 -0
  60. package/dist/article-tables-OGE52LNT.mjs.map +1 -0
  61. package/dist/{blog/BlogPost.d.ts → articles/Article.d.ts} +18 -16
  62. package/dist/articles/Article.d.ts.map +1 -0
  63. package/dist/{blog/BlogFAQ.d.ts → articles/ArticleFAQ.d.ts} +6 -6
  64. package/dist/articles/ArticleFAQ.d.ts.map +1 -0
  65. package/dist/articles/ArticleFaqSection.d.ts +3 -0
  66. package/dist/articles/ArticleFaqSection.d.ts.map +1 -0
  67. package/dist/{blog/BlogList.d.ts → articles/ArticleList.d.ts} +16 -16
  68. package/dist/articles/ArticleList.d.ts.map +1 -0
  69. package/dist/{blog/BlogViewTracker.d.ts → articles/ArticleViewTracker.d.ts} +4 -4
  70. package/dist/articles/ArticleViewTracker.d.ts.map +1 -0
  71. package/dist/{blog → articles}/AuthorCard.d.ts +3 -3
  72. package/dist/articles/AuthorCard.d.ts.map +1 -0
  73. package/dist/{blog → articles}/AuthorPage.d.ts +1 -1
  74. package/dist/articles/AuthorPage.d.ts.map +1 -0
  75. package/dist/{blog → articles}/ClusterLandingPage.d.ts +6 -6
  76. package/dist/articles/ClusterLandingPage.d.ts.map +1 -0
  77. package/dist/{blog → articles}/ClusterNavigation.d.ts +5 -5
  78. package/dist/articles/ClusterNavigation.d.ts.map +1 -0
  79. package/dist/{blog → articles}/ManagedNewsletterForm.d.ts +1 -1
  80. package/dist/articles/ManagedNewsletterForm.d.ts.map +1 -0
  81. package/dist/{blog → articles}/NewsletterShell.d.ts +1 -1
  82. package/dist/articles/NewsletterShell.d.ts.map +1 -0
  83. package/dist/{blog → articles}/NewsletterWidget.d.ts +4 -4
  84. package/dist/articles/NewsletterWidget.d.ts.map +1 -0
  85. package/dist/{blog/BlogLayout.d.ts → articles/PublicationLayout.d.ts} +18 -18
  86. package/dist/articles/PublicationLayout.d.ts.map +1 -0
  87. package/dist/{blog/BlogSidebar.d.ts → articles/PublicationSidebar.d.ts} +8 -8
  88. package/dist/articles/PublicationSidebar.d.ts.map +1 -0
  89. package/dist/{blog → articles}/RelatedPosts.d.ts +6 -6
  90. package/dist/articles/RelatedPosts.d.ts.map +1 -0
  91. package/dist/{blog → articles}/ServiceCallout.d.ts +4 -4
  92. package/dist/articles/ServiceCallout.d.ts.map +1 -0
  93. package/dist/{blog → articles}/TableOfContents.d.ts +1 -1
  94. package/dist/articles/TableOfContents.d.ts.map +1 -0
  95. package/dist/{blog/blog-styles.d.ts → articles/article-styles.d.ts} +5 -5
  96. package/dist/articles/article-styles.d.ts.map +1 -0
  97. package/dist/{blog/blog-tables.d.ts → articles/article-tables.d.ts} +4 -4
  98. package/dist/articles/article-tables.d.ts.map +1 -0
  99. package/dist/articles/artwork.d.ts +14 -0
  100. package/dist/articles/artwork.d.ts.map +1 -0
  101. package/dist/{blog → articles}/author-schema.d.ts +6 -5
  102. package/dist/articles/author-schema.d.ts.map +1 -0
  103. package/dist/{blog → articles}/author-social.d.ts +2 -2
  104. package/dist/articles/author-social.d.ts.map +1 -0
  105. package/dist/{blog → articles}/excerpt.d.ts +2 -2
  106. package/dist/articles/excerpt.d.ts.map +1 -0
  107. package/dist/{blog → articles}/index.d.ts +26 -31
  108. package/dist/articles/index.d.ts.map +1 -0
  109. package/dist/{blog → articles}/index.js +39 -73
  110. package/dist/articles/index.js.map +1 -0
  111. package/dist/{blog → articles}/index.mjs +16 -17
  112. package/dist/articles/index.mjs.map +1 -0
  113. package/dist/articles/news-sitemap.d.ts +47 -0
  114. package/dist/articles/news-sitemap.d.ts.map +1 -0
  115. package/dist/{blog/processBlogHtml.d.ts → articles/processArticleHtml.d.ts} +4 -4
  116. package/dist/articles/processArticleHtml.d.ts.map +1 -0
  117. package/dist/articles/public-reads.d.ts +27 -0
  118. package/dist/articles/public-reads.d.ts.map +1 -0
  119. package/dist/{blog → articles}/reading-time.d.ts +2 -2
  120. package/dist/articles/reading-time.d.ts.map +1 -0
  121. package/dist/{blog → articles}/routes.d.ts +16 -12
  122. package/dist/articles/routes.d.ts.map +1 -0
  123. package/dist/{blog → articles}/server-core.d.ts +94 -81
  124. package/dist/articles/server-core.d.ts.map +1 -0
  125. package/dist/articles/server-ui.d.ts +29 -0
  126. package/dist/articles/server-ui.d.ts.map +1 -0
  127. package/dist/{chunk-W2U5SLMY.js → articles/server-ui.js} +378 -499
  128. package/dist/articles/server-ui.js.map +1 -0
  129. package/dist/{chunk-MWKI6ZIS.mjs → articles/server-ui.mjs} +328 -474
  130. package/dist/articles/server-ui.mjs.map +1 -0
  131. package/dist/{blog → articles}/server.d.ts +94 -81
  132. package/dist/articles/server.d.ts.map +1 -0
  133. package/dist/articles/server.js +236 -0
  134. package/dist/articles/server.mjs +11 -0
  135. package/dist/{blog → articles}/types.d.ts +34 -34
  136. package/dist/articles/types.d.ts.map +1 -0
  137. package/dist/articles/widget-styles.d.ts +9 -0
  138. package/dist/articles/widget-styles.d.ts.map +1 -0
  139. package/dist/brand/extract-brand-profile.d.ts +1 -1
  140. package/dist/{chunk-PIEHBSCN.js → chunk-254IW4FC.js} +9 -9
  141. package/dist/{chunk-PIEHBSCN.js.map → chunk-254IW4FC.js.map} +1 -1
  142. package/dist/chunk-2KRAY5NC.mjs +6 -0
  143. package/dist/{chunk-5FETEH3Y.mjs.map → chunk-2KRAY5NC.mjs.map} +1 -1
  144. package/dist/{chunk-MQS6AF5K.mjs → chunk-2WEBUZNP.mjs} +3 -3
  145. package/dist/{chunk-MQS6AF5K.mjs.map → chunk-2WEBUZNP.mjs.map} +1 -1
  146. package/dist/{chunk-4C5CZJIN.mjs → chunk-2YSB4FIC.mjs} +4 -4
  147. package/dist/{chunk-4C5CZJIN.mjs.map → chunk-2YSB4FIC.mjs.map} +1 -1
  148. package/dist/{chunk-I7HOMMIK.js → chunk-3GHPFLEH.js} +15 -15
  149. package/dist/{chunk-I7HOMMIK.js.map → chunk-3GHPFLEH.js.map} +1 -1
  150. package/dist/{chunk-CY2RFEW6.js → chunk-3LCJJ6Y6.js} +3 -3
  151. package/dist/{chunk-CY2RFEW6.js.map → chunk-3LCJJ6Y6.js.map} +1 -1
  152. package/dist/{chunk-QOOLMSBY.mjs → chunk-3OCXVTN5.mjs} +3 -3
  153. package/dist/{chunk-QOOLMSBY.mjs.map → chunk-3OCXVTN5.mjs.map} +1 -1
  154. package/dist/{chunk-AJBOEV4I.mjs → chunk-3T2CVHFO.mjs} +3 -3
  155. package/dist/chunk-3T2CVHFO.mjs.map +1 -0
  156. package/dist/{chunk-NR7YZQRR.js → chunk-3U5SVB4W.js} +4 -4
  157. package/dist/{chunk-NR7YZQRR.js.map → chunk-3U5SVB4W.js.map} +1 -1
  158. package/dist/{chunk-I3ND4XYW.js → chunk-4BEGKFMN.js} +8 -8
  159. package/dist/{chunk-I3ND4XYW.js.map → chunk-4BEGKFMN.js.map} +1 -1
  160. package/dist/{chunk-YFX3Q56O.mjs → chunk-4KAMU5HD.mjs} +3 -3
  161. package/dist/chunk-4KAMU5HD.mjs.map +1 -0
  162. package/dist/{chunk-ZPLN537Q.mjs → chunk-4WCA2HM6.mjs} +2 -2
  163. package/dist/chunk-4WCA2HM6.mjs.map +1 -0
  164. package/dist/{chunk-6CCS2EGF.js → chunk-5DSUPTBU.js} +13 -13
  165. package/dist/{chunk-6CCS2EGF.js.map → chunk-5DSUPTBU.js.map} +1 -1
  166. package/dist/{chunk-GHECYDSZ.mjs → chunk-5F2FIM5A.mjs} +16 -15
  167. package/dist/chunk-5F2FIM5A.mjs.map +1 -0
  168. package/dist/{chunk-Y5QZPUL3.mjs → chunk-5YVQWLSQ.mjs} +5 -5
  169. package/dist/chunk-5YVQWLSQ.mjs.map +1 -0
  170. package/dist/chunk-6DCAMW62.mjs +55 -0
  171. package/dist/chunk-6DCAMW62.mjs.map +1 -0
  172. package/dist/{chunk-Q2NBD54V.js → chunk-7THQ4UW3.js} +2 -2
  173. package/dist/chunk-7THQ4UW3.js.map +1 -0
  174. package/dist/{chunk-LGOQ65JQ.mjs → chunk-7TKSRUQU.mjs} +7 -7
  175. package/dist/{chunk-LGOQ65JQ.mjs.map → chunk-7TKSRUQU.mjs.map} +1 -1
  176. package/dist/{chunk-4TLFZ6NR.mjs → chunk-AS2HUIDW.mjs} +3 -3
  177. package/dist/chunk-AS2HUIDW.mjs.map +1 -0
  178. package/dist/{chunk-YN7QHLWF.js → chunk-BBBJHYPH.js} +5 -5
  179. package/dist/chunk-BBBJHYPH.js.map +1 -0
  180. package/dist/{chunk-2TS4ALOA.js → chunk-BZG34WHF.js} +14 -14
  181. package/dist/{chunk-2TS4ALOA.js.map → chunk-BZG34WHF.js.map} +1 -1
  182. package/dist/{chunk-4LYW6PMU.mjs → chunk-C2CTLLIO.mjs} +3 -3
  183. package/dist/chunk-C2CTLLIO.mjs.map +1 -0
  184. package/dist/{chunk-O237KKCX.mjs → chunk-C5VITBN7.mjs} +5 -5
  185. package/dist/chunk-C5VITBN7.mjs.map +1 -0
  186. package/dist/{chunk-75A4KY4X.mjs → chunk-CFRJ2MK5.mjs} +2 -2
  187. package/dist/chunk-CFRJ2MK5.mjs.map +1 -0
  188. package/dist/{chunk-G2WC3CXW.js → chunk-CLO3K7PF.js} +2 -2
  189. package/dist/chunk-CLO3K7PF.js.map +1 -0
  190. package/dist/{chunk-SFC3OWSE.mjs → chunk-CSSNZGTU.mjs} +4 -4
  191. package/dist/{chunk-SFC3OWSE.mjs.map → chunk-CSSNZGTU.mjs.map} +1 -1
  192. package/dist/{chunk-7VZYP4K5.js → chunk-CZGGUPPQ.js} +15 -15
  193. package/dist/{chunk-7VZYP4K5.js.map → chunk-CZGGUPPQ.js.map} +1 -1
  194. package/dist/{chunk-FTB5LAD3.js → chunk-D47W5DE4.js} +4 -4
  195. package/dist/{chunk-FTB5LAD3.js.map → chunk-D47W5DE4.js.map} +1 -1
  196. package/dist/{chunk-AK6JZ3OE.mjs → chunk-EDLNMGQC.mjs} +3 -3
  197. package/dist/{chunk-DO6ATNLN.js.map → chunk-EDLNMGQC.mjs.map} +1 -1
  198. package/dist/chunk-EL74YDNX.js +8 -0
  199. package/dist/{chunk-7R6H3GQ4.js.map → chunk-EL74YDNX.js.map} +1 -1
  200. package/dist/{chunk-HX6ZG3PP.js → chunk-EMHSHAVW.js} +3 -3
  201. package/dist/chunk-EMHSHAVW.js.map +1 -0
  202. package/dist/{chunk-7TC3GRXQ.js → chunk-G6KQ3C3W.js} +25 -23
  203. package/dist/chunk-G6KQ3C3W.js.map +1 -0
  204. package/dist/{chunk-HEOLGDUX.mjs → chunk-GQ3SDRL6.mjs} +4 -4
  205. package/dist/{chunk-HEOLGDUX.mjs.map → chunk-GQ3SDRL6.mjs.map} +1 -1
  206. package/dist/{chunk-IIHIRHSW.js → chunk-HFCQGSD2.js} +18 -18
  207. package/dist/{chunk-IIHIRHSW.js.map → chunk-HFCQGSD2.js.map} +1 -1
  208. package/dist/{chunk-XZO5I4RS.mjs → chunk-HIZJD7TU.mjs} +3 -3
  209. package/dist/{chunk-XZO5I4RS.mjs.map → chunk-HIZJD7TU.mjs.map} +1 -1
  210. package/dist/{chunk-UQY5KC64.mjs → chunk-HOOVYWTO.mjs} +4 -4
  211. package/dist/{chunk-UQY5KC64.mjs.map → chunk-HOOVYWTO.mjs.map} +1 -1
  212. package/dist/{chunk-NBT4L2D7.js → chunk-HX5JPNMN.js} +8 -8
  213. package/dist/{chunk-NBT4L2D7.js.map → chunk-HX5JPNMN.js.map} +1 -1
  214. package/dist/chunk-HXHRS3PX.mjs +349 -0
  215. package/dist/chunk-HXHRS3PX.mjs.map +1 -0
  216. package/dist/{chunk-XBX2K3GA.js → chunk-IBJOGXQI.js} +4 -4
  217. package/dist/{chunk-XBX2K3GA.js.map → chunk-IBJOGXQI.js.map} +1 -1
  218. package/dist/{chunk-3OO5EDKD.mjs → chunk-JMNBU2RG.mjs} +9 -9
  219. package/dist/{chunk-3OO5EDKD.mjs.map → chunk-JMNBU2RG.mjs.map} +1 -1
  220. package/dist/{chunk-R7GHP3FY.js → chunk-JONWE26J.js} +3 -3
  221. package/dist/chunk-JONWE26J.js.map +1 -0
  222. package/dist/{chunk-PQYQHZWV.mjs → chunk-JSXLTZUP.mjs} +6 -6
  223. package/dist/{chunk-PQYQHZWV.mjs.map → chunk-JSXLTZUP.mjs.map} +1 -1
  224. package/dist/{chunk-FD72QL42.mjs → chunk-JXCMCASE.mjs} +3 -3
  225. package/dist/{chunk-FD72QL42.mjs.map → chunk-JXCMCASE.mjs.map} +1 -1
  226. package/dist/{chunk-TR3F3CLV.js → chunk-KA4NNWOK.js} +10 -10
  227. package/dist/{chunk-TR3F3CLV.js.map → chunk-KA4NNWOK.js.map} +1 -1
  228. package/dist/{chunk-K4TCEAUX.js → chunk-KR7ATPAN.js} +5 -5
  229. package/dist/chunk-KR7ATPAN.js.map +1 -0
  230. package/dist/{chunk-JLVCWC6A.mjs → chunk-KZVSB5WE.mjs} +2 -2
  231. package/dist/chunk-KZVSB5WE.mjs.map +1 -0
  232. package/dist/chunk-LWCAZDY2.js +355 -0
  233. package/dist/chunk-LWCAZDY2.js.map +1 -0
  234. package/dist/{chunk-5HFD36AN.js → chunk-MCG4BSRW.js} +5 -5
  235. package/dist/{chunk-5HFD36AN.js.map → chunk-MCG4BSRW.js.map} +1 -1
  236. package/dist/{chunk-S2Y7LVQ2.js → chunk-MIYGP34I.js} +14 -14
  237. package/dist/{chunk-S2Y7LVQ2.js.map → chunk-MIYGP34I.js.map} +1 -1
  238. package/dist/{chunk-PI3FHABB.js → chunk-MS2EE6LT.js} +10 -10
  239. package/dist/{chunk-PI3FHABB.js.map → chunk-MS2EE6LT.js.map} +1 -1
  240. package/dist/{chunk-QQQAW6WY.js → chunk-N6CB7MK7.js} +6 -6
  241. package/dist/{chunk-QQQAW6WY.js.map → chunk-N6CB7MK7.js.map} +1 -1
  242. package/dist/{chunk-LMEUYRRR.js → chunk-NAA5J2JM.js} +233 -120
  243. package/dist/chunk-NAA5J2JM.js.map +1 -0
  244. package/dist/{chunk-OE7CTIOU.mjs → chunk-NE6QGHFT.mjs} +4 -4
  245. package/dist/{chunk-OE7CTIOU.mjs.map → chunk-NE6QGHFT.mjs.map} +1 -1
  246. package/dist/{chunk-NCDF2RK4.mjs → chunk-NR52QHOD.mjs} +3 -3
  247. package/dist/{chunk-NCDF2RK4.mjs.map → chunk-NR52QHOD.mjs.map} +1 -1
  248. package/dist/{chunk-GQF6QAQS.js → chunk-OGNX6JT4.js} +5 -5
  249. package/dist/chunk-OGNX6JT4.js.map +1 -0
  250. package/dist/{chunk-FVW6FT6L.js → chunk-OSU4QA5M.js} +8 -8
  251. package/dist/{chunk-FVW6FT6L.js.map → chunk-OSU4QA5M.js.map} +1 -1
  252. package/dist/{chunk-UEUWC3PH.mjs → chunk-PJYTYM5G.mjs} +3 -3
  253. package/dist/{chunk-UEUWC3PH.mjs.map → chunk-PJYTYM5G.mjs.map} +1 -1
  254. package/dist/{chunk-DO6ATNLN.js → chunk-PRAHDLKT.js} +5 -5
  255. package/dist/chunk-PRAHDLKT.js.map +1 -0
  256. package/dist/{chunk-RFAAY3BW.mjs → chunk-QEM2F2DC.mjs} +3 -3
  257. package/dist/{chunk-RFAAY3BW.mjs.map → chunk-QEM2F2DC.mjs.map} +1 -1
  258. package/dist/{chunk-F3KA6V2Y.js → chunk-QOCLSJIU.js} +3 -3
  259. package/dist/{chunk-F3KA6V2Y.js.map → chunk-QOCLSJIU.js.map} +1 -1
  260. package/dist/{chunk-R5IPDAMM.mjs → chunk-QVBAAPNT.mjs} +3 -3
  261. package/dist/{chunk-R5IPDAMM.mjs.map → chunk-QVBAAPNT.mjs.map} +1 -1
  262. package/dist/{chunk-POJZA67T.mjs → chunk-RC4L4DV2.mjs} +4 -4
  263. package/dist/{chunk-POJZA67T.mjs.map → chunk-RC4L4DV2.mjs.map} +1 -1
  264. package/dist/{chunk-ZVI7MBVF.mjs → chunk-RDPI27TS.mjs} +12 -10
  265. package/dist/chunk-RDPI27TS.mjs.map +1 -0
  266. package/dist/{chunk-OEW2LFOV.js → chunk-RLUNEEGM.js} +4 -4
  267. package/dist/{chunk-OEW2LFOV.js.map → chunk-RLUNEEGM.js.map} +1 -1
  268. package/dist/{chunk-HBHS5VRN.mjs → chunk-RPCODOWG.mjs} +3 -3
  269. package/dist/{chunk-HBHS5VRN.mjs.map → chunk-RPCODOWG.mjs.map} +1 -1
  270. package/dist/{chunk-ROSYCNXG.mjs → chunk-RZGVNDO6.mjs} +4 -4
  271. package/dist/{chunk-ROSYCNXG.mjs.map → chunk-RZGVNDO6.mjs.map} +1 -1
  272. package/dist/{chunk-MYMZRHTC.js → chunk-SCRB5DZM.js} +8 -8
  273. package/dist/{chunk-MYMZRHTC.js.map → chunk-SCRB5DZM.js.map} +1 -1
  274. package/dist/{chunk-YQTE32QK.js → chunk-SG7CWD2R.js} +17 -16
  275. package/dist/chunk-SG7CWD2R.js.map +1 -0
  276. package/dist/{chunk-XEB77DQS.js → chunk-TBXWJJGT.js} +3 -3
  277. package/dist/chunk-TBXWJJGT.js.map +1 -0
  278. package/dist/{chunk-QXMWNIKL.mjs → chunk-TCCGP3FT.mjs} +3 -3
  279. package/dist/{chunk-QXMWNIKL.mjs.map → chunk-TCCGP3FT.mjs.map} +1 -1
  280. package/dist/{chunk-CZEIHFLW.js → chunk-TCZHRSWY.js} +4 -4
  281. package/dist/{chunk-CZEIHFLW.js.map → chunk-TCZHRSWY.js.map} +1 -1
  282. package/dist/{chunk-LB44BNJ2.js → chunk-TST2IKYO.js} +14 -14
  283. package/dist/{chunk-LB44BNJ2.js.map → chunk-TST2IKYO.js.map} +1 -1
  284. package/dist/{chunk-U2V542EO.mjs → chunk-UHYRVALN.mjs} +2 -2
  285. package/dist/{chunk-U2V542EO.mjs.map → chunk-UHYRVALN.mjs.map} +1 -1
  286. package/dist/{chunk-4L7OHRWB.mjs → chunk-UO6KTHBQ.mjs} +4 -4
  287. package/dist/{chunk-4L7OHRWB.mjs.map → chunk-UO6KTHBQ.mjs.map} +1 -1
  288. package/dist/{chunk-H5NBFNND.js → chunk-UZKXOZHR.js} +2 -2
  289. package/dist/chunk-UZKXOZHR.js.map +1 -0
  290. package/dist/{chunk-NMM27UOF.js → chunk-VINLIMAX.js} +3 -3
  291. package/dist/{chunk-NMM27UOF.js.map → chunk-VINLIMAX.js.map} +1 -1
  292. package/dist/{chunk-UHM3CCCC.js → chunk-WDEGGHTB.js} +12 -12
  293. package/dist/{chunk-UHM3CCCC.js.map → chunk-WDEGGHTB.js.map} +1 -1
  294. package/dist/{chunk-PIPERXI7.mjs → chunk-WV5YQL3W.mjs} +3 -3
  295. package/dist/{chunk-PIPERXI7.mjs.map → chunk-WV5YQL3W.mjs.map} +1 -1
  296. package/dist/{chunk-3EY5E35M.mjs → chunk-X3MY4PL7.mjs} +210 -107
  297. package/dist/chunk-X3MY4PL7.mjs.map +1 -0
  298. package/dist/{chunk-4NVMAYMQ.mjs → chunk-XKXZ4OW4.mjs} +4 -4
  299. package/dist/{chunk-4NVMAYMQ.mjs.map → chunk-XKXZ4OW4.mjs.map} +1 -1
  300. package/dist/{chunk-WINDP776.mjs → chunk-XSBKR3VF.mjs} +5 -5
  301. package/dist/{chunk-WINDP776.mjs.map → chunk-XSBKR3VF.mjs.map} +1 -1
  302. package/dist/{chunk-SEP5D4MI.js → chunk-Y2CP5J6Q.js} +2 -2
  303. package/dist/{chunk-SEP5D4MI.js.map → chunk-Y2CP5J6Q.js.map} +1 -1
  304. package/dist/{chunk-VVBMWV4O.js → chunk-YGDQVZEL.js} +7 -7
  305. package/dist/{chunk-VVBMWV4O.js.map → chunk-YGDQVZEL.js.map} +1 -1
  306. package/dist/{chunk-KV7XXLJV.mjs → chunk-YPGUV6BT.mjs} +3 -3
  307. package/dist/{chunk-KV7XXLJV.mjs.map → chunk-YPGUV6BT.mjs.map} +1 -1
  308. package/dist/chunk-YRCR7WY3.js +65 -0
  309. package/dist/chunk-YRCR7WY3.js.map +1 -0
  310. package/dist/{chunk-F2KW4OJF.mjs → chunk-YWLBQ3L5.mjs} +4 -4
  311. package/dist/{chunk-F2KW4OJF.mjs.map → chunk-YWLBQ3L5.mjs.map} +1 -1
  312. package/dist/{chunk-CNTDV7HM.js → chunk-YYEBFQGS.js} +4 -4
  313. package/dist/{chunk-CNTDV7HM.js.map → chunk-YYEBFQGS.js.map} +1 -1
  314. package/dist/chunk-ZL35JCDA.mjs +61 -0
  315. package/dist/chunk-ZL35JCDA.mjs.map +1 -0
  316. package/dist/chunk-ZR43XFUM.js +60 -0
  317. package/dist/chunk-ZR43XFUM.js.map +1 -0
  318. package/dist/{chunk-25F2EXOC.mjs → chunk-ZR4TYCQW.mjs} +3 -3
  319. package/dist/{chunk-25F2EXOC.mjs.map → chunk-ZR4TYCQW.mjs.map} +1 -1
  320. package/dist/cli/commands/setup.d.ts +2 -2
  321. package/dist/cli/commands/sync.d.ts +2 -2
  322. package/dist/cli/commands/sync.d.ts.map +1 -1
  323. package/dist/cli/index.js +76 -76
  324. package/dist/cli/index.mjs +29 -29
  325. package/dist/client/index.js +5 -5
  326. package/dist/client/index.mjs +4 -4
  327. package/dist/commerce/index.js +57 -57
  328. package/dist/commerce/index.mjs +5 -5
  329. package/dist/discover-LPN55L5B.js +21 -0
  330. package/dist/{discover-NGEPXZOD.js.map → discover-LPN55L5B.js.map} +1 -1
  331. package/dist/discover-QGA6QFSR.mjs +4 -0
  332. package/dist/{discover-CYXYKHZ2.mjs.map → discover-QGA6QFSR.mjs.map} +1 -1
  333. package/dist/engage/index.js +13 -13
  334. package/dist/engage/index.mjs +7 -7
  335. package/dist/fleet/contract.d.ts +1 -1
  336. package/dist/fleet/contract.js +12 -12
  337. package/dist/fleet/contract.mjs +1 -1
  338. package/dist/fleet/index.js +20 -20
  339. package/dist/fleet/index.mjs +5 -5
  340. package/dist/forms/index.js +16 -16
  341. package/dist/forms/index.mjs +9 -9
  342. package/dist/forms/server.js +10 -10
  343. package/dist/forms/server.mjs +8 -8
  344. package/dist/forms/static.js +7 -7
  345. package/dist/forms/static.mjs +6 -6
  346. package/dist/images/index.js +11 -11
  347. package/dist/images/index.mjs +4 -4
  348. package/dist/index.d.ts +2 -2
  349. package/dist/index.d.ts.map +1 -1
  350. package/dist/index.js +70 -70
  351. package/dist/index.mjs +12 -12
  352. package/dist/layout/client.js +10 -10
  353. package/dist/layout/client.mjs +9 -9
  354. package/dist/layout/index.js +12 -12
  355. package/dist/layout/index.mjs +10 -10
  356. package/dist/llms/SpeakableSchema.d.ts +1 -1
  357. package/dist/llms/SpeakableSchema.d.ts.map +1 -1
  358. package/dist/llms/client.js +15 -15
  359. package/dist/llms/client.mjs +1 -1
  360. package/dist/llms/generateLLMsTxt.d.ts.map +1 -1
  361. package/dist/llms/index.js +32 -31
  362. package/dist/llms/index.js.map +1 -1
  363. package/dist/llms/index.mjs +8 -7
  364. package/dist/llms/index.mjs.map +1 -1
  365. package/dist/llms/types.d.ts +7 -0
  366. package/dist/llms/types.d.ts.map +1 -1
  367. package/dist/maps/index.js +5 -5
  368. package/dist/maps/index.mjs +3 -3
  369. package/dist/og/index.js +7 -7
  370. package/dist/og/index.mjs +2 -2
  371. package/dist/og/route.d.ts +6 -6
  372. package/dist/og/route.js.map +1 -1
  373. package/dist/og/route.mjs.map +1 -1
  374. package/dist/og/wiring.d.ts +4 -4
  375. package/dist/og/wiring.d.ts.map +1 -1
  376. package/dist/proxy/index.js +5 -5
  377. package/dist/proxy/index.mjs +3 -3
  378. package/dist/redirects/index.js +10 -10
  379. package/dist/redirects/index.mjs +3 -3
  380. package/dist/redirects/not-found.js +7 -7
  381. package/dist/redirects/not-found.mjs +3 -3
  382. package/dist/robots/indexnow.d.ts +17 -0
  383. package/dist/robots/indexnow.d.ts.map +1 -0
  384. package/dist/robots/indexnow.js +39 -0
  385. package/dist/robots/indexnow.js.map +1 -0
  386. package/dist/robots/indexnow.mjs +34 -0
  387. package/dist/robots/indexnow.mjs.map +1 -0
  388. package/dist/search/contract.d.ts +1 -1
  389. package/dist/search/contract.js +3 -3
  390. package/dist/search/contract.mjs +1 -1
  391. package/dist/search/index.js +7 -7
  392. package/dist/search/index.mjs +5 -5
  393. package/dist/seo/client.js +6 -6
  394. package/dist/seo/client.mjs +5 -5
  395. package/dist/seo/index.js +47 -47
  396. package/dist/seo/index.js.map +1 -1
  397. package/dist/seo/index.mjs +12 -12
  398. package/dist/seo/index.mjs.map +1 -1
  399. package/dist/seo/register-sitemap-cli.js +12 -12
  400. package/dist/seo/register-sitemap-cli.mjs +5 -5
  401. package/dist/seo/server.js +21 -21
  402. package/dist/seo/server.mjs +5 -5
  403. package/dist/server/index.js +3 -3
  404. package/dist/server/index.mjs +2 -2
  405. package/dist/{server-api-A2EFCTZX.mjs → server-api-7F5P6CSH.mjs} +6 -6
  406. package/dist/{server-api-A2EFCTZX.mjs.map → server-api-7F5P6CSH.mjs.map} +1 -1
  407. package/dist/{server-api-IWTZ7C3O.js → server-api-OC5JNK6R.js} +24 -24
  408. package/dist/{server-api-IWTZ7C3O.js.map → server-api-OC5JNK6R.js.map} +1 -1
  409. package/dist/shared/build-entries.d.ts +9 -7
  410. package/dist/shared/build-entries.d.ts.map +1 -1
  411. package/dist/shared/version.d.ts +1 -1
  412. package/dist/signal/index.js +11 -11
  413. package/dist/signal/index.mjs +2 -2
  414. package/dist/sitemap/discover.d.ts +1 -1
  415. package/dist/sitemap/index.d.ts +7 -1
  416. package/dist/sitemap/index.d.ts.map +1 -1
  417. package/dist/sitemap/index.js +47 -30
  418. package/dist/sitemap/index.js.map +1 -1
  419. package/dist/sitemap/index.mjs +26 -9
  420. package/dist/sitemap/index.mjs.map +1 -1
  421. package/dist/sitemap/shared.d.ts +1 -1
  422. package/dist/sitemap/shared.d.ts.map +1 -1
  423. package/dist/sites/site-param.d.ts +2 -2
  424. package/dist/sync/index.js +17 -17
  425. package/dist/sync/index.mjs +4 -4
  426. package/dist/types.d.ts +1 -1
  427. package/dist/types.d.ts.map +1 -1
  428. package/dist/writeLLMsTxt-ATADOUVZ.js +20 -0
  429. package/dist/writeLLMsTxt-QJFXBHZF.mjs +11 -0
  430. package/package.json +20 -14
  431. package/dist/BlogPostFaqSection-P3GXVQPA.mjs +0 -11
  432. package/dist/BlogPostFaqSection-P3GXVQPA.mjs.map +0 -1
  433. package/dist/BlogPostFaqSection-YG5J4B4U.js +0 -13
  434. package/dist/BlogPostFaqSection-YG5J4B4U.js.map +0 -1
  435. package/dist/BlogViewTracker-Q7Y4MN4B.mjs.map +0 -1
  436. package/dist/BlogViewTracker-S3CSRBIZ.js.map +0 -1
  437. package/dist/ChatWidget-76R7NOWD.js +0 -24
  438. package/dist/ChatWidget-G3KOUDX5.mjs +0 -15
  439. package/dist/EngageWidget-HD4F2ZVE.js +0 -21
  440. package/dist/EngageWidget-UJ47PQFG.mjs +0 -12
  441. package/dist/ManagedForm-4ZGU4R3G.js +0 -25
  442. package/dist/ManagedForm-Z7QE4KYI.mjs +0 -16
  443. package/dist/ManagedNewsletterForm-22EPZB7Z.js.map +0 -1
  444. package/dist/ManagedNewsletterForm-4TVO2VL4.mjs.map +0 -1
  445. package/dist/SitemapSync-GF4OKN4Z.js +0 -18
  446. package/dist/SitemapSync-WIUEYOBS.mjs +0 -9
  447. package/dist/blog/AuthorCard.d.ts.map +0 -1
  448. package/dist/blog/AuthorPage.d.ts.map +0 -1
  449. package/dist/blog/BlogFAQ.d.ts.map +0 -1
  450. package/dist/blog/BlogLayout.d.ts.map +0 -1
  451. package/dist/blog/BlogList.d.ts.map +0 -1
  452. package/dist/blog/BlogPost.d.ts.map +0 -1
  453. package/dist/blog/BlogPostFaqSection.d.ts +0 -3
  454. package/dist/blog/BlogPostFaqSection.d.ts.map +0 -1
  455. package/dist/blog/BlogSidebar.d.ts.map +0 -1
  456. package/dist/blog/BlogViewTracker.d.ts.map +0 -1
  457. package/dist/blog/ClusterLandingPage.d.ts.map +0 -1
  458. package/dist/blog/ClusterNavigation.d.ts.map +0 -1
  459. package/dist/blog/ManagedNewsletterForm.d.ts.map +0 -1
  460. package/dist/blog/NewsletterShell.d.ts.map +0 -1
  461. package/dist/blog/NewsletterWidget.d.ts.map +0 -1
  462. package/dist/blog/RelatedPosts.d.ts.map +0 -1
  463. package/dist/blog/ServiceCallout.d.ts.map +0 -1
  464. package/dist/blog/TableOfContents.d.ts.map +0 -1
  465. package/dist/blog/artwork.d.ts +0 -14
  466. package/dist/blog/artwork.d.ts.map +0 -1
  467. package/dist/blog/author-schema.d.ts.map +0 -1
  468. package/dist/blog/author-social.d.ts.map +0 -1
  469. package/dist/blog/blog-styles.d.ts.map +0 -1
  470. package/dist/blog/blog-tables.d.ts.map +0 -1
  471. package/dist/blog/excerpt.d.ts.map +0 -1
  472. package/dist/blog/index.d.ts.map +0 -1
  473. package/dist/blog/index.js.map +0 -1
  474. package/dist/blog/index.mjs.map +0 -1
  475. package/dist/blog/processBlogHtml.d.ts.map +0 -1
  476. package/dist/blog/public-reads.d.ts +0 -27
  477. package/dist/blog/public-reads.d.ts.map +0 -1
  478. package/dist/blog/reading-time.d.ts.map +0 -1
  479. package/dist/blog/routes.d.ts.map +0 -1
  480. package/dist/blog/server-core.d.ts.map +0 -1
  481. package/dist/blog/server-ui.d.ts +0 -29
  482. package/dist/blog/server-ui.d.ts.map +0 -1
  483. package/dist/blog/server-ui.js +0 -267
  484. package/dist/blog/server-ui.js.map +0 -1
  485. package/dist/blog/server-ui.mjs +0 -203
  486. package/dist/blog/server-ui.mjs.map +0 -1
  487. package/dist/blog/server.d.ts.map +0 -1
  488. package/dist/blog/server.js +0 -207
  489. package/dist/blog/server.mjs +0 -10
  490. package/dist/blog/types.d.ts.map +0 -1
  491. package/dist/blog/widget-styles.d.ts +0 -9
  492. package/dist/blog/widget-styles.d.ts.map +0 -1
  493. package/dist/blog-styles-6EY3BS5Y.js.map +0 -1
  494. package/dist/blog-styles-7AKRUMV7.mjs.map +0 -1
  495. package/dist/blog-tables-DWDEWA3S.js +0 -17
  496. package/dist/blog-tables-DWDEWA3S.js.map +0 -1
  497. package/dist/blog-tables-QGIHOESZ.mjs +0 -4
  498. package/dist/blog-tables-QGIHOESZ.mjs.map +0 -1
  499. package/dist/chunk-3EY5E35M.mjs.map +0 -1
  500. package/dist/chunk-4LYW6PMU.mjs.map +0 -1
  501. package/dist/chunk-4TLFZ6NR.mjs.map +0 -1
  502. package/dist/chunk-5B4J3IBX.mjs +0 -164
  503. package/dist/chunk-5B4J3IBX.mjs.map +0 -1
  504. package/dist/chunk-5FETEH3Y.mjs +0 -6
  505. package/dist/chunk-75A4KY4X.mjs.map +0 -1
  506. package/dist/chunk-7R6H3GQ4.js +0 -8
  507. package/dist/chunk-7TC3GRXQ.js.map +0 -1
  508. package/dist/chunk-AJBOEV4I.mjs.map +0 -1
  509. package/dist/chunk-AK6JZ3OE.mjs.map +0 -1
  510. package/dist/chunk-FHPW7XVH.js +0 -175
  511. package/dist/chunk-FHPW7XVH.js.map +0 -1
  512. package/dist/chunk-G2WC3CXW.js.map +0 -1
  513. package/dist/chunk-GHECYDSZ.mjs.map +0 -1
  514. package/dist/chunk-GQF6QAQS.js.map +0 -1
  515. package/dist/chunk-H5NBFNND.js.map +0 -1
  516. package/dist/chunk-HX6ZG3PP.js.map +0 -1
  517. package/dist/chunk-JLVCWC6A.mjs.map +0 -1
  518. package/dist/chunk-K4TCEAUX.js.map +0 -1
  519. package/dist/chunk-LMEUYRRR.js.map +0 -1
  520. package/dist/chunk-MWKI6ZIS.mjs.map +0 -1
  521. package/dist/chunk-O237KKCX.mjs.map +0 -1
  522. package/dist/chunk-Q2NBD54V.js.map +0 -1
  523. package/dist/chunk-R7GHP3FY.js.map +0 -1
  524. package/dist/chunk-W2U5SLMY.js.map +0 -1
  525. package/dist/chunk-XEB77DQS.js.map +0 -1
  526. package/dist/chunk-Y5QZPUL3.mjs.map +0 -1
  527. package/dist/chunk-YFX3Q56O.mjs.map +0 -1
  528. package/dist/chunk-YN7QHLWF.js.map +0 -1
  529. package/dist/chunk-YQTE32QK.js.map +0 -1
  530. package/dist/chunk-ZPLN537Q.mjs.map +0 -1
  531. package/dist/chunk-ZVI7MBVF.mjs.map +0 -1
  532. package/dist/discover-CYXYKHZ2.mjs +0 -4
  533. package/dist/discover-NGEPXZOD.js +0 -21
  534. package/dist/writeLLMsTxt-CQ7A4AAL.js +0 -19
  535. package/dist/writeLLMsTxt-NX5MSPYX.mjs +0 -10
  536. /package/dist/{blog → articles}/server.js.map +0 -0
  537. /package/dist/{blog → articles}/server.mjs.map +0 -0
@@ -1,175 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkYN7QHLWF_js = require('./chunk-YN7QHLWF.js');
4
-
5
- // src/blog/public-reads.ts
6
- async function fetchBlogPostBySlug({ apiUrl, apiKey, site }, slug) {
7
- try {
8
- const response = await fetch(chunkYN7QHLWF_js.withSiteParam(`${apiUrl}/public/blog/posts/${slug}`, site), {
9
- headers: { "x-api-key": apiKey },
10
- next: { revalidate: 60 }
11
- });
12
- if (!response.ok) {
13
- if (response.status !== 404) console.error("[Blog] Failed to fetch post:", response.statusText);
14
- return null;
15
- }
16
- const data = await response.json();
17
- return data.post || null;
18
- } catch (error) {
19
- console.error("[Blog] Error fetching post:", error);
20
- return null;
21
- }
22
- }
23
- async function fetchBlogCategories({ apiUrl, apiKey, site }) {
24
- try {
25
- const response = await fetch(chunkYN7QHLWF_js.withSiteParam(`${apiUrl}/public/blog/categories`, site), {
26
- headers: { "x-api-key": apiKey },
27
- next: { revalidate: 300 }
28
- });
29
- if (!response.ok) return [];
30
- const data = await response.json();
31
- return data.categories || [];
32
- } catch (error) {
33
- console.error("[Blog] Error fetching categories:", error);
34
- return [];
35
- }
36
- }
37
- async function fetchRelatedBlogPosts({ apiUrl, apiKey, site }, currentPostId, limit, cache = {}) {
38
- try {
39
- const response = await fetch(`${apiUrl}/public/blog/related`, {
40
- method: "POST",
41
- headers: { "Content-Type": "application/json", "x-api-key": apiKey },
42
- body: JSON.stringify(chunkYN7QHLWF_js.withSiteBody({ current_post_id: currentPostId, limit }, site)),
43
- ...cache.revalidate !== void 0 ? { next: { revalidate: cache.revalidate } } : {}
44
- });
45
- if (!response.ok) {
46
- console.error("[Blog] Failed to fetch related posts:", response.statusText);
47
- return [];
48
- }
49
- const data = await response.json();
50
- return data.posts || [];
51
- } catch (error) {
52
- console.error("[Blog] Error fetching related posts:", error);
53
- return [];
54
- }
55
- }
56
-
57
- // src/blog/artwork.ts
58
- function resolveBlogArtwork(post, placement = "card") {
59
- const editorial = placement === "article" && !!post.editorial_image;
60
- const src = (editorial ? post.editorial_image : post.featured_image) || void 0;
61
- const alt = editorial ? post.editorial_image_alt ?? "" : post.featured_image_alt ?? post.title ?? "";
62
- let composed = false;
63
- if (src) {
64
- try {
65
- composed = /\/Blog\/Cards\//i.test(new URL(src, "https://site-kit.invalid").pathname);
66
- } catch {
67
- }
68
- }
69
- return { src, alt, composed };
70
- }
71
- function blogArtworkImageStyle(artwork) {
72
- return artwork.composed ? { display: "block", height: "auto", maxHeight: "none", objectFit: "contain", transform: "none" } : void 0;
73
- }
74
-
75
- // src/blog/routes.ts
76
- function blogCategorySlug(category) {
77
- const value = typeof category === "string" ? category : category?.slug || category?.name || "";
78
- return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
79
- }
80
- function localPath(path) {
81
- const value = path.trim();
82
- if (!value || /^[a-z][a-z\d+.-]*:/i.test(value) || value.startsWith("//") || value.includes("\\")) {
83
- throw new Error("Blog routes must be local paths, such as /theforge/guides/story.");
84
- }
85
- return value.startsWith("/") ? value : `/${value}`;
86
- }
87
- function createBlogRoutes(options = {}) {
88
- const raw = (options.basePath ?? options.blogBasePath ?? "/blog").trim() || "/blog";
89
- const index = localPath(raw).replace(/\/+$/, "") || "/";
90
- const prefix = index === "/" ? "" : index;
91
- return {
92
- index,
93
- post(post) {
94
- if (options.postPath) return localPath(options.postPath(post));
95
- const category = blogCategorySlug(post.category || options.category);
96
- return `${prefix}${options.includeCategoryInPath && category ? `/${encodeURIComponent(category)}` : ""}/${encodeURIComponent(post.slug)}`;
97
- },
98
- category(slug) {
99
- const normalized = blogCategorySlug(slug);
100
- return options.categoryPath ? localPath(options.categoryPath(normalized)) : `${prefix}/category/${encodeURIComponent(normalized)}`;
101
- },
102
- cluster: (slug) => `${prefix}/cluster/${encodeURIComponent(slug)}`,
103
- author: (slug) => `${prefix}/author/${encodeURIComponent(slug)}`,
104
- rss: `${prefix}/rss.xml`,
105
- atom: `${prefix}/feed.xml`
106
- };
107
- }
108
- function resolveBlogUrl(path, siteUrl = "") {
109
- return `${siteUrl.replace(/\/+$/, "")}${localPath(path)}`;
110
- }
111
- function resolveBlogPostUrl(post, options = {}) {
112
- const canonical = post.canonical_url?.trim();
113
- if (canonical) {
114
- if (/^https?:\/\//i.test(canonical)) {
115
- try {
116
- return new URL(canonical).toString();
117
- } catch {
118
- }
119
- } else if (canonical.startsWith("/") && !canonical.startsWith("//") && !canonical.includes("\\")) {
120
- return resolveBlogUrl(canonical, options.siteUrl);
121
- }
122
- }
123
- return resolveBlogUrl(createBlogRoutes(options).post(post), options.siteUrl);
124
- }
125
-
126
- // src/blog/author-schema.ts
127
- function generateAuthorSchema(author, options = {}) {
128
- const { siteUrl = "", siteName } = options;
129
- const sameAs = [...author.same_as || []];
130
- if (author.linkedin_url) sameAs.push(author.linkedin_url);
131
- if (author.twitter_url) sameAs.push(author.twitter_url);
132
- if (author.website_url) sameAs.push(author.website_url);
133
- if (author.social_links?.twitter) sameAs.push(`https://twitter.com/${author.social_links.twitter}`);
134
- if (author.social_links?.linkedin) sameAs.push(author.social_links.linkedin);
135
- if (author.social_links?.github) sameAs.push(`https://github.com/${author.social_links.github}`);
136
- const uniqueSameAs = [...new Set(sameAs.filter(Boolean))];
137
- const schema = {
138
- "@context": "https://schema.org",
139
- "@type": "Person",
140
- name: author.name,
141
- url: author.author_page_url || `${siteUrl}/blog/author/${author.slug}`
142
- };
143
- if (author.title) schema.jobTitle = author.title;
144
- if (author.company || siteName) {
145
- schema.worksFor = {
146
- "@type": "Organization",
147
- name: author.company || siteName,
148
- ...siteUrl ? { url: siteUrl } : {}
149
- };
150
- }
151
- if (author.bio || author.short_bio) schema.description = author.short_bio || author.bio;
152
- if (author.avatar_url) schema.image = author.avatar_url;
153
- if (uniqueSameAs.length > 0) schema.sameAs = uniqueSameAs;
154
- if (author.knows_about && author.knows_about.length > 0) schema.knowsAbout = author.knows_about;
155
- if (author.credentials && author.credentials.length > 0) {
156
- schema.hasCredential = author.credentials.map((c) => ({
157
- "@type": "EducationalOccupationalCredential",
158
- credentialCategory: c
159
- }));
160
- }
161
- return schema;
162
- }
163
-
164
- exports.blogArtworkImageStyle = blogArtworkImageStyle;
165
- exports.blogCategorySlug = blogCategorySlug;
166
- exports.createBlogRoutes = createBlogRoutes;
167
- exports.fetchBlogCategories = fetchBlogCategories;
168
- exports.fetchBlogPostBySlug = fetchBlogPostBySlug;
169
- exports.fetchRelatedBlogPosts = fetchRelatedBlogPosts;
170
- exports.generateAuthorSchema = generateAuthorSchema;
171
- exports.resolveBlogArtwork = resolveBlogArtwork;
172
- exports.resolveBlogPostUrl = resolveBlogPostUrl;
173
- exports.resolveBlogUrl = resolveBlogUrl;
174
- //# sourceMappingURL=chunk-FHPW7XVH.js.map
175
- //# sourceMappingURL=chunk-FHPW7XVH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/blog/public-reads.ts","../src/blog/artwork.ts","../src/blog/routes.ts","../src/blog/author-schema.ts"],"names":["withSiteParam","withSiteBody"],"mappings":"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","file":"chunk-FHPW7XVH.js","sourcesContent":["/**\n * @sonordev/site-kit/blog — shared public blog reads (leaf module)\n *\n * SINGLE SOURCE OF TRUTH for the three blog reads that both `server-core`\n * (credentials from resolveServerApiConfig) and the server-ui components\n * (credentials from their `apiUrl` / `apiKey` props) make:\n *\n * - post by slug — getBlogPost, <BlogPost>\n * - categories — getBlogCategories, <BlogList>, <BlogSidebar>\n * - related posts — getRelatedInsights, <BlogPost>, <RelatedPosts>\n *\n * Each used to be copied into every caller, which is how one copy of the\n * related read kept a camelCase body and a wrong path long after the others\n * were fixed (see the notes in RelatedPosts). Adding the multi-site `site`\n * dimension would have meant a fourth edit to each copy, so they live here now.\n *\n * A leaf on purpose: it takes credentials as arguments and imports no\n * `server-only` code, because the client-stamped `blog` barrel reaches it\n * through BlogList / BlogPost / BlogSidebar. Callers resolve credentials; this\n * module only talks to the API.\n */\nimport { withSiteBody, withSiteParam } from '../sites/site-param'\nimport type { BlogCategory, BlogPost } from './types'\n\nexport interface BlogReadConfig {\n apiUrl: string\n apiKey: string\n /** Sub-site host override. Omit to resolve from NEXT_PUBLIC_SITE_URL. */\n site?: string\n}\n\n/** GET /public/blog/posts/:slug — null when missing or on any failure. */\nexport async function fetchBlogPostBySlug(\n { apiUrl, apiKey, site }: BlogReadConfig,\n slug: string\n): Promise<BlogPost | null> {\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/posts/${slug}`, site), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 60 },\n })\n if (!response.ok) {\n if (response.status !== 404) console.error('[Blog] Failed to fetch post:', response.statusText)\n return null\n }\n const data = await response.json()\n return data.post || null\n } catch (error) {\n console.error('[Blog] Error fetching post:', error)\n return null\n }\n}\n\n/** GET /public/blog/categories — [] on any failure. */\nexport async function fetchBlogCategories({ apiUrl, apiKey, site }: BlogReadConfig): Promise<BlogCategory[]> {\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/categories`, site), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n })\n if (!response.ok) return []\n const data = await response.json()\n return data.categories || []\n } catch (error) {\n console.error('[Blog] Error fetching categories:', error)\n return []\n }\n}\n\n/**\n * POST /public/blog/related — [] on any failure.\n *\n * `/public/blog/*`, not `/api/public/blog/*`: public-blog.controller is one of\n * only two public controllers whose @Controller() omits the `api/` segment.\n * The body is snake_case `current_post_id` (the controller looks the post up\n * by id — there is no slug path), and `site` rides in the body because this is\n * the one blog read that isn't a GET.\n *\n * `revalidate` is optional so <RelatedPosts>, which never set a fetch cache\n * lifetime, keeps rendering exactly as it did.\n */\nexport async function fetchRelatedBlogPosts(\n { apiUrl, apiKey, site }: BlogReadConfig,\n currentPostId: string,\n limit: number,\n cache: { revalidate?: number } = {}\n): Promise<BlogPost[]> {\n try {\n const response = await fetch(`${apiUrl}/public/blog/related`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },\n body: JSON.stringify(withSiteBody({ current_post_id: currentPostId, limit }, site)),\n ...(cache.revalidate !== undefined ? { next: { revalidate: cache.revalidate } } : {}),\n })\n if (!response.ok) {\n console.error('[Blog] Failed to fetch related posts:', response.statusText)\n return []\n }\n const data = await response.json()\n return data.posts || []\n } catch (error) {\n console.error('[Blog] Error fetching related posts:', error)\n return []\n }\n}\n","import type { CSSProperties } from 'react'\nimport type { BlogPost } from './types'\n\nexport interface BlogArtwork {\n src: string | undefined\n alt: string\n composed: boolean\n}\n\ntype ArtworkSource = Pick<BlogPost, 'title' | 'featured_image' | 'featured_image_alt' | 'editorial_image' | 'editorial_image_alt'>\n\n/** Single source of truth for article artwork versus complete promotional cards. */\nexport function resolveBlogArtwork(post: ArtworkSource, placement: 'article' | 'card' = 'card'): BlogArtwork {\n const editorial = placement === 'article' && !!post.editorial_image\n const src = (editorial ? post.editorial_image : post.featured_image) || undefined\n const alt = editorial\n ? post.editorial_image_alt ?? ''\n : post.featured_image_alt ?? post.title ?? ''\n let composed = false\n if (src) {\n try {\n // Inspect the path, not a query string that happens to mention a card.\n composed = /\\/Blog\\/Cards\\//i.test(new URL(src, 'https://site-kit.invalid').pathname)\n } catch { /* An unrecognized source retains the normal photograph layout. */ }\n }\n return { src, alt, composed }\n}\n\n/** Preserve generated text and graphics across every stock card surface. */\nexport function blogArtworkImageStyle(artwork: BlogArtwork): CSSProperties | undefined {\n return artwork.composed\n ? { display: 'block', height: 'auto', maxHeight: 'none', objectFit: 'contain', transform: 'none' }\n : undefined\n}\n","/** Shared, environment-free publication routes for server helpers and blog UI. */\nexport interface BlogRoutePost {\n slug: string\n category?: string | { slug?: string; name?: string } | null\n canonical_url?: string | null\n}\n\nexport interface BlogRoutingOptions {\n /** Publication root. Defaults to /blog. */\n basePath?: string\n /** Existing metadata option; basePath takes precedence when both are set. */\n blogBasePath?: string\n /** Use /publication/category/post instead of /publication/post. */\n includeCategoryInPath?: boolean\n /** Fallback category for partial cluster records. */\n category?: string\n /** A custom local article path. Receives the post's slug and category. */\n postPath?: (post: BlogRoutePost) => string\n /** A custom category destination, for example /theforge?category=guides. */\n categoryPath?: (slug: string) => string\n}\n\nexport function blogCategorySlug(category: BlogRoutePost['category']): string {\n const value = typeof category === 'string' ? category : category?.slug || category?.name || ''\n return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')\n}\n\nfunction localPath(path: string): string {\n const value = path.trim()\n if (!value || /^[a-z][a-z\\d+.-]*:/i.test(value) || value.startsWith('//') || value.includes('\\\\')) {\n throw new Error('Blog routes must be local paths, such as /theforge/guides/story.')\n }\n return value.startsWith('/') ? value : `/${value}`\n}\n\n/** Single source of truth for publication paths. Custom routes never change OG artwork. */\nexport function createBlogRoutes(options: BlogRoutingOptions = {}) {\n const raw = (options.basePath ?? options.blogBasePath ?? '/blog').trim() || '/blog'\n const index = localPath(raw).replace(/\\/+$/, '') || '/'\n const prefix = index === '/' ? '' : index\n return {\n index,\n post(post: BlogRoutePost): string {\n if (options.postPath) return localPath(options.postPath(post))\n const category = blogCategorySlug(post.category || options.category)\n return `${prefix}${options.includeCategoryInPath && category ? `/${encodeURIComponent(category)}` : ''}/${encodeURIComponent(post.slug)}`\n },\n category(slug: string): string {\n const normalized = blogCategorySlug(slug)\n return options.categoryPath\n ? localPath(options.categoryPath(normalized))\n : `${prefix}/category/${encodeURIComponent(normalized)}`\n },\n cluster: (slug: string) => `${prefix}/cluster/${encodeURIComponent(slug)}`,\n author: (slug: string) => `${prefix}/author/${encodeURIComponent(slug)}`,\n rss: `${prefix}/rss.xml`,\n atom: `${prefix}/feed.xml`,\n }\n}\n\nexport function resolveBlogUrl(path: string, siteUrl = ''): string {\n return `${siteUrl.replace(/\\/+$/, '')}${localPath(path)}`\n}\n\n/** Metadata, feeds, sitemaps and generated schemas agree on a supplied canonical. */\nexport function resolveBlogPostUrl(post: BlogRoutePost, options: BlogRoutingOptions & { siteUrl?: string } = {}): string {\n const canonical = post.canonical_url?.trim()\n if (canonical) {\n if (/^https?:\\/\\//i.test(canonical)) {\n try { return new URL(canonical).toString() } catch { /* use the local route */ }\n } else if (canonical.startsWith('/') && !canonical.startsWith('//') && !canonical.includes('\\\\')) {\n return resolveBlogUrl(canonical, options.siteUrl)\n }\n }\n return resolveBlogUrl(createBlogRoutes(options).post(post), options.siteUrl)\n}\n","/**\n * @sonordev/site-kit/blog — Author JSON-LD (pure)\n *\n * Split out of `./server-core` on purpose. `generateAuthorSchema` is a pure\n * function — it maps a `BlogAuthor` to a Person schema object and touches no\n * env, no config, and no network. It only ever lived in `server-core` because\n * the other author helpers there (`getAuthorBySlug`, `getAuthorPosts`) do fetch.\n *\n * `AuthorPage` is exported from the client-stamped `@sonordev/site-kit/blog`\n * barrel (see `./index.ts`), so importing this one function from `server-core`\n * dragged that whole server module — its `x-api-key` fetches included — into\n * the browser bundle. Keeping the pure half here means `AuthorPage` can stay in\n * the barrel without pulling a single line of server code with it.\n *\n * Anything added here must stay pure. If it needs an API key or a fetch, it\n * belongs in `./server-core`, not in this file.\n */\n\nimport type { BlogAuthor } from './types'\n\n/**\n * Generate Person JSON-LD schema for an author page\n */\nexport function generateAuthorSchema(\n author: BlogAuthor,\n options: { siteUrl?: string; siteName?: string } = {}\n): object {\n const { siteUrl = '', siteName } = options\n\n const sameAs: string[] = [...(author.same_as || [])]\n if (author.linkedin_url) sameAs.push(author.linkedin_url)\n if (author.twitter_url) sameAs.push(author.twitter_url)\n if (author.website_url) sameAs.push(author.website_url)\n if (author.social_links?.twitter) sameAs.push(`https://twitter.com/${author.social_links.twitter}`)\n if (author.social_links?.linkedin) sameAs.push(author.social_links.linkedin)\n if (author.social_links?.github) sameAs.push(`https://github.com/${author.social_links.github}`)\n\n // Deduplicate\n const uniqueSameAs = [...new Set(sameAs.filter(Boolean))]\n\n const schema: Record<string, any> = {\n '@context': 'https://schema.org',\n '@type': 'Person',\n name: author.name,\n url: author.author_page_url || `${siteUrl}/blog/author/${author.slug}`,\n }\n\n if (author.title) schema.jobTitle = author.title\n if (author.company || siteName) {\n schema.worksFor = {\n '@type': 'Organization',\n name: author.company || siteName,\n ...(siteUrl ? { url: siteUrl } : {}),\n }\n }\n if (author.bio || author.short_bio) schema.description = author.short_bio || author.bio\n if (author.avatar_url) schema.image = author.avatar_url\n if (uniqueSameAs.length > 0) schema.sameAs = uniqueSameAs\n if (author.knows_about && author.knows_about.length > 0) schema.knowsAbout = author.knows_about\n if (author.credentials && author.credentials.length > 0) {\n schema.hasCredential = author.credentials.map((c) => ({\n '@type': 'EducationalOccupationalCredential',\n credentialCategory: c,\n }))\n }\n\n return schema\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/search/contract.ts"],"names":[],"mappings":";;;AAmBO,IAAM,eAAA,GAAkB;AAkCxB,IAAM,uBAAA,GAA0B;AAAA,EACrC;AAAA,IACE,IAAA,EAAM,iBAAA;AAAA,IACN,OAAA,EAAS,EAAE,CAAA,EAAG,SAAA;AAAU,GAC1B;AAAA,EACA;AAAA,IACE,IAAA,EAAM,kBAAA;AAAA,IACN,OAAA,EAAS,EAAE,CAAA,EAAG,aAAA,EAAe,IAAA,EAAM,eAAA,EAAiB,KAAA,EAAO,CAAC,MAAA,EAAQ,MAAM,CAAA,EAAG,KAAA,EAAO,CAAA;AAAE;AAE1F","file":"chunk-G2WC3CXW.js","sourcesContent":["/**\n * @sonordev/site-kit/search/contract — site search wire contract.\n *\n * CANONICAL SOURCE for the shape of POST /api/public/search. sonor-api keeps\n * an inline mirror per the cross-repo contract pattern (APIs do not depend on\n * site-kit at runtime); drift is caught by test vectors on both sides.\n *\n * Design notes (4.0 exploration):\n * - One endpoint, unified across content types the project already manages:\n * pages (seo_pages), blog posts, and commerce offerings. The site's whole\n * navigable surface, ranked together.\n * - Tenant scoping comes from the API key (ApiKeyGuard) — requests carry no\n * project id, same as every public endpoint.\n * - `site` narrows multi-site projects (TPS microsites) using the standard\n * host convention from sites/contract.\n * - Results carry a `url` the site can link directly plus a typed `kind`\n * so UIs can group (\"Pages / Posts / Events\").\n */\n\nexport const SEARCH_ENDPOINT = '/api/public/search'\n\nexport type SearchKind = 'page' | 'post' | 'offering'\n\nexport interface SearchRequest {\n /** The user's query. Server trims; empty queries return empty results. */\n q: string\n /** Multi-site narrowing (normalized host, e.g. 'ohiopowerstudies.com'). */\n site?: string\n /** Restrict to specific kinds. Default: all. */\n kinds?: SearchKind[]\n /** Max results across kinds. Server clamps to 25. Default 10. */\n limit?: number\n}\n\nexport interface SearchResult {\n kind: SearchKind\n /** Path or absolute URL the site should link to. */\n url: string\n title: string\n /** Short excerpt with the match context when the backend can produce one. */\n excerpt?: string\n /** Higher = better. Only comparable within a single response. */\n score: number\n}\n\nexport interface SearchResponse {\n query: string\n results: SearchResult[]\n /** Total matches before the limit, per kind — lets UIs show \"12 more posts\". */\n totals?: Partial<Record<SearchKind, number>>\n}\n\n/** Shared test vectors — mirrored by sonor-api's contract test. */\nexport const SEARCH_CONTRACT_VECTORS = [\n {\n name: 'minimal request',\n request: { q: 'bonfire' } satisfies SearchRequest,\n },\n {\n name: 'narrowed request',\n request: { q: 'supply list', site: 'ludlowpto.org', kinds: ['page', 'post'], limit: 5 } satisfies SearchRequest,\n },\n] as const\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/blog/blog-tables.ts"],"names":["frame"],"mappings":"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","file":"chunk-GHECYDSZ.mjs","sourcesContent":["/**\n * Server-rendered table scrolling. Keep the author's table markup intact so\n * captions, header associations, links and table semantics survive unchanged.\n */\n\nconst tableScrollClass = 'sk-blog-table-scroll'\nconst voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta',\n 'param', 'source', 'track', 'wbr',\n])\nconst preservedElements = new Set(['pre', 'code', 'script', 'style', 'textarea', 'xmp', 'template'])\n\n/** Match complete tags, including quoted `>` characters, without reserializing HTML. */\nfunction htmlTags(): RegExp {\n return /<!--[\\s\\S]*?(?:-->|$)|<\\/?[a-z][\\w:-]*(?=[\\s/>])(?:[^\"'<>]|\"[^\"]*\"|'[^']*')*>/gi\n}\n\nfunction hasScrollClass(tag: string): boolean {\n const attributes = tag.replace(/^<[\\w:-]+/, '').replace(/\\/?>$/, '')\n const pattern = /([^\\s=/>]+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g\n for (const attribute of attributes.matchAll(pattern)) {\n if (attribute[1].toLowerCase() !== 'class') continue\n return (attribute[2] ?? attribute[3] ?? attribute[4] ?? '').split(/\\s+/).includes(tableScrollClass)\n }\n return false\n}\n\nfunction labelText(html: string): string {\n // Preserve existing character references in the eventual attribute. Escaping\n // them a second time would announce \"amp\" instead of the author's text.\n return html\n .replace(/<!--[\\s\\S]*?(?:-->|$)/g, '')\n .replace(htmlTags(), ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction escapeLabel(text: string): string {\n return text\n .replace(/&(?!(?:#\\d+|#x[\\da-f]+|[a-z][\\da-z]+);)/gi, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nfunction tableLabel(table: string, number: number): string {\n const caption = table.match(/<caption\\b(?:[^\"'<>]|\"[^\"]*\"|'[^']*')*>([\\s\\S]*?)<\\/caption\\s*>/i)?.[1]\n const headers = [...table.matchAll(/<th\\b(?:[^\"'<>]|\"[^\"]*\"|'[^']*')*>([\\s\\S]*?)<\\/th\\s*>/gi)]\n .slice(0, 4)\n .map((match) => labelText(match[1]))\n .filter(Boolean)\n .join(', ')\n const summary = labelText(caption || headers).slice(0, 180)\n return escapeLabel(`Table ${number}${summary ? `: ${summary}` : ''}`)\n}\n\ninterface ElementFrame {\n tag: string\n scrollRegion: boolean\n table?: { start: number; number: number }\n}\n\n/**\n * Single source of truth for default blog table wrappers. Safe to call twice;\n * already wrapped tables and HTML examples are left alone. This is a layout\n * transform, not an HTML sanitizer: content still follows the host's trust policy.\n */\nexport function wrapBlogTables(html: string): string {\n const tags = htmlTags()\n const stack: ElementFrame[] = []\n const tables: Array<{ start: number; end: number; number: number }> = []\n let tableNumber = 0\n let token: RegExpExecArray | null\n\n while ((token = tags.exec(html))) {\n const tag = token[0]\n if (tag.startsWith('<!--')) continue\n const tagName = /^<\\/?([\\w:-]+)/.exec(tag)![1].toLowerCase()\n\n if (tag.startsWith('</')) {\n const index = stack.map((frame) => frame.tag).lastIndexOf(tagName)\n if (index < 0) continue\n const frame = stack[index]\n if (frame.table) tables.push({ ...frame.table, end: tags.lastIndex })\n stack.length = index\n continue\n }\n\n if (preservedElements.has(tagName)) {\n // Code examples and raw text can contain literal HTML. Skip to the end\n // without interpreting any of those tags as article content.\n const closingTag = new RegExp(`</${tagName}\\\\s*>`, 'gi')\n closingTag.lastIndex = tags.lastIndex\n if (!closingTag.exec(html)) break\n tags.lastIndex = closingTag.lastIndex\n continue\n }\n\n if (voidElements.has(tagName) || /\\/\\s*>$/.test(tag)) continue\n const frame: ElementFrame = { tag: tagName, scrollRegion: hasScrollClass(tag) }\n if (tagName === 'table' && !stack.some((ancestor) => ancestor.tag === 'table')) {\n tableNumber += 1\n if (!stack.some((ancestor) => ancestor.scrollRegion)) {\n frame.table = { start: token.index, number: tableNumber }\n }\n }\n stack.push(frame)\n }\n\n let output = ''\n let cursor = 0\n for (const { start, end, number } of tables) {\n const table = html.slice(start, end)\n output += `${html.slice(cursor, start)}<div class=\"${tableScrollClass}\" role=\"region\" tabindex=\"0\" aria-label=\"${tableLabel(table, number)}\">${table}</div>`\n cursor = end\n }\n return output + html.slice(cursor)\n}\n\n/** Native overflow only when content needs it; small tables retain their layout. */\nexport const blogTableCss = /* css */ `\n.sk-blog-content .sk-blog-table-scroll {\n box-sizing: border-box;\n min-width: 0;\n max-width: 100%;\n overflow-x: auto;\n margin: 1.5em 0;\n border: 1px solid var(--sk-surface-border, #e2e8f0);\n border-radius: var(--sk-radius, 0.5rem);\n background: var(--sk-bg-elevated, #f8fafc);\n scrollbar-width: thin;\n scrollbar-color: var(--sk-primary, #2563eb) var(--sk-bg-elevated, #f8fafc);\n overscroll-behavior-x: contain;\n}\n.sk-blog-content .sk-blog-table-scroll:focus-visible {\n outline: 2px solid var(--sk-border-focus, var(--sk-primary, #2563eb));\n outline-offset: 3px;\n}\n.sk-blog-content .sk-blog-table-scroll::-webkit-scrollbar { height: 10px; }\n.sk-blog-content .sk-blog-table-scroll::-webkit-scrollbar-track {\n background: var(--sk-bg-elevated, #f8fafc);\n border-radius: var(--sk-radius, 0.5rem);\n}\n.sk-blog-content .sk-blog-table-scroll::-webkit-scrollbar-thumb {\n background: var(--sk-primary, #2563eb);\n background-clip: padding-box;\n border: 2px solid var(--sk-bg-elevated, #f8fafc);\n border-radius: var(--sk-radius, 0.5rem);\n}\n.sk-blog-content .sk-blog-table-scroll > table {\n display: table;\n margin: 0;\n overflow: visible;\n}\n.sk-blog-content .sk-blog-table-scroll caption {\n padding: 0.625rem 0.875rem;\n text-align: start;\n color: var(--sk-text-secondary, #475569);\n}\n`.trim()\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/blog/BlogFAQ.tsx"],"names":["jsxs","jsx","useRef","useState","useCallback"],"mappings":";;;;;;AA+CA,IAAM,aAAA,GAAgB;AAAA,EACpB,OAAA,EAAS;AAAA,IACP,MAAA,EAAQ,QAAA;AAAA,IACR,OAAA,EAAS;AAAA,GACX;AAAA,EACA,KAAA,EAAO;AAAA,IACL,QAAA,EAAU,QAAA;AAAA,IACV,UAAA,EAAY,GAAA;AAAA,IACZ,KAAA,EAAO,iCAAA;AAAA,IACP,YAAA,EAAc,QAAA;AAAA,IACd,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,GAAA,EAAK;AAAA,GACP;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,SAAA,EAAW,MAAA;AAAA,IACX,MAAA,EAAQ,CAAA;AAAA,IACR,OAAA,EAAS,CAAA;AAAA,IACT,OAAA,EAAS,MAAA;AAAA,IACT,aAAA,EAAe,QAAA;AAAA,IACf,GAAA,EAAK;AAAA,GACP;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,YAAA,EAAc,SAAA;AAAA,IACd,MAAA,EAAQ,6CAAA;AAAA,IACR,UAAA,EAAY,gCAAA;AAAA,IACZ,QAAA,EAAU,QAAA;AAAA,IACV,UAAA,EAAY;AAAA,GACd;AAAA,EACA,QAAA,EAAU;AAAA,IACR,WAAA,EAAa,uFAAA;AAAA,IACb,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,cAAA,EAAgB,eAAA;AAAA,IAChB,KAAA,EAAO,MAAA;AAAA,IACP,OAAA,EAAS,cAAA;AAAA,IACT,UAAA,EAAY,MAAA;AAAA,IACZ,MAAA,EAAQ,MAAA;AAAA,IACR,MAAA,EAAQ,SAAA;AAAA,IACR,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,QAAA,EAAU,MAAA;AAAA,IACV,UAAA,EAAY,GAAA;AAAA,IACZ,KAAA,EAAO,iCAAA;AAAA,IACP,YAAA,EAAc;AAAA,GAChB;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,UAAA,EAAY,CAAA;AAAA,IACZ,KAAA,EAAO,SAAA;AAAA,IACP,MAAA,EAAQ,SAAA;AAAA,IACR,KAAA,EAAO,4BAAA;AAAA,IACP,UAAA,EAAY;AAAA,GACd;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,QAAA,EAAU,QAAA;AAAA,IACV,UAAA,EAAY;AAAA,GACd;AAAA,EACA,WAAA,EAAa;AAAA,IACX,OAAA,EAAS,mBAAA;AAAA,IACT,QAAA,EAAU,WAAA;AAAA,IACV,UAAA,EAAY,GAAA;AAAA,IACZ,KAAA,EAAO;AAAA;AAEX,CAAA;AAEA,SAAS,aAAA,CAAc,EAAE,MAAA,EAAO,EAAwB;AACtD,EAAA,uBACEA,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,IAAA;AAAA,MACN,MAAA,EAAO,IAAA;AAAA,MACP,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,MAAA;AAAA,MACL,MAAA,EAAO,cAAA;AAAA,MACP,WAAA,EAAY,GAAA;AAAA,MACZ,aAAA,EAAc,OAAA;AAAA,MACd,KAAA,EAAO;AAAA,QACL,GAAG,aAAA,CAAc,IAAA;AAAA,QACjB,SAAA,EAAW,SAAS,eAAA,GAAkB;AAAA,OACxC;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,MAAA,EAAA,EAAK,IAAG,IAAA,EAAK,EAAA,EAAG,KAAI,EAAA,EAAG,IAAA,EAAK,IAAG,IAAA,EAAK,CAAA;AAAA,wBACrCA,cAAA,CAAC,UAAK,EAAA,EAAG,GAAA,EAAI,IAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK;AAAA;AAAA;AAAA,GACvC;AAEJ;AAEA,SAAS,OAAA,CAAQ;AAAA,EACf,IAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,EAMG;AACD,EAAA,MAAM,SAAA,GAAYC,aAAuB,IAAI,CAAA;AAE7C,EAAA,uBACEF,eAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,WAAW,MAAA,EAAQ,IAAA;AAAA,MACnB,KAAA,EAAO,WAAW,MAAA,GAAY;AAAA,QAC5B,GAAG,aAAA,CAAc,IAAA;AAAA,QACjB,GAAI,MAAA,GAAS,aAAA,CAAc,QAAA,GAAW;AAAC,OACzC;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAA,eAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,WAAW,MAAA,EAAQ,QAAA;AAAA,YACnB,KAAA,EAAO,QAAA,GAAW,MAAA,GAAY,aAAA,CAAc,QAAA;AAAA,YAC5C,OAAA,EAAS,QAAA;AAAA,YACT,eAAA,EAAe,MAAA;AAAA,YACf,IAAA,EAAK,QAAA;AAAA,YAEL,QAAA,EAAA;AAAA,8BAAAC,cAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,WAAW,MAAA,EAAQ,YAAA;AAAA,kBACnB,KAAA,EAAO,QAAA,GAAW,MAAA,GAAY,aAAA,CAAc,YAAA;AAAA,kBAE3C,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA,eACR;AAAA,8BACAA,cAAA,CAAC,UAAK,SAAA,EAAW,MAAA,EAAQ,MACvB,QAAA,kBAAAA,cAAA,CAAC,aAAA,EAAA,EAAc,QAAgB,CAAA,EACjC;AAAA;AAAA;AAAA,SACF;AAAA,wBACAA,cAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,SAAA;AAAA,YACL,WAAW,MAAA,EAAQ,MAAA;AAAA,YACnB,KAAA,EAAO,WAAW,MAAA,GAAY;AAAA,cAC5B,GAAG,aAAA,CAAc,MAAA;AAAA,cACjB,SAAA,EAAW,SAAS,CAAA,EAAA,CAAI,SAAA,CAAU,SAAS,YAAA,IAAgB,GAAA,IAAO,EAAE,CAAA,EAAA,CAAA,GAAO,GAAA;AAAA,cAC3E,OAAA,EAAS,SAAS,CAAA,GAAI;AAAA,aACxB;AAAA,YACA,IAAA,EAAK,QAAA;AAAA,YACL,eAAa,CAAC,MAAA;AAAA,YAEd,QAAA,kBAAAA,cAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,WAAW,MAAA,EAAQ,WAAA;AAAA,gBACnB,KAAA,EAAO,QAAA,GAAW,MAAA,GAAY,aAAA,CAAc,WAAA;AAAA,gBAC5C,uBAAA,EAAyB,EAAE,MAAA,EAAQ,IAAA,CAAK,MAAA;AAAO;AAAA;AACjD;AAAA;AACF;AAAA;AAAA,GACF;AAEJ;AAEO,SAAS,OAAA,CAAQ;AAAA,EACtB,KAAA;AAAA,EACA,KAAA,GAAQ,4BAAA;AAAA,EACR,aAAA,GAAgB,KAAA;AAAA,EAChB,MAAA;AAAA,EACA,QAAA,GAAW;AACb,CAAA,EAAiB;AACf,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,IAAIE,cAAA,iBAAsB,IAAI,KAAK,CAAA;AAErE,EAAA,MAAM,MAAA,GAASC,iBAAA,CAAY,CAAC,KAAA,KAAkB;AAC5C,IAAA,cAAA,CAAe,CAAA,IAAA,KAAQ;AACrB,MAAA,MAAM,OAAO,IAAI,GAAA,CAAI,aAAA,GAAgB,IAAA,GAAO,EAAE,CAAA;AAC9C,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,EAAG;AACnB,QAAA,IAAA,CAAK,OAAO,KAAK,CAAA;AAAA,MACnB,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,IAAI,KAAK,CAAA;AAAA,MAChB;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,MAAA,KAAW,GAAG,OAAO,IAAA;AAEzC,EAAA,uBACEJ,eAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,WAAW,MAAA,EAAQ,OAAA;AAAA,MACnB,KAAA,EAAO,QAAA,GAAW,MAAA,GAAY,aAAA,CAAc,OAAA;AAAA,MAC5C,YAAA,EAAW,4BAAA;AAAA,MAEX,QAAA,EAAA;AAAA,wBAAAC,cAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YACC,WAAW,MAAA,EAAQ,KAAA;AAAA,YACnB,KAAA,EAAO,QAAA,GAAW,MAAA,GAAY,aAAA,CAAc,KAAA;AAAA,YAE3C,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,wBACAA,cAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YACC,WAAW,MAAA,EAAQ,IAAA;AAAA,YACnB,KAAA,EAAO,QAAA,GAAW,MAAA,GAAY,aAAA,CAAc,IAAA;AAAA,YAE3C,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,EAAM,KAAA,qBAChBA,cAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBAEC,IAAA;AAAA,gBACA,MAAA,EAAQ,WAAA,CAAY,GAAA,CAAI,KAAK,CAAA;AAAA,gBAC7B,QAAA,EAAU,MAAM,MAAA,CAAO,KAAK,CAAA;AAAA,gBAC5B,MAAA;AAAA,gBACA;AAAA,eAAA;AAAA,cALK;AAAA,aAOR;AAAA;AAAA;AACH;AAAA;AAAA,GACF;AAEJ","file":"chunk-GQF6QAQS.js","sourcesContent":["/**\n * @sonordev/site-kit/blog - Blog FAQ Component\n *\n * Renders FAQ items with collapsible accordion behavior.\n * Content is always in the DOM (not display:none) for SEO crawlability.\n * Uses CSS max-height transitions — no JS animation library required.\n *\n * Pairs with FAQPage schema (generated by Signal AI in the blog pipeline)\n * for Google's \"People Also Ask\" featured snippets.\n *\n * @example\n * ```tsx\n * <BlogFAQ items={post.faqItems} />\n * ```\n */\n\n'use client'\n\nimport React, { useState, useRef, useCallback } from 'react'\n\nexport interface FaqItem {\n question: string\n answer: string\n}\n\nexport interface BlogFAQProps {\n items: FaqItem[]\n /** Section title */\n title?: string\n /** Allow multiple items open simultaneously */\n allowMultiple?: boolean\n /** Override styles with Tailwind classes */\n styles?: {\n section?: string\n title?: string\n list?: string\n item?: string\n question?: string\n questionText?: string\n icon?: string\n answer?: string\n answerInner?: string\n }\n unstyled?: boolean\n}\n\n// CSS custom property defaults\nconst defaultStyles = {\n section: {\n margin: '3rem 0',\n padding: 0,\n },\n title: {\n fontSize: '1.5rem',\n fontWeight: 700,\n color: 'var(--sk-text-primary, #0f172a)',\n marginBottom: '1.5rem',\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n },\n list: {\n listStyle: 'none',\n margin: 0,\n padding: 0,\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '0.5rem',\n },\n item: {\n borderRadius: '0.75rem',\n border: '1px solid var(--sk-surface-border, #e2e8f0)',\n background: 'var(--sk-bg-elevated, #f8fafc)',\n overflow: 'hidden',\n transition: 'border-color 0.2s, box-shadow 0.2s',\n },\n itemOpen: {\n borderColor: 'color-mix(in srgb, var(--sk-primary, #2563eb) 40%, var(--sk-surface-border, #e2e8f0))',\n boxShadow: '0 1px 3px rgba(0,0,0,0.06)',\n },\n question: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n width: '100%',\n padding: '1rem 1.25rem',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n textAlign: 'left' as const,\n },\n questionText: {\n fontSize: '1rem',\n fontWeight: 600,\n color: 'var(--sk-text-primary, #0f172a)',\n paddingRight: '1rem',\n },\n icon: {\n flexShrink: 0,\n width: '1.25rem',\n height: '1.25rem',\n color: 'var(--sk-primary, #2563eb)',\n transition: 'transform 0.2s',\n },\n answer: {\n overflow: 'hidden',\n transition: 'max-height 0.3s ease, opacity 0.2s ease',\n },\n answerInner: {\n padding: '0 1.25rem 1.25rem',\n fontSize: '0.9375rem',\n lineHeight: 1.7,\n color: 'var(--sk-text-secondary, #475569)',\n },\n}\n\nfunction PlusMinusIcon({ isOpen }: { isOpen: boolean }) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n style={{\n ...defaultStyles.icon,\n transform: isOpen ? 'rotate(45deg)' : 'rotate(0deg)',\n }}\n >\n <line x1=\"10\" y1=\"4\" x2=\"10\" y2=\"16\" />\n <line x1=\"4\" y1=\"10\" x2=\"16\" y2=\"10\" />\n </svg>\n )\n}\n\nfunction FAQItem({\n item,\n isOpen,\n onToggle,\n styles,\n unstyled,\n}: {\n item: FaqItem\n isOpen: boolean\n onToggle: () => void\n styles?: BlogFAQProps['styles']\n unstyled?: boolean\n}) {\n const answerRef = useRef<HTMLDivElement>(null)\n\n return (\n <li\n className={styles?.item}\n style={unstyled ? undefined : {\n ...defaultStyles.item,\n ...(isOpen ? defaultStyles.itemOpen : {}),\n }}\n >\n <button\n className={styles?.question}\n style={unstyled ? undefined : defaultStyles.question}\n onClick={onToggle}\n aria-expanded={isOpen}\n type=\"button\"\n >\n <span\n className={styles?.questionText}\n style={unstyled ? undefined : defaultStyles.questionText}\n >\n {item.question}\n </span>\n <span className={styles?.icon}>\n <PlusMinusIcon isOpen={isOpen} />\n </span>\n </button>\n <div\n ref={answerRef}\n className={styles?.answer}\n style={unstyled ? undefined : {\n ...defaultStyles.answer,\n maxHeight: isOpen ? `${(answerRef.current?.scrollHeight || 500) + 40}px` : '0',\n opacity: isOpen ? 1 : 0,\n }}\n role=\"region\"\n aria-hidden={!isOpen}\n >\n <div\n className={styles?.answerInner}\n style={unstyled ? undefined : defaultStyles.answerInner}\n dangerouslySetInnerHTML={{ __html: item.answer }}\n />\n </div>\n </li>\n )\n}\n\nexport function BlogFAQ({\n items,\n title = 'Frequently Asked Questions',\n allowMultiple = false,\n styles,\n unstyled = false,\n}: BlogFAQProps) {\n const [openIndices, setOpenIndices] = useState<Set<number>>(new Set())\n\n const toggle = useCallback((index: number) => {\n setOpenIndices(prev => {\n const next = new Set(allowMultiple ? prev : [])\n if (prev.has(index)) {\n next.delete(index)\n } else {\n next.add(index)\n }\n return next\n })\n }, [allowMultiple])\n\n if (!items || items.length === 0) return null\n\n return (\n <section\n className={styles?.section}\n style={unstyled ? undefined : defaultStyles.section}\n aria-label=\"Frequently Asked Questions\"\n >\n <h2\n className={styles?.title}\n style={unstyled ? undefined : defaultStyles.title}\n >\n {title}\n </h2>\n <ul\n className={styles?.list}\n style={unstyled ? undefined : defaultStyles.list}\n >\n {items.map((item, index) => (\n <FAQItem\n key={index}\n item={item}\n isOpen={openIndices.has(index)}\n onToggle={() => toggle(index)}\n styles={styles}\n unstyled={unstyled}\n />\n ))}\n </ul>\n </section>\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sites/resolve.ts"],"names":["normalizeSiteHost"],"mappings":";;;;;AAkCO,SAAS,gBAAgB,YAAA,EAA+B;AAC7D,EAAA,MAAM,aACJ,OAAO,OAAA,KAAY,WAAA,GAAc,OAAA,CAAQ,IAAI,oBAAA,GAAuB,MAAA;AAEtE,EAAA,IAAI,WAAW,YAAA,IAAgB,EAAA;AAC/B,EAAA,IAAI,CAAC,YAAY,UAAA,EAAY;AAC3B,IAAA,IAAI;AACF,MAAA,QAAA,GAAW,IAAI,GAAA,CAAI,UAAU,CAAA,CAAE,IAAA;AAAA,IACjC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,IAAI,CAAC,QAAA,IAAY,OAAO,MAAA,KAAW,WAAA,EAAa;AAC9C,IAAA,QAAA,GAAW,OAAO,QAAA,CAAS,IAAA;AAAA,EAC7B;AACA,EAAA,OAAOA,kCAAA,CAAkB,QAAQ,CAAA,IAAK,EAAA;AACxC","file":"chunk-H5NBFNND.js","sourcesContent":["/**\n * @sonordev/site-kit/sites — resolveSiteHost (leaf module)\n *\n * SINGLE SOURCE OF TRUTH for \"which sub-site am I?\" — the multi-site `site`\n * dimension that lets one Sonor project host many domains (bd-aec.com plus 48\n * sibling domains; truepowersystems.com plus 16 state microsites).\n *\n * Precedence, per the platform site contract:\n * explicit `site` > NEXT_PUBLIC_SITE_URL host > window.location.host\n *\n * Deliberately usable on BOTH sides. The browser half seeds\n * `window.__SITE_KIT_SITE__` (see `shared/publishCredential`); the build half\n * tags the sitemap sync, the llms.txt reads and every blog read (see\n * `sitemap/index`, `llms/writeLLMsTxt`, and `./site-param`, which appends the\n * host to API requests) so a build describes only its OWN host. Server-side the\n * `window` branch simply never fires, which leaves the NEXT_PUBLIC_SITE_URL\n * host — the right source at build time.\n *\n * Do not re-fork this. Before it existed the build path sent no `site` at all,\n * so every site in a 49-domain network read whichever sibling's seo_pages row\n * the database happened to hand back for a shared path like `/` — bd-aec.com\n * shipped soqwriters.com's homepage copy in its llms.txt.\n *\n * Its own file, not `./contract`, for the same bundle reason as\n * `./normalize`: browser entries import this without dragging the contract's\n * JSON schema along.\n */\nimport { normalizeSiteHost } from './normalize'\n\n/**\n * Resolve the sub-site host. Always returns a string (possibly empty), already\n * normalized with the shared contract (lowercase host, no protocol/path/port)\n * so it matches exactly what the Sonor API stores.\n */\nexport function resolveSiteHost(explicitSite?: string): string {\n const envSiteUrl =\n typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_SITE_URL : undefined\n\n let resolved = explicitSite ?? ''\n if (!resolved && envSiteUrl) {\n try {\n resolved = new URL(envSiteUrl).host\n } catch {\n /* malformed URL — fall through to client-side host */\n }\n }\n if (!resolved && typeof window !== 'undefined') {\n resolved = window.location.host\n }\n return normalizeSiteHost(resolved) ?? ''\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/blog/widget-styles.ts"],"names":[],"mappings":";;;AAOO,IAAM,WAAA,GAAmC;AAAA,EAC9C,OAAA,EAAS,EAAA;AAAA,EACT,eAAA,EAAiB,uBAAA;AAAA,EACjB,YAAA,EAAc;AAChB;AAEO,IAAM,gBAAA,GAAwC;AAAA,EACnD,MAAA,EAAQ,UAAA;AAAA,EACR,QAAA,EAAU,EAAA;AAAA,EACV,UAAA,EAAY,GAAA;AAAA,EACZ,KAAA,EAAO;AACT","file":"chunk-HX6ZG3PP.js","sourcesContent":["import type React from 'react'\n\n/**\n * Shared sidebar-widget chrome, extracted from BlogSidebar so the client-side\n * NewsletterWidget (its own module — it needs hooks, and BlogSidebar is also\n * bundled into the un-stamped server entry blog/server-ui) can reuse it.\n */\nexport const widgetStyle: React.CSSProperties = {\n padding: 20,\n backgroundColor: 'var(--sk-bg-elevated)',\n borderRadius: 12,\n}\n\nexport const widgetTitleStyle: React.CSSProperties = {\n margin: '0 0 16px',\n fontSize: 16,\n fontWeight: 600,\n color: 'var(--sk-text-primary)',\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sites/resolve.ts"],"names":[],"mappings":";;;AAkCO,SAAS,gBAAgB,YAAA,EAA+B;AAC7D,EAAA,MAAM,aACJ,OAAO,OAAA,KAAY,WAAA,GAAc,OAAA,CAAQ,IAAI,oBAAA,GAAuB,MAAA;AAEtE,EAAA,IAAI,WAAW,YAAA,IAAgB,EAAA;AAC/B,EAAA,IAAI,CAAC,YAAY,UAAA,EAAY;AAC3B,IAAA,IAAI;AACF,MAAA,QAAA,GAAW,IAAI,GAAA,CAAI,UAAU,CAAA,CAAE,IAAA;AAAA,IACjC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,IAAI,CAAC,QAAA,IAAY,OAAO,MAAA,KAAW,WAAA,EAAa;AAC9C,IAAA,QAAA,GAAW,OAAO,QAAA,CAAS,IAAA;AAAA,EAC7B;AACA,EAAA,OAAO,iBAAA,CAAkB,QAAQ,CAAA,IAAK,EAAA;AACxC","file":"chunk-JLVCWC6A.mjs","sourcesContent":["/**\n * @sonordev/site-kit/sites — resolveSiteHost (leaf module)\n *\n * SINGLE SOURCE OF TRUTH for \"which sub-site am I?\" — the multi-site `site`\n * dimension that lets one Sonor project host many domains (bd-aec.com plus 48\n * sibling domains; truepowersystems.com plus 16 state microsites).\n *\n * Precedence, per the platform site contract:\n * explicit `site` > NEXT_PUBLIC_SITE_URL host > window.location.host\n *\n * Deliberately usable on BOTH sides. The browser half seeds\n * `window.__SITE_KIT_SITE__` (see `shared/publishCredential`); the build half\n * tags the sitemap sync, the llms.txt reads and every blog read (see\n * `sitemap/index`, `llms/writeLLMsTxt`, and `./site-param`, which appends the\n * host to API requests) so a build describes only its OWN host. Server-side the\n * `window` branch simply never fires, which leaves the NEXT_PUBLIC_SITE_URL\n * host — the right source at build time.\n *\n * Do not re-fork this. Before it existed the build path sent no `site` at all,\n * so every site in a 49-domain network read whichever sibling's seo_pages row\n * the database happened to hand back for a shared path like `/` — bd-aec.com\n * shipped soqwriters.com's homepage copy in its llms.txt.\n *\n * Its own file, not `./contract`, for the same bundle reason as\n * `./normalize`: browser entries import this without dragging the contract's\n * JSON schema along.\n */\nimport { normalizeSiteHost } from './normalize'\n\n/**\n * Resolve the sub-site host. Always returns a string (possibly empty), already\n * normalized with the shared contract (lowercase host, no protocol/path/port)\n * so it matches exactly what the Sonor API stores.\n */\nexport function resolveSiteHost(explicitSite?: string): string {\n const envSiteUrl =\n typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_SITE_URL : undefined\n\n let resolved = explicitSite ?? ''\n if (!resolved && envSiteUrl) {\n try {\n resolved = new URL(envSiteUrl).host\n } catch {\n /* malformed URL — fall through to client-side host */\n }\n }\n if (!resolved && typeof window !== 'undefined') {\n resolved = window.location.host\n }\n return normalizeSiteHost(resolved) ?? ''\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/blog/processBlogHtml.ts"],"names":[],"mappings":";;;AAgBO,SAAS,uBAAuB,OAAA,EAAyB;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,OAAO,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,GAAI,OAAA,GAAU,WAAW,OAAO,CAAA,CAAA;AACtE,IAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,IAAI,CAAA;AACtB,IAAA,OAAO,EAAE,QAAA,CAAS,OAAA,CAAQ,SAAA,EAAW,EAAE,EAAE,WAAA,EAAY;AAAA,EACvD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAA;AAAA,EACT;AACF;AAQO,IAAM,iBAAA,GAAoB;AAM1B,SAAS,mBAAmB,QAAA,EAA2B;AAC5D,EAAA,MAAM,QAAA,GAAW,UAAU,IAAA,EAAK;AAChC,EAAA,IAAI,UAAU,OAAO,QAAA;AACrB,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,oBAAA,EAAsB,IAAA,EAAK;AACpD,EAAA,IAAI,MAAM,OAAO,IAAA;AACjB,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,GAAA,CAAI,QAAA,EAAU,IAAA,EAAK;AACxC,EAAA,IAAI,MAAM,OAAO,IAAA;AACjB,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,UAAA,EAAY,IAAA,EAAK;AAC5C,EAAA,IAAI,MAAA,SAAe,MAAA,CAAO,UAAA,CAAW,MAAM,CAAA,GAAI,MAAA,GAAS,WAAW,MAAM,CAAA,CAAA;AACzE,EAAA,OAAO,EAAA;AACT;AAOO,SAAS,sBAAA,CAAuB,MAAc,OAAA,EAAyB;AAC5E,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,OAAA,EAAS,OAAO,IAAA;AAC9B,EAAA,MAAM,QAAA,GAAW,uBAAuB,OAAO,CAAA;AAC/C,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AAEtB,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,gBAAA,EAAkB,CAAC,OAAO,KAAA,KAAkB;AAC9D,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,KAAA,CAAM,gCAAgC,CAAA;AAC9D,IAAA,IAAI,CAAC,SAAA,EAAW,OAAO,CAAA,EAAA,EAAK,KAAK,CAAA,CAAA,CAAA;AACjC,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,CAAC,CAAA,CAAE,IAAA,EAAK;AAC/B,IAAA,IAAI,CAAC,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,KAAK,KAAK,CAAA,CAAA,CAAA;AAElD,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,IAAI,CAAA;AACxB,MAAA,MAAM,WAAW,GAAA,CAAI,QAAA,CAAS,QAAQ,SAAA,EAAW,EAAE,EAAE,WAAA,EAAY;AACjE,MAAA,IAAI,QAAA,KAAa,QAAA,EAAU,OAAO,CAAA,EAAA,EAAK,KAAK,CAAA,CAAA,CAAA;AAC5C,MAAA,IAAI,gBAAgB,IAAA,CAAK,KAAK,CAAA,EAAG,OAAO,KAAK,KAAK,CAAA,CAAA,CAAA;AAClD,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,MAAA,OAAO,MAAM,OAAO,CAAA,2CAAA,CAAA;AAAA,IACtB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAK,KAAK,CAAA,CAAA,CAAA;AAAA,IACnB;AAAA,EACF,CAAC,CAAA;AACH","file":"chunk-K4TCEAUX.js","sourcesContent":["/**\n * Post-process Portal/blog HTML for safe, consistent link behavior.\n * Server-safe string transforms only (no DOM).\n */\n\n/**\n * The host `addExternalLinkTargets` compares links against to decide whether a\n * link is internal (same tab) or external (`target=\"_blank\"`). Lowercased,\n * leading `www.` stripped, so `www.example.com` and `example.com` count as the\n * same site; returns '' for anything unparseable.\n *\n * NOT the multi-site host normalizer. That is `normalizeSiteHost` in\n * `sites/normalize`, which keeps `www.` because `www.example.com` and\n * `example.com` can be different sub-sites of one Sonor project. This used to\n * share that name, so the two were easy to swap by accident.\n */\nexport function linkClassificationHost(siteUrl: string): string {\n try {\n const base = siteUrl.startsWith('http') ? siteUrl : `https://${siteUrl}`\n const u = new URL(base)\n return u.hostname.replace(/^www\\./i, '').toLowerCase()\n } catch {\n return ''\n }\n}\n\n/**\n * @deprecated Renamed to {@link linkClassificationHost} (same behavior: strips\n * `www.`). It shared a name with the multi-site `normalizeSiteHost` from\n * `sites/normalize`, which keeps `www.`. Kept so existing imports from\n * `@sonordev/site-kit/blog` still work.\n */\nexport const normalizeSiteHost = linkClassificationHost\n\n/**\n * Resolve canonical site URL for link classification.\n * Pass `explicit` from props when env vars are not set (e.g. non-Vercel hosts).\n */\nexport function resolveBlogSiteUrl(explicit?: string): string {\n const fromProp = explicit?.trim()\n if (fromProp) return fromProp\n const next = process.env.NEXT_PUBLIC_SITE_URL?.trim()\n if (next) return next\n const site = process.env.SITE_URL?.trim()\n if (site) return site\n const vercel = process.env.VERCEL_URL?.trim()\n if (vercel) return vercel.startsWith('http') ? vercel : `https://${vercel}`\n return ''\n}\n\n/**\n * Add `target=\"_blank\"` and `rel=\"noopener noreferrer\"` to external http(s) links.\n * Same registrable host (with or without `www`) is treated as internal.\n * Skips anchors that already set `target`.\n */\nexport function addExternalLinkTargets(html: string, siteUrl: string): string {\n if (!html || !siteUrl) return html\n const siteHost = linkClassificationHost(siteUrl)\n if (!siteHost) return html\n\n return html.replace(/<a\\b([^>]*)>/gi, (_full, attrs: string) => {\n const hrefMatch = attrs.match(/\\bhref\\s*=\\s*([\"'])([^\"']*)\\1/i)\n if (!hrefMatch) return `<a${attrs}>`\n const href = hrefMatch[2].trim()\n if (!/^https?:\\/\\//i.test(href)) return `<a${attrs}>`\n\n try {\n const url = new URL(href)\n const linkHost = url.hostname.replace(/^www\\./i, '').toLowerCase()\n if (linkHost === siteHost) return `<a${attrs}>`\n if (/\\btarget\\s*=/i.test(attrs)) return `<a${attrs}>`\n const trimmed = attrs.trim()\n return `<a ${trimmed} target=\"_blank\" rel=\"noopener noreferrer\">`\n } catch {\n return `<a${attrs}>`\n }\n })\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/blog/excerpt.ts","../src/blog/server-core.ts"],"names":["resolveServerApiConfig","fetchBlogPostBySlug","notFound","withSiteParam","fetchBlogCategories","resolveBlogPostUrl","createBlogRoutes","resolveBlogUrl","blogCategorySlug","resolveBlogArtwork","description","fetchRelatedBlogPosts"],"mappings":"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","file":"chunk-LMEUYRRR.js","sourcesContent":["/**\n * Excerpt sanitizing for blog metadata, schema and feeds.\n *\n * Pure (no `server-only` chain) so it is unit-testable and safe to reuse.\n * Source of truth for turning a stored excerpt into display-safe prose.\n */\n\n/**\n * Turn a stored excerpt into something safe to put in a meta description,\n * a schema `description`, or an RSS `<description>`.\n *\n * The AI blog pipeline stores content as JSON sections\n * (`{\"heading\": \"## ...\", \"content\": \"## ...\\n\\nIf you're asking **how**...\"}`)\n * and that value has been shipping verbatim into <meta name=\"description\">.\n * nkylawfirm.com had three live articles whose SERP snippet was raw JSON with\n * markdown asterisks and literal \\n. Parse the sections out, strip markdown,\n * and cap at a sentence boundary.\n */\nexport function sanitizeExcerpt(raw: string | undefined | null, maxLength = 160): string {\n if (!raw) return ''\n let text = String(raw).trim()\n\n // JSON section(s) — take the prose, not the heading, and never the braces.\n if (text.startsWith('{') || text.startsWith('[')) {\n const fromSections = (value: unknown): string => {\n const list = Array.isArray(value) ? value : [value]\n return list\n .map((s) => (s && typeof s === 'object' ? (s as any).content || (s as any).heading || '' : ''))\n .filter(Boolean)\n .join(' ')\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n // Concatenated objects: \"{...}\\n\\n{...}\" — take the first.\n const first = text.split(/\\}\\s*[\\r\\n]+\\s*\\{/)[0]\n try {\n parsed = JSON.parse(first.endsWith('}') ? first : `${first}}`)\n } catch {\n parsed = null\n }\n }\n text = parsed ? fromSections(parsed) : text.replace(/[{}\"[\\]]/g, ' ')\n }\n\n const plain = text\n .replace(/```[\\s\\S]*?```/g, ' ') // fenced code\n .replace(/!\\[[^\\]]*\\]\\([^)]*\\)/g, ' ') // images\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1') // links -> text\n .replace(/^#{1,6}\\s+/gm, '') // headings\n .replace(/[*_~`>]/g, '') // emphasis / quotes / code\n .replace(/\\\\n/g, ' ') // literal escaped newlines\n .replace(/\\s+/g, ' ')\n .trim()\n\n if (plain.length <= maxLength) return plain\n\n // Prefer a sentence boundary, then a word boundary.\n const window = plain.slice(0, maxLength + 1)\n const lastStop = Math.max(window.lastIndexOf('. '), window.lastIndexOf('? '), window.lastIndexOf('! '))\n if (lastStop > maxLength * 0.5) return window.slice(0, lastStop + 1).trim()\n\n const cut = plain.slice(0, maxLength)\n const lastSpace = cut.lastIndexOf(' ')\n return `${(lastSpace > maxLength * 0.6 ? cut.slice(0, lastSpace) : cut).replace(/[\\s,;:—–-]+$/, '')}…`\n}\n","/**\n * @sonordev/site-kit/blog/server\n *\n * Data and metadata helpers only (no React components). For `BlogPost` / `BlogList`\n * in the App Router, import from `@sonordev/site-kit/blog/server-ui` so Next never\n * treats `getAllBlogSlugs` / `generateBlogStaticParams` as client code.\n */\n\nimport type { Metadata } from 'next'\nimport { notFound } from 'next/navigation'\nimport type { BlogPost as BlogPostRecord, BlogAuthor, TocItem, TopicCluster, ClusterNavigation } from './types'\n\n/**\n * Server key/URL resolution — SINGLE SOURCE OF TRUTH.\n *\n * Every fetcher below resolves its API config through `resolveServerApiConfig`.\n * Do NOT re-fork a local `getConfig()` here: this file used to carry a drifted\n * copy of that helper, and the copy grew a `process.env.NEXT_PUBLIC_SONOR_API_KEY`\n * fallback the canonical one never had. Because Next inlines `NEXT_PUBLIC_*` into\n * every bundle a module lands in, and this module was reachable from the\n * client-stamped `@sonordev/site-kit/blog` barrel, that fallback published the\n * site's raw `sonor_` project key to the browser. One diverging copy, one leaked\n * credential.\n *\n * This import also carries `server-only` transitively, which is the point: if a\n * client component ever reaches this module again, the consumer's build fails\n * loudly instead of silently shipping an `x-api-key` fetch to visitors.\n */\nimport { resolveServerApiConfig } from '../server/server-fetch'\nimport { withSiteParam } from '../sites/site-param'\nimport { fetchBlogCategories, fetchBlogPostBySlug, fetchRelatedBlogPosts } from './public-reads'\nimport { sanitizeExcerpt } from './excerpt'\nimport { resolveBlogArtwork } from './artwork'\nimport { blogCategorySlug, createBlogRoutes, resolveBlogPostUrl, resolveBlogUrl, type BlogRoutingOptions, type BlogRoutePost } from './routes'\n\nexport { sanitizeExcerpt }\n\n// ============================================================================\n// SEO VALIDATION UTILITIES\n// ============================================================================\n\nexport const SEO_LIMITS = {\n title: { min: 30, max: 60, recommended: 55 },\n metaDescription: { min: 120, max: 160, recommended: 155 },\n excerpt: { min: 100, max: 300, recommended: 200 },\n slug: { max: 75 },\n focusKeyphrase: { min: 2, max: 4, words: true }, // 2-4 words\n} as const\n\nexport interface SeoValidationResult {\n field: string\n value: string\n length: number\n limit: { min?: number; max: number; recommended?: number }\n status: 'good' | 'warning' | 'error'\n message: string\n}\n\n/**\n * Validate SEO title (60 chars max, keyword-first recommended)\n */\nexport function validateSeoTitle(title: string, focusKeyphrase?: string): SeoValidationResult {\n const length = title.length\n let status: SeoValidationResult['status'] = 'good'\n let message = 'Title length is optimal'\n\n if (length > SEO_LIMITS.title.max) {\n status = 'error'\n message = `Title is ${length - SEO_LIMITS.title.max} characters too long (max ${SEO_LIMITS.title.max})`\n } else if (length < SEO_LIMITS.title.min) {\n status = 'warning'\n message = `Title is short (${length}/${SEO_LIMITS.title.min} min)`\n } else if (focusKeyphrase && !title.toLowerCase().includes(focusKeyphrase.toLowerCase())) {\n status = 'warning'\n message = 'Focus keyphrase not found in title'\n } else if (focusKeyphrase && !title.toLowerCase().startsWith(focusKeyphrase.toLowerCase())) {\n status = 'warning'\n message = 'Title should start with focus keyphrase for best results'\n }\n\n return { field: 'title', value: title, length, limit: SEO_LIMITS.title, status, message }\n}\n\n/**\n * Validate meta description (150-160 chars, benefit-driven)\n */\nexport function validateMetaDescription(description: string, focusKeyphrase?: string): SeoValidationResult {\n const length = description.length\n let status: SeoValidationResult['status'] = 'good'\n let message = 'Meta description is optimal'\n\n if (length > SEO_LIMITS.metaDescription.max) {\n status = 'error'\n message = `Description will be truncated (${length}/${SEO_LIMITS.metaDescription.max})`\n } else if (length < SEO_LIMITS.metaDescription.min) {\n status = 'warning'\n message = `Description is short (${length}/${SEO_LIMITS.metaDescription.min} min)`\n } else if (focusKeyphrase && !description.toLowerCase().includes(focusKeyphrase.toLowerCase())) {\n status = 'warning'\n message = 'Focus keyphrase not found in description'\n }\n\n return { field: 'meta_description', value: description, length, limit: SEO_LIMITS.metaDescription, status, message }\n}\n\n/**\n * Full SEO validation for a blog post\n */\nexport function validateBlogPostSeo(post: Partial<BlogPostRecord>): SeoValidationResult[] {\n const results: SeoValidationResult[] = []\n const focusKeyphrase = post.focus_keyphrase\n\n if (post.meta_title || post.title) {\n results.push(validateSeoTitle(post.meta_title || post.title || '', focusKeyphrase))\n }\n\n if (post.meta_description) {\n results.push(validateMetaDescription(post.meta_description, focusKeyphrase))\n }\n\n // Check H1 alignment (title should match keyword)\n if (post.title && focusKeyphrase) {\n const titleLower = post.title.toLowerCase()\n const keyLower = focusKeyphrase.toLowerCase()\n if (!titleLower.includes(keyLower)) {\n results.push({\n field: 'h1_alignment',\n value: post.title,\n length: post.title.length,\n limit: { max: 100 },\n status: 'warning',\n message: 'H1 (title) should include the focus keyphrase',\n })\n }\n }\n\n // Check word count (1200-2500 recommended for long-form)\n if (post.word_count !== undefined) {\n let wordStatus: SeoValidationResult['status'] = 'good'\n let wordMessage = 'Content length is optimal for SEO'\n \n if (post.word_count < 600) {\n wordStatus = 'error'\n wordMessage = `Content is thin (${post.word_count} words). Aim for 1200+ words.`\n } else if (post.word_count < 1200) {\n wordStatus = 'warning'\n wordMessage = `Content is short (${post.word_count} words). Long-form (1200-2500) ranks better.`\n } else if (post.word_count > 2500) {\n wordMessage = `Comprehensive content (${post.word_count} words). Consider splitting into a topic cluster.`\n }\n\n results.push({\n field: 'word_count',\n value: String(post.word_count),\n length: post.word_count,\n limit: { min: 1200, max: 2500, recommended: 1800 },\n status: wordStatus,\n message: wordMessage,\n })\n }\n\n return results\n}\n\n// ============================================================================\n// DATA FETCHING\n// ============================================================================\n//\n// Every blog read below tells the API which sub-site is asking: the URL (or,\n// for POST related, the body) carries `site=<host>` via `sites/site-param`.\n// On a multi-site project a post with no site is project-wide and a post\n// tagged `bd-charlotte.com` shows only on that host, so a read without `site`\n// would hand one microsite its siblings' posts. The host resolves from\n// NEXT_PUBLIC_SITE_URL, which every microsite already sets; fetchers that take\n// arguments also accept an explicit `{ site }` override. `getAllBlogSlugs` and\n// `getAllAuthorSlugs` stay zero-arg so they remain assignable to Next's\n// `generateStaticParams` (see 5.7.0), so they resolve from the env only.\n\n/**\n * Fetch a blog post by slug (server-side)\n *\n * @param options.site - Sub-site host override. Omit to resolve from\n * NEXT_PUBLIC_SITE_URL.\n */\nexport async function getBlogPost(\n slug: string,\n options?: { site?: string }\n): Promise<BlogPostRecord | null> {\n const config = resolveServerApiConfig()\n if (!config) {\n console.warn('[Blog] No API key configured')\n return null\n }\n return fetchBlogPostBySlug({ ...config, site: options?.site }, slug)\n}\n\n/**\n * Fetch a blog post by slug, or answer 404 when there isn't one.\n *\n * `getBlogPost` returns null for a mistyped or deleted slug, and the page\n * used to carry on rendering a \"not found\" message with HTTP 200 and an\n * indexable title: a soft 404 on every bad post URL (vsf-consulting-nextjs\n * /insights, found 2026-09-15). This calls Next's `notFound()` instead, so the\n * route answers 404 with `noindex`.\n *\n * Call it from `generateMetadata` (or the page) before rendering `BlogPost`:\n * metadata resolves before anything streams, so the status is a real 404.\n * The fetch is the same one `BlogPost` and `generateBlogPostMetadata` make,\n * and Next dedupes it within the render.\n *\n * @example\n * ```tsx\n * export async function generateMetadata({ params }) {\n * const { slug } = await params\n * await requireBlogPost(slug)\n * return generateBlogPostMetadata(slug, { siteName: 'Acme' })\n * }\n * ```\n */\nexport async function requireBlogPost(\n slug: string,\n options?: { site?: string }\n): Promise<BlogPostRecord> {\n const post = await getBlogPost(slug, options)\n if (!post) notFound()\n return post\n}\n\n/**\n * Fetch all blog post slugs for static generation\n */\nexport async function getAllBlogSlugs(): Promise<\n { slug: string; id?: string; last_modified?: string; article_type?: string }[]\n> {\n const config = resolveServerApiConfig()\n if (!config) {\n console.warn('[Blog] No API key configured')\n return []\n }\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/slugs`), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n })\n\n if (!response.ok) return []\n\n const data = await response.json()\n return data.slugs || []\n } catch (error) {\n console.error('[Blog] Error fetching slugs:', error)\n return []\n }\n}\n\n/**\n * Fetch blog categories\n */\nexport async function getBlogCategories(\n options?: { site?: string }\n): Promise<{ name: string; slug: string; post_count: number }[]> {\n const config = resolveServerApiConfig()\n if (!config) return []\n // The public endpoint always returns post_count; BlogCategory types it\n // optional because the dashboard shape omits it.\n return (await fetchBlogCategories({ ...config, site: options?.site })) as {\n name: string\n slug: string\n post_count: number\n }[]\n}\n\n// ============================================================================\n// METADATA GENERATION\n// ============================================================================\n\nexport interface BlogMetadataOptions extends BlogRoutingOptions {\n /** Site name for og:site_name */\n siteName?: string\n /** Base URL of the site */\n siteUrl?: string\n /** URL path prefix for blog index and posts (default `/blog`, e.g. `/insights`) */\n blogBasePath?: string\n /** Default OG image */\n defaultImage?: string\n /** Twitter handle */\n twitterHandle?: string\n}\n\nexport interface BlogPostMetadataOptions extends BlogMetadataOptions {\n /**\n * Declare the featured image (or `defaultImage`) as `openGraph.images` and\n * `twitter.images`. Default true.\n *\n * Pass false when the post route has its own card, an\n * `opengraph-image.tsx` beside the page. Next lets declared images beat\n * that file, so leaving them in silently swaps the post's card for the raw\n * photo. With false the `images` keys are left out entirely, not set to\n * undefined: Next checks `hasOwnProperty('images')`, so even an undefined\n * value hides the card.\n */\n images?: boolean\n /**\n * What to do when the post doesn't exist. Default true: call Next's\n * `notFound()`, so the route answers 404. False returns placeholder\n * metadata marked `noindex` instead, for a page that renders its own\n * missing-post state.\n */\n notFound?: boolean\n}\n\n/**\n * Generate metadata for a blog post page\n */\nexport async function generateBlogPostMetadata(\n slug: string,\n options: BlogPostMetadataOptions = {}\n): Promise<Metadata> {\n const post = await getBlogPost(slug)\n const { siteName, siteUrl = '', defaultImage, twitterHandle } = options\n if (!post) {\n // A missing post used to get an indexable \"Post Not Found\" title with a\n // 200, which is a soft 404 on every mistyped or deleted post URL.\n if (options.notFound !== false) notFound()\n return {\n title: 'Post Not Found',\n description: 'The requested blog post could not be found.',\n robots: { index: false, follow: true },\n }\n }\n\n const title = post.meta_title || post.title\n const description = sanitizeExcerpt(post.meta_description || post.excerpt)\n // og_image / featured_image_width / featured_image_height never existed in\n // blog_posts (verified against the live schema) — reading them always\n // produced undefined. Resolution goes straight to featured_image.\n const image = options.images === false ? undefined : post.featured_image || defaultImage\n const url = resolveBlogPostUrl(post, options)\n\n return {\n title,\n description,\n openGraph: {\n title: post.og_title || title,\n description: post.og_description || description,\n url,\n siteName,\n type: 'article',\n publishedTime: post.published_at,\n authors: post.author ? [typeof post.author === 'string' ? post.author : post.author.name] : undefined,\n ...(image && {\n images: [\n {\n url: image,\n width: 1200,\n height: 630,\n alt: post.featured_image_alt || post.title,\n },\n ],\n }),\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n ...(image && { images: [image] }),\n creator: twitterHandle,\n },\n alternates: {\n canonical: url,\n },\n other: {\n 'article:published_time': post.published_at || '',\n 'article:section': typeof post.category === 'string' ? post.category : (post.category?.name || ''),\n },\n }\n}\n\n/**\n * Generate metadata for the blog index page\n */\nexport function generateBlogIndexMetadata(options: BlogMetadataOptions & {\n title?: string\n description?: string\n}): Metadata {\n const {\n title = 'Blog',\n description = 'Read our latest articles and insights.',\n siteName,\n siteUrl = '',\n defaultImage,\n twitterHandle,\n } = options\n const base = createBlogRoutes(options).index\n\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n url: resolveBlogUrl(base, siteUrl),\n siteName,\n type: 'website',\n ...(defaultImage && { images: [{ url: defaultImage }] }),\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n ...(defaultImage && { images: [defaultImage] }),\n creator: twitterHandle,\n },\n }\n}\n\n/**\n * Generate metadata for a category page\n */\nexport function generateBlogCategoryMetadata(\n categoryName: string,\n options: BlogMetadataOptions & { categorySlug?: string } = {}\n): Metadata {\n const { siteName, siteUrl = '', defaultImage } = options\n const title = `${categoryName} - Blog`\n const description = `Browse all posts in ${categoryName}.`\n // The URL wants the route slug, not the display name: lowercasing \"Case\n // Studies\" produced /blog/category/case%20studies against a real route of\n // /blog/category/case-studies. Callers that have the slug pass it; the\n // fallback slugifies the name the same way the dashboard does.\n const slug =\n options.categorySlug ??\n categoryName\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n url: resolveBlogUrl(createBlogRoutes(options).category(slug), siteUrl),\n siteName,\n type: 'website',\n ...(defaultImage && { images: [{ url: defaultImage }] }),\n },\n }\n}\n\n// ============================================================================\n// STATIC PARAMS GENERATION\n// ============================================================================\n\n/**\n * What Next passes to a route's `generateStaticParams`: the parent segments'\n * params. The blog helpers ignore it; it's typed so they can be exported\n * directly as `generateStaticParams`.\n */\nexport interface NextStaticParamsProps {\n params: unknown\n}\n\nfunction isNextStaticParamsProps(value: BlogRoutingOptions | NextStaticParamsProps): value is NextStaticParamsProps {\n return 'params' in value\n}\n\n/**\n * Generate static params for blog post pages.\n *\n * Export it directly for the default `/blog/[slug]` routes:\n *\n * export const generateStaticParams = generateBlogStaticParams\n *\n * With routing options, wrap it:\n *\n * export function generateStaticParams() {\n * return generateBlogStaticParams(routing)\n * }\n */\nexport function generateBlogStaticParams(options?: BlogRoutingOptions): Promise<{ slug: string; category?: string }[]>\n// Next's props overload must stay LAST. Next 16 checks the export two ways:\n// `.next/types/validator.ts` needs it assignable to `(props: { params }) => …`,\n// and on webpack builds `NextTypesPlugin` infers the first argument (from the\n// last overload) and requires it to be `{ params }`. A single optional\n// `BlogRoutingOptions` parameter fails both (site-kit 5.4.0–5.6.1).\nexport function generateBlogStaticParams(props: NextStaticParamsProps): Promise<{ slug: string; category?: string }[]>\nexport async function generateBlogStaticParams(\n arg: BlogRoutingOptions | NextStaticParamsProps = {},\n): Promise<{ slug: string; category?: string }[]> {\n // Called by Next as `generateStaticParams({ params })`: use the defaults.\n const options: BlogRoutingOptions = isNextStaticParamsProps(arg) ? {} : arg\n const posts = await getBlogRouteEntries(options)\n return posts.map(post => {\n const category = blogCategorySlug(post.category || options.category)\n return { slug: post.slug, ...(options.includeCategoryInPath && category ? { category } : {}) }\n })\n}\n\n/**\n * Generate static params for category pages. Takes no arguments, so it can be\n * exported directly:\n *\n * export const generateStaticParams = generateCategoryStaticParams\n */\nexport async function generateCategoryStaticParams(): Promise<{ category: string }[]> {\n const categories = await getBlogCategories()\n return categories.map((c) => ({ category: c.slug }))\n}\n\n// ============================================================================\n// SITEMAP GENERATION\n// ============================================================================\n\ninterface SitemapEntry {\n url: string\n lastModified?: Date\n changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n priority?: number\n}\n\n/**\n * Generate sitemap entries for blog posts\n */\nexport interface BlogSitemapOptions extends BlogRoutingOptions {\n includeCategories?: boolean\n includeClusters?: boolean\n}\n\n/** Sitemaps need canonicals; nested/custom static params also need full route records. */\nasync function getBlogRouteEntries(options: BlogRoutingOptions, withCanonicals = false): Promise<Array<BlogRoutePost & { last_modified?: string; article_type?: string }>> {\n if (withCanonicals || options.includeCategoryInPath || options.postPath) {\n return (await getAllBlogPosts({ maxPosts: Infinity })).map(post => ({ ...post, last_modified: post.updated_at }))\n }\n return getAllBlogSlugs()\n}\n\nexport async function generateBlogSitemap(\n siteUrl: string,\n options: BlogSitemapOptions = {},\n): Promise<SitemapEntry[]> {\n const slugs = await getBlogRouteEntries(options, true)\n const categories = options.includeCategories === false ? [] : await getBlogCategories()\n const clusters = options.includeClusters === false ? [] : await getTopicClusters()\n\n const entries: SitemapEntry[] = [\n // Blog index\n {\n url: resolveBlogUrl(createBlogRoutes(options).index, siteUrl),\n changeFrequency: 'daily',\n priority: 0.8,\n },\n ]\n\n // Cluster landing pages\n clusters.forEach((cluster) => {\n entries.push({\n url: resolveBlogUrl(createBlogRoutes(options).cluster(cluster.cluster_slug), siteUrl),\n changeFrequency: 'weekly',\n priority: 0.8,\n })\n })\n\n // Category pages\n categories.forEach((cat) => {\n entries.push({\n url: resolveBlogUrl(createBlogRoutes(options).category(cat.slug), siteUrl),\n changeFrequency: 'weekly',\n priority: 0.6,\n })\n })\n\n // Individual posts — pillar posts get higher priority (0.9 vs 0.7)\n slugs.forEach((post) => {\n const isPillar = (post as any).article_type === 'pillar'\n entries.push({\n url: resolveBlogPostUrl(post, { ...options, siteUrl }),\n lastModified: post.last_modified ? new Date(post.last_modified) : undefined,\n changeFrequency: isPillar ? 'weekly' : 'monthly',\n priority: isPillar ? 0.9 : 0.7,\n })\n })\n\n return entries\n}\n\n// ============================================================================\n// JSON-LD SCHEMA GENERATION\n// ============================================================================\n\n/**\n * Generate JSON-LD schema for a blog post\n */\nexport function generateBlogPostSchema(\n post: BlogPostRecord,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string; logoUrl?: string } = {}\n): object {\n const { siteUrl = '', siteName, logoUrl } = options\n\n const schema: any = {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: post.title,\n description: sanitizeExcerpt(post.excerpt || post.meta_description),\n url: resolveBlogPostUrl(post, { ...options, siteUrl }),\n datePublished: post.published_at,\n dateModified: post.updated_at,\n author: {\n '@type': 'Person',\n name: typeof post.author === 'string' ? post.author : post.author?.name,\n },\n }\n\n const articleImage = resolveBlogArtwork(post, 'article').src\n if (articleImage) schema.image = articleImage\n\n if (siteName || logoUrl) {\n schema.publisher = {\n '@type': 'Organization',\n name: siteName,\n logo: logoUrl\n ? {\n '@type': 'ImageObject',\n url: logoUrl,\n }\n : undefined,\n }\n }\n\n // Add FAQ schema if present\n if (post.faq_items && Array.isArray(post.faq_items) && post.faq_items.length > 0) {\n schema.mainEntity = {\n '@type': 'FAQPage',\n mainEntity: post.faq_items.map((faq: { question: string; answer: string }) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n }\n }\n\n return schema\n}\n\n/**\n * Generate JSON-LD schema for blog index/listing page\n */\nexport function generateBlogListSchema(\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string; description?: string } = {}\n): object {\n const { siteUrl = '', siteName, description } = options\n\n return {\n '@context': 'https://schema.org',\n '@type': 'Blog',\n name: siteName ? `${siteName} Blog` : 'Blog',\n description: description || 'Our latest articles and insights.',\n url: resolveBlogUrl(createBlogRoutes(options).index, siteUrl),\n }\n}\n\n// ============================================================================\n// FAQ SCHEMA (for PAA rankings)\n// ============================================================================\n\n/**\n * Generate standalone FAQ schema (great for PAA inclusion)\n */\nexport function generateFaqSchema(\n faqItems: { question: string; answer: string }[]\n): object | null {\n if (!faqItems || faqItems.length === 0) return null\n\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: faqItems.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n }\n}\n\n// ============================================================================\n// HOWTO SCHEMA (for step-by-step posts)\n// ============================================================================\n\nexport interface HowToStep {\n name: string\n text: string\n image?: string\n url?: string\n}\n\n/**\n * Generate HowTo schema for step-by-step posts\n */\nexport function generateHowToSchema(\n post: BlogPostRecord,\n steps: HowToStep[],\n options: BlogRoutingOptions & { siteUrl?: string } = {}\n): object {\n const { siteUrl = '' } = options\n\n return {\n '@context': 'https://schema.org',\n '@type': 'HowTo',\n name: post.title,\n description: sanitizeExcerpt(post.excerpt || post.meta_description),\n image: resolveBlogArtwork(post, 'article').src,\n totalTime: post.reading_time ? `PT${post.reading_time}M` : undefined,\n step: steps.map((step, index) => ({\n '@type': 'HowToStep',\n position: index + 1,\n name: step.name,\n text: step.text,\n image: step.image,\n url: step.url || `${resolveBlogPostUrl(post, { ...options, siteUrl }).split(\"#\")[0]}#step-${index + 1}`,\n })),\n }\n}\n\n// ============================================================================\n// RSS FEED GENERATION\n// ============================================================================\n\nexport interface RssFeedOptions extends BlogRoutingOptions {\n siteUrl: string\n siteName: string\n description?: string\n language?: string\n copyright?: string\n managingEditor?: string\n webMaster?: string\n ttl?: number // Time to live in minutes\n imageUrl?: string\n}\n\n/**\n * Maximum posts a feed will accumulate. Preserves the historical intent of the\n * old `?limit=100` request (which the API silently ignored — it only reads\n * `per_page`, capped at 50, so feeds were stuck at the 12-post default).\n */\nconst FEED_MAX_POSTS = 100\n\n/**\n * Fetch all published blog posts for RSS feed.\n *\n * Paginates with `per_page`/`page` — the only pagination params the deployed\n * controller reads — accumulating up to FEED_MAX_POSTS. A mid-loop failure\n * returns what was accumulated so far: a partial feed beats an empty one.\n */\nexport async function getAllBlogPosts(\n options: { maxPosts?: number; /** Sub-site host override (see getBlogPost). */ site?: string } = {}\n): Promise<BlogPostRecord[]> {\n const maxPosts = options.maxPosts ?? FEED_MAX_POSTS\n if (!(maxPosts > 0)) return []\n const config = resolveServerApiConfig()\n if (!config) {\n console.warn('[Blog] No API key configured')\n return []\n }\n const { apiUrl, apiKey } = config\n\n const posts: BlogPostRecord[] = []\n let page = 1\n try {\n while (posts.length < maxPosts) {\n const response = await fetch(\n withSiteParam(`${apiUrl}/public/blog/posts?per_page=50&page=${page}`, options.site),\n {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n }\n )\n\n if (!response.ok) break\n\n const data = await response.json()\n const batch = data.posts || []\n posts.push(...batch)\n if (!batch.length || !data.pagination?.has_next) break\n page += 1\n }\n } catch (error) {\n console.error('[Blog] Error fetching posts for RSS:', error)\n }\n return posts.slice(0, maxPosts)\n}\n\n/**\n * Escape XML special characters\n */\nfunction escapeXml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;')\n}\n\n/**\n * Strip HTML tags for RSS descriptions\n */\nfunction stripHtml(html: string): string {\n return html.replace(/<[^>]*>/g, '').trim()\n}\n\n/**\n * Generate RSS 2.0 feed XML\n */\nexport async function generateRssFeed(options: RssFeedOptions): Promise<string> {\n const {\n siteUrl,\n siteName,\n description = 'Latest blog posts and insights',\n language = 'en-us',\n copyright,\n managingEditor,\n webMaster,\n ttl = 60,\n imageUrl,\n } = options\n\n const posts = await getAllBlogPosts()\n const now = new Date().toUTCString()\n\n let rss = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n <title>${escapeXml(siteName)}</title>\n <link>${siteUrl}</link>\n <description>${escapeXml(description)}</description>\n <language>${language}</language>\n <lastBuildDate>${now}</lastBuildDate>\n <pubDate>${now}</pubDate>\n <ttl>${ttl}</ttl>\n <atom:link href=\"${escapeXml(resolveBlogUrl(createBlogRoutes(options).rss, siteUrl))}\" rel=\"self\" type=\"application/rss+xml\"/>\n`\n\n if (copyright) {\n rss += ` <copyright>${escapeXml(copyright)}</copyright>\\n`\n }\n\n if (managingEditor) {\n rss += ` <managingEditor>${escapeXml(managingEditor)}</managingEditor>\\n`\n }\n\n if (webMaster) {\n rss += ` <webMaster>${escapeXml(webMaster)}</webMaster>\\n`\n }\n\n if (imageUrl) {\n rss += ` <image>\n <url>${imageUrl}</url>\n <title>${escapeXml(siteName)}</title>\n <link>${siteUrl}</link>\n </image>\\n`\n }\n\n // Add each post as an item with FULL content (not excerpts)\n for (const post of posts) {\n const postUrl = resolveBlogPostUrl(post, { ...options, siteUrl })\n const pubDate = post.published_at ? new Date(post.published_at).toUTCString() : now\n const author = typeof post.author === 'string' ? post.author : post.author?.name || 'Unknown'\n \n // Use full content_html if available, otherwise content\n const fullContent = post.content_html || post.content || ''\n const description = sanitizeExcerpt(post.excerpt, 300) || stripHtml(fullContent).substring(0, 300) + '...'\n\n rss += ` <item>\n <title>${escapeXml(post.title)}</title>\n <link>${escapeXml(postUrl)}</link>\n <guid isPermaLink=\"true\">${escapeXml(postUrl)}</guid>\n <description>${escapeXml(description)}</description>\n <content:encoded><![CDATA[${fullContent}]]></content:encoded>\n <pubDate>${pubDate}</pubDate>\n <author>${escapeXml(author)}</author>\n`\n\n if (post.category) {\n const categoryName = typeof post.category === 'string' ? post.category : post.category.name\n rss += ` <category>${escapeXml(categoryName)}</category>\\n`\n }\n\n if (post.tags && Array.isArray(post.tags)) {\n for (const tag of post.tags) {\n const tagName = typeof tag === 'string' ? tag : tag.name\n rss += ` <category>${escapeXml(tagName)}</category>\\n`\n }\n }\n\n if (post.featured_image) {\n rss += ` <enclosure url=\"${post.featured_image}\" type=\"image/jpeg\"/>\\n`\n }\n\n rss += ` </item>\\n`\n }\n\n rss += ` </channel>\n</rss>`\n\n return rss\n}\n\n/**\n * Generate Atom feed XML\n */\nexport async function generateAtomFeed(options: RssFeedOptions): Promise<string> {\n const {\n siteUrl,\n siteName,\n description = 'Latest blog posts and insights',\n managingEditor,\n } = options\n\n const posts = await getAllBlogPosts()\n const now = new Date().toISOString()\n\n let atom = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<feed xmlns=\"http://www.w3.org/2005/Atom\">\n <title>${escapeXml(siteName)}</title>\n <subtitle>${escapeXml(description)}</subtitle>\n <link href=\"${escapeXml(resolveBlogUrl(createBlogRoutes(options).atom, siteUrl))}\" rel=\"self\"/>\n <link href=\"${siteUrl}\"/>\n <id>${escapeXml(resolveBlogUrl(createBlogRoutes(options).index, siteUrl))}</id>\n <updated>${now}</updated>\n`\n\n if (managingEditor) {\n atom += ` <author>\n <name>${escapeXml(managingEditor)}</name>\n </author>\\n`\n }\n\n for (const post of posts) {\n const postUrl = resolveBlogPostUrl(post, { ...options, siteUrl })\n const updated = post.updated_at || post.published_at || now\n const published = post.published_at || now\n const author = typeof post.author === 'string' ? post.author : post.author?.name || 'Unknown'\n const fullContent = post.content_html || post.content || ''\n\n atom += ` <entry>\n <title>${escapeXml(post.title)}</title>\n <link href=\"${escapeXml(postUrl)}\"/>\n <id>${escapeXml(postUrl)}</id>\n <updated>${new Date(updated).toISOString()}</updated>\n <published>${new Date(published).toISOString()}</published>\n <author>\n <name>${escapeXml(author)}</name>\n </author>\n <summary>${escapeXml(sanitizeExcerpt(post.excerpt, 300) || stripHtml(fullContent).substring(0, 300))}</summary>\n <content type=\"html\"><![CDATA[${fullContent}]]></content>\n </entry>\\n`\n }\n\n atom += `</feed>`\n\n return atom\n}\n\n// ============================================================================\n// TOPIC CLUSTER SUPPORT\n// ============================================================================\n\n/**\n * Fetch all active topic clusters for the project.\n * Uses the real cluster data from Portal API (not heuristic-based).\n */\nexport async function getTopicClusters(options?: { site?: string }): Promise<TopicCluster[]> {\n const config = resolveServerApiConfig()\n if (!config) return []\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/clusters`, options?.site), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n })\n if (!response.ok) return []\n const data = await response.json()\n\n return (data.clusters || []).map((c: any) => ({\n id: c.id,\n cluster_name: c.cluster_name,\n cluster_slug: c.cluster_slug,\n core_topic: c.core_topic,\n primary_entity: c.primary_entity,\n industry: c.industry,\n geo_target: c.geo_target,\n commercial_goal: c.commercial_goal,\n pillar_post_id: c.pillar_post_id,\n target_service_page: c.target_service_page,\n article_count: c.article_count,\n created_at: c.created_at,\n pillar: null,\n supports: [],\n interlink_map: [],\n }))\n } catch (error) {\n console.error('[Blog] Error fetching topic clusters:', error)\n return []\n }\n}\n\n/**\n * Fetch a single topic cluster with its pillar and support articles.\n */\nexport async function getTopicCluster(\n slug: string,\n options?: { site?: string }\n): Promise<TopicCluster | null> {\n const config = resolveServerApiConfig()\n if (!config) return null\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/clusters/${slug}`, options?.site), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n })\n if (!response.ok) return null\n const data = await response.json()\n\n return {\n id: data.id,\n cluster_name: data.cluster_name,\n cluster_slug: data.cluster_slug,\n core_topic: data.core_topic,\n primary_entity: data.primary_entity,\n industry: data.industry,\n geo_target: data.geo_target,\n commercial_goal: data.commercial_goal,\n // Authoritative column once the API's detail response ships it;\n // derived from the embedded (published) pillar until then.\n pillar_post_id: data.pillar_post_id ?? data.pillar?.id,\n target_service_page: data.target_service_page,\n article_count: data.article_count,\n created_at: data.created_at,\n pillar: data.pillar || null,\n supports: data.supports || [],\n interlink_map: data.interlink_map || [],\n }\n } catch (error) {\n console.error('[Blog] Error fetching topic cluster:', error)\n return null\n }\n}\n\n/**\n * Get cluster navigation context for a blog post.\n * Returns the cluster name, pillar link, and sibling articles.\n */\nexport function getClusterNavigation(post: BlogPostRecord): ClusterNavigation | null {\n if (!post.cluster_id && !post.cluster_slug) return null\n\n const isPillar = post.article_type === 'pillar'\n\n // Mirrored copy: BlogPost.tsx has an identical builder. Keep the two in\n // sync — they CANNOT be consolidated because blog/index is a CLIENT_ENTRY\n // that must never import blog/server-core (see build-entries.ts and\n // client-entry-boundary.test.ts).\n return {\n cluster_name: post.cluster_name || '',\n cluster_slug: post.cluster_slug || '',\n pillar: isPillar\n ? null\n : post.parent_pillar_slug\n ? {\n slug: post.parent_pillar_slug,\n title: post.parent_pillar_title || '',\n category: post.parent_pillar_category || undefined,\n }\n : null,\n siblings: post.sibling_articles || [],\n is_pillar: isPillar,\n }\n}\n\n/**\n * @deprecated Use getTopicCluster(slug) instead. This naive heuristic picks\n * the longest post as pillar. Real cluster data is now managed in the Sonor dashboard.\n */\nexport async function identifyTopicClusters(categorySlug: string): Promise<{\n pillar: BlogPostRecord\n supportingPosts: BlogPostRecord[]\n internalLinks: Array<{ from: string; to: string; anchor: string }>\n} | null> {\n console.warn(\n '[Blog] identifyTopicClusters() is deprecated. Use getTopicCluster(slug) for real cluster data managed in the Sonor dashboard.'\n )\n const posts = await getPostsByCategory(categorySlug)\n if (posts.length < 3) return null\n\n const sortedByLength = [...posts].sort((a, b) => (b.word_count || 0) - (a.word_count || 0))\n const pillar = sortedByLength[0]\n const supportingPosts = sortedByLength.slice(1)\n\n const internalLinks: Array<{ from: string; to: string; anchor: string }> = []\n for (const support of supportingPosts) {\n internalLinks.push({ from: pillar.slug, to: support.slug, anchor: support.title })\n internalLinks.push({ from: support.slug, to: pillar.slug, anchor: pillar.title })\n }\n\n return { pillar, supportingPosts, internalLinks }\n}\n\n/**\n * Fetch posts by category for topic cluster organization\n */\nexport async function getPostsByCategory(\n categorySlug: string,\n options?: { site?: string }\n): Promise<BlogPostRecord[]> {\n const config = resolveServerApiConfig()\n if (!config) return []\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/posts?category=${encodeURIComponent(categorySlug)}&per_page=50`, options?.site), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n })\n if (!response.ok) return []\n const data = await response.json()\n return data.posts || []\n } catch (error) {\n console.error('[Blog] Error fetching category posts:', error)\n return []\n }\n}\n\n// ============================================================================\n// CLUSTER SCHEMA GENERATION\n// ============================================================================\n\n/**\n * Generate ItemList schema for a cluster's pillar page.\n * Wraps supporting posts as a structured list Google can parse.\n */\nexport function generateClusterItemListSchema(\n cluster: TopicCluster,\n options: BlogRoutingOptions & { siteUrl?: string } = {}\n): object {\n const { siteUrl = '' } = options\n\n return {\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n name: cluster.cluster_name,\n description: `Articles about ${cluster.core_topic}`,\n numberOfItems: cluster.supports.length,\n itemListElement: cluster.supports.map((post, i) => ({\n '@type': 'ListItem',\n position: i + 1,\n name: post.title,\n url: resolveBlogPostUrl(post, { ...options, siteUrl }),\n })),\n }\n}\n\n/**\n * Generate Article schema with cluster relationship (isPartOf / hasPart).\n * - Support articles get `isPartOf` pointing to the pillar.\n * - Pillar articles get `hasPart` listing all supports.\n */\nexport function generateClusterArticleSchema(\n post: BlogPostRecord,\n cluster: TopicCluster,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string; logoUrl?: string } = {}\n): object {\n const { siteUrl = '', siteName, logoUrl } = options\n const isPillar = post.article_type === 'pillar'\n\n const schema: any = {\n '@context': 'https://schema.org',\n '@type': 'Article',\n headline: post.title,\n description: sanitizeExcerpt(post.excerpt || post.meta_description),\n url: resolveBlogPostUrl(post, { ...options, siteUrl }),\n datePublished: post.published_at,\n dateModified: post.updated_at,\n author: {\n '@type': 'Person',\n name: typeof post.author === 'string' ? post.author : (post.author as any)?.name,\n },\n keywords: post.focus_keyphrase || post.keywords,\n }\n\n const articleImage = resolveBlogArtwork(post, 'article').src\n if (articleImage) schema.image = articleImage\n\n if (siteName || logoUrl) {\n schema.publisher = {\n '@type': 'Organization',\n name: siteName,\n ...(logoUrl && { logo: { '@type': 'ImageObject', url: logoUrl } }),\n }\n }\n\n // Cluster relationships\n if (isPillar && cluster.supports.length > 0) {\n schema.hasPart = cluster.supports.map((s) => ({\n '@type': 'Article',\n headline: s.title,\n url: resolveBlogPostUrl(s, { ...options, siteUrl }),\n }))\n } else if (!isPillar && cluster.pillar) {\n schema.isPartOf = {\n '@type': 'Article',\n headline: cluster.pillar.title,\n url: resolveBlogPostUrl(cluster.pillar, { ...options, siteUrl }),\n }\n }\n\n // Geo targeting\n if (cluster.geo_target) {\n schema.spatialCoverage = {\n '@type': 'Place',\n name: cluster.geo_target,\n }\n }\n\n return schema\n}\n\n/**\n * Generate BreadcrumbList schema for a cluster-linked post.\n * Path: Home > Blog > Cluster > Article\n */\nexport function generateClusterBreadcrumbSchema(\n post: BlogPostRecord,\n cluster: TopicCluster,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string } = {}\n): object {\n const { siteUrl = '', siteName = 'Home' } = options\n\n const items: any[] = [\n { '@type': 'ListItem', position: 1, name: siteName, item: siteUrl },\n { '@type': 'ListItem', position: 2, name: 'Blog', item: resolveBlogUrl(createBlogRoutes(options).index, siteUrl) },\n {\n '@type': 'ListItem',\n position: 3,\n name: cluster.cluster_name,\n item: resolveBlogUrl(createBlogRoutes(options).cluster(cluster.cluster_slug), siteUrl),\n },\n {\n '@type': 'ListItem',\n position: 4,\n name: post.title,\n item: resolveBlogPostUrl(post, { ...options, siteUrl }),\n },\n ]\n\n return {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items,\n }\n}\n\n/**\n * Generate CollectionPage schema for a cluster landing page.\n */\nexport function generateClusterLandingSchema(\n cluster: TopicCluster,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string } = {}\n): object {\n const { siteUrl = '', siteName } = options\n\n return {\n '@context': 'https://schema.org',\n '@type': 'CollectionPage',\n name: cluster.cluster_name,\n description: `Comprehensive guide to ${cluster.core_topic}`,\n url: resolveBlogUrl(createBlogRoutes(options).cluster(cluster.cluster_slug), siteUrl),\n ...(siteName && {\n isPartOf: { '@type': 'WebSite', name: siteName, url: siteUrl },\n }),\n mainEntity: cluster.pillar\n ? {\n '@type': 'Article',\n headline: cluster.pillar.title,\n url: resolveBlogPostUrl(cluster.pillar, { ...options, siteUrl }),\n }\n : undefined,\n hasPart: cluster.supports.map((s) => ({\n '@type': 'Article',\n headline: s.title,\n url: resolveBlogPostUrl(s, { ...options, siteUrl }),\n })),\n }\n}\n\n/**\n * Generate metadata for a cluster landing page (Next.js generateMetadata).\n */\nexport async function generateClusterPageMetadata(\n clusterSlug: string,\n options: BlogRoutingOptions & { siteName?: string; siteUrl?: string } = {}\n) {\n const cluster = await getTopicCluster(clusterSlug)\n if (!cluster) return {}\n\n const { siteName = '', siteUrl = '' } = options\n const title = `${cluster.cluster_name}${siteName ? ` | ${siteName}` : ''}`\n const description = `Comprehensive guide to ${cluster.core_topic}. ${cluster.article_count} articles covering everything you need to know.`\n\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n url: resolveBlogUrl(createBlogRoutes(options).cluster(cluster.cluster_slug), siteUrl),\n type: 'website',\n },\n }\n}\n\n/**\n * Generate \"Related Insights\" section data\n */\nexport async function getRelatedInsights(\n currentSlug: string,\n options: { limit?: number; category?: string; /** Sub-site host override (see getBlogPost). */ site?: string } = {}\n): Promise<BlogPostRecord[]> {\n const config = resolveServerApiConfig()\n if (!config) return []\n const { limit = 3, site } = options\n // options.category is accepted for compatibility, but the endpoint derives\n // relatedness from the current post's own category server-side; a category\n // field in the body has always been ignored.\n\n // The endpoint takes `current_post_id` and looks the post up by id — it has\n // no slug path. Sending `slug` meant the lookup matched nothing and this\n // returned [] on every call since it shipped. Resolve the slug first (60s\n // cache via getBlogPost), then ask with the id.\n const current = await getBlogPost(currentSlug, { site })\n if (!current?.id) return []\n\n return fetchRelatedBlogPosts({ ...config, site }, current.id, limit, { revalidate: 300 })\n}\n\n// ============================================================================\n// BREADCRUMB SCHEMA\n// ============================================================================\n\n/**\n * Generate BreadcrumbList schema for blog navigation\n */\nexport function generateBreadcrumbSchema(\n post: BlogPostRecord,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string } = {}\n): object {\n const { siteUrl = '', siteName = 'Home' } = options\n\n const items = [\n {\n '@type': 'ListItem',\n position: 1,\n name: siteName,\n item: siteUrl,\n },\n {\n '@type': 'ListItem',\n position: 2,\n name: 'Blog',\n item: resolveBlogUrl(createBlogRoutes(options).index, siteUrl),\n },\n ]\n\n if (post.category) {\n const categoryName = typeof post.category === 'string' ? post.category : post.category.name\n const categorySlug = typeof post.category === 'string' \n ? post.category.toLowerCase().replace(/\\s+/g, '-')\n : post.category.slug\n\n items.push({\n '@type': 'ListItem',\n position: 3,\n name: categoryName,\n item: resolveBlogUrl(createBlogRoutes(options).category(categorySlug), siteUrl),\n })\n\n items.push({\n '@type': 'ListItem',\n position: 4,\n name: post.title,\n item: resolveBlogPostUrl(post, { ...options, siteUrl }),\n })\n } else {\n items.push({\n '@type': 'ListItem',\n position: 3,\n name: post.title,\n item: resolveBlogPostUrl(post, { ...options, siteUrl }),\n })\n }\n\n return {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items,\n }\n}\n\n// ============================================================================\n// COMBINED SCHEMA (Article + FAQ + Breadcrumb)\n// ============================================================================\n\n/**\n * Generate all relevant schemas for a blog post in a single array\n * This is the recommended way to include multiple schemas.\n * When post has E-E-A-T schema (post.schema), use it instead of generating Article/FAQ.\n */\nexport function generateAllBlogSchemas(\n post: BlogPostRecord,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string; logoUrl?: string } = {}\n): object[] {\n const schemas: object[] = []\n\n // E-E-A-T: use pre-built schema from Signal when present. Single source of\n // truth for E-E-A-T schema resolution: the DB column is schema_json, which\n // the public API passes through raw today; `schema` is the mapped name the\n // dashboard serializer (and a future public mapping) uses. Do NOT also\n // normalize in getBlogPost — that would re-fork this rule.\n const eeatSchema = post.schema ?? post.schema_json\n if (eeatSchema) {\n const arr = Array.isArray(eeatSchema) ? eeatSchema : [eeatSchema]\n schemas.push(...arr)\n } else {\n // 1. Article/BlogPosting schema (always)\n schemas.push(generateBlogPostSchema(post, options))\n\n // 3. FAQ schema (if FAQs present)\n const faqItems = post.faq_items ?? (post as any).faqItems\n if (faqItems && faqItems.length > 0) {\n const faqSchema = generateFaqSchema(faqItems)\n if (faqSchema) schemas.push(faqSchema)\n }\n }\n\n // 2. Breadcrumb schema (always)\n schemas.push(generateBreadcrumbSchema(post, options))\n\n return schemas\n}\n\n// ============================================================================\n// AUTHOR PAGE DATA FETCHING\n// ============================================================================\n\n/**\n * Fetch a single author by slug\n */\nexport async function getAuthorBySlug(\n slug: string,\n options?: { site?: string }\n): Promise<BlogAuthor | null> {\n const config = resolveServerApiConfig()\n if (!config) {\n console.warn('[Blog] No API key configured')\n return null\n }\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/authors/${slug}`, options?.site), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 60 },\n })\n\n if (!response.ok) return null\n\n const data = await response.json()\n return data.author || null\n } catch (error) {\n console.error('[Blog] Error fetching author:', error)\n return null\n }\n}\n\n/**\n * Fetch an author's posts by slug.\n *\n * `offset` pages through the controller's existing offset support. Older API\n * versions return a stripped `{name, slug}` author on this endpoint; when\n * that shape is detected the full author row is hydrated via getAuthorBySlug\n * (both fetches revalidate:60, so the extra hop is cached and disappears once\n * the API returns the full row itself).\n */\nexport async function getAuthorPosts(\n slug: string,\n limit = 20,\n offset = 0,\n options?: { site?: string }\n): Promise<{ author: BlogAuthor | null; posts: BlogPostRecord[]; total: number }> {\n const config = resolveServerApiConfig()\n if (!config) {\n console.warn('[Blog] No API key configured')\n return { author: null, posts: [], total: 0 }\n }\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(\n withSiteParam(`${apiUrl}/public/blog/authors/${slug}/posts?limit=${limit}&offset=${offset}`, options?.site),\n {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 60 },\n }\n )\n\n if (!response.ok) return { author: null, posts: [], total: 0 }\n\n const data = await response.json()\n let author: BlogAuthor | null = data.author || null\n if (author && author.id === undefined) {\n try {\n const full = await getAuthorBySlug(slug, options)\n if (full) author = full\n } catch {\n // keep the stripped {name, slug} object\n }\n }\n return {\n author,\n posts: data.posts || [],\n total: data.total || 0,\n }\n } catch (error) {\n console.error('[Blog] Error fetching author posts:', error)\n return { author: null, posts: [], total: 0 }\n }\n}\n\n/**\n * Fetch all author slugs for static generation\n */\nexport async function getAllAuthorSlugs(): Promise<{ slug: string }[]> {\n const config = resolveServerApiConfig()\n if (!config) {\n console.warn('[Blog] No API key configured')\n return []\n }\n const { apiUrl, apiKey } = config\n\n try {\n const response = await fetch(withSiteParam(`${apiUrl}/public/blog/authors`), {\n headers: { 'x-api-key': apiKey },\n next: { revalidate: 300 },\n })\n\n if (!response.ok) return []\n\n const data = await response.json()\n const authors: BlogAuthor[] = data.authors || []\n return authors.map((a) => ({ slug: a.slug }))\n } catch (error) {\n console.error('[Blog] Error fetching author slugs:', error)\n return []\n }\n}\n\n/**\n * Generate static params for author pages. Takes no arguments, so it can be\n * exported directly:\n *\n * export const generateStaticParams = generateAuthorStaticParams\n */\nexport async function generateAuthorStaticParams(): Promise<{ slug: string }[]> {\n return getAllAuthorSlugs()\n}\n\n// ============================================================================\n// AUTHOR METADATA & SCHEMA\n// ============================================================================\n\n/**\n * Generate Next.js Metadata for an author page\n */\nexport function generateAuthorPageMetadata(\n author: BlogAuthor,\n options: BlogRoutingOptions & { siteUrl?: string; siteName?: string } = {}\n): Metadata {\n const { siteUrl = '', siteName } = options\n const title = `${author.name}${author.title ? ` - ${author.title}` : ''}`\n const description = author.short_bio || author.bio || `Articles by ${author.name}`\n const url = resolveBlogUrl(createBlogRoutes(options).author(author.slug), siteUrl)\n\n return {\n title,\n description,\n openGraph: {\n title,\n description,\n url,\n siteName,\n type: 'profile',\n ...(author.avatar_url && { images: [{ url: author.avatar_url }] }),\n },\n twitter: {\n card: 'summary',\n title,\n description,\n ...(author.avatar_url && { images: [author.avatar_url] }),\n },\n alternates: {\n canonical: author.author_page_url || url,\n },\n }\n}\n\n/**\n * Person JSON-LD for an author page.\n *\n * The implementation lives in `./author-schema` because it is pure, and\n * `AuthorPage` — which is exported from the client-stamped `blog` barrel —\n * needs it. Importing it from here instead would drag this entire server module\n * into the browser bundle. Re-exported so `@sonordev/site-kit/blog/server`\n * keeps its existing public API.\n */\nexport { generateAuthorSchema } from './author-schema'\n\n// -----------------------------------------------------------------------------\n// Blog UI (BlogPost, BlogList) lives in `@sonordev/site-kit/blog/server-ui`.\n// This entry must stay free of React components so Next never marks server\n// helpers (e.g. getAllBlogSlugs) as client — see tsup onSuccess \"use client\".\n// -----------------------------------------------------------------------------\n\n/** Portal blog post document shape (alias of `BlogPost` from `./types`) */\nexport type { BlogPost as BlogPostRecord } from './types'\n\nexport { blogCategorySlug, createBlogRoutes, resolveBlogPostUrl, resolveBlogUrl } from './routes'\nexport type { BlogRoutePost, BlogRoutingOptions } from './routes'\nexport { resolveBlogArtwork } from './artwork'\nexport { wrapBlogTables, blogTableCss } from './blog-tables'\n"]}