@sonordev/site-kit 1.2.7

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 (300) hide show
  1. package/README.md +376 -0
  2. package/dist/SetupWizard-Cki06kB0.d.mts +12 -0
  3. package/dist/SetupWizard-Cki06kB0.d.ts +12 -0
  4. package/dist/analytics/index.d.mts +93 -0
  5. package/dist/analytics/index.d.ts +93 -0
  6. package/dist/analytics/index.js +89 -0
  7. package/dist/analytics/index.js.map +1 -0
  8. package/dist/analytics/index.mjs +71 -0
  9. package/dist/analytics/index.mjs.map +1 -0
  10. package/dist/api-CWtoFJCO.d.mts +137 -0
  11. package/dist/api-CWtoFJCO.d.ts +137 -0
  12. package/dist/blog/index.d.mts +305 -0
  13. package/dist/blog/index.d.ts +305 -0
  14. package/dist/blog/index.js +1578 -0
  15. package/dist/blog/index.js.map +1 -0
  16. package/dist/blog/index.mjs +1562 -0
  17. package/dist/blog/index.mjs.map +1 -0
  18. package/dist/blog/server.d.mts +229 -0
  19. package/dist/blog/server.d.ts +229 -0
  20. package/dist/blog/server.js +692 -0
  21. package/dist/blog/server.js.map +1 -0
  22. package/dist/blog/server.mjs +666 -0
  23. package/dist/blog/server.mjs.map +1 -0
  24. package/dist/chunk-24277A3Q.mjs +968 -0
  25. package/dist/chunk-24277A3Q.mjs.map +1 -0
  26. package/dist/chunk-373TK6TZ.js +321 -0
  27. package/dist/chunk-373TK6TZ.js.map +1 -0
  28. package/dist/chunk-3MYZS6PD.js +30 -0
  29. package/dist/chunk-3MYZS6PD.js.map +1 -0
  30. package/dist/chunk-43GBM4SX.js +283 -0
  31. package/dist/chunk-43GBM4SX.js.map +1 -0
  32. package/dist/chunk-4XPGGLVP.mjs +53 -0
  33. package/dist/chunk-4XPGGLVP.mjs.map +1 -0
  34. package/dist/chunk-622GAQP5.js +2008 -0
  35. package/dist/chunk-622GAQP5.js.map +1 -0
  36. package/dist/chunk-6BIPAKL4.mjs +28 -0
  37. package/dist/chunk-6BIPAKL4.mjs.map +1 -0
  38. package/dist/chunk-6ZCISNAB.mjs +343 -0
  39. package/dist/chunk-6ZCISNAB.mjs.map +1 -0
  40. package/dist/chunk-72MQFHYJ.js +1429 -0
  41. package/dist/chunk-72MQFHYJ.js.map +1 -0
  42. package/dist/chunk-7557OTHW.js +62 -0
  43. package/dist/chunk-7557OTHW.js.map +1 -0
  44. package/dist/chunk-7FUV73JZ.js +981 -0
  45. package/dist/chunk-7FUV73JZ.js.map +1 -0
  46. package/dist/chunk-7RF6PVHA.mjs +324 -0
  47. package/dist/chunk-7RF6PVHA.mjs.map +1 -0
  48. package/dist/chunk-7RYCHO6D.mjs +134 -0
  49. package/dist/chunk-7RYCHO6D.mjs.map +1 -0
  50. package/dist/chunk-7UKPRW25.mjs +1999 -0
  51. package/dist/chunk-7UKPRW25.mjs.map +1 -0
  52. package/dist/chunk-7URAOG2M.js +14864 -0
  53. package/dist/chunk-7URAOG2M.js.map +1 -0
  54. package/dist/chunk-AFAO3TGS.mjs +810 -0
  55. package/dist/chunk-AFAO3TGS.mjs.map +1 -0
  56. package/dist/chunk-BYLIU6XG.js +343 -0
  57. package/dist/chunk-BYLIU6XG.js.map +1 -0
  58. package/dist/chunk-D63MUKZ6.mjs +4423 -0
  59. package/dist/chunk-D63MUKZ6.mjs.map +1 -0
  60. package/dist/chunk-DDKW2FNA.js +390 -0
  61. package/dist/chunk-DDKW2FNA.js.map +1 -0
  62. package/dist/chunk-DQYMKR27.mjs +341 -0
  63. package/dist/chunk-DQYMKR27.mjs.map +1 -0
  64. package/dist/chunk-DW5UJKHH.js +221 -0
  65. package/dist/chunk-DW5UJKHH.js.map +1 -0
  66. package/dist/chunk-EEZCR6E6.js +50 -0
  67. package/dist/chunk-EEZCR6E6.js.map +1 -0
  68. package/dist/chunk-GCJXQ4AG.mjs +59 -0
  69. package/dist/chunk-GCJXQ4AG.mjs.map +1 -0
  70. package/dist/chunk-JGNQK2G6.mjs +14845 -0
  71. package/dist/chunk-JGNQK2G6.mjs.map +1 -0
  72. package/dist/chunk-JTLOJLWQ.mjs +563 -0
  73. package/dist/chunk-JTLOJLWQ.mjs.map +1 -0
  74. package/dist/chunk-K23A4G76.mjs +202 -0
  75. package/dist/chunk-K23A4G76.mjs.map +1 -0
  76. package/dist/chunk-KKU3K7RG.js +336 -0
  77. package/dist/chunk-KKU3K7RG.js.map +1 -0
  78. package/dist/chunk-KUGMH4ZF.js +571 -0
  79. package/dist/chunk-KUGMH4ZF.js.map +1 -0
  80. package/dist/chunk-LBVWVP72.js +110 -0
  81. package/dist/chunk-LBVWVP72.js.map +1 -0
  82. package/dist/chunk-LIVWLY2P.js +138 -0
  83. package/dist/chunk-LIVWLY2P.js.map +1 -0
  84. package/dist/chunk-M2T6R7BA.mjs +1003 -0
  85. package/dist/chunk-M2T6R7BA.mjs.map +1 -0
  86. package/dist/chunk-MV3QN7PW.mjs +47 -0
  87. package/dist/chunk-MV3QN7PW.mjs.map +1 -0
  88. package/dist/chunk-OB7E654K.js +72 -0
  89. package/dist/chunk-OB7E654K.js.map +1 -0
  90. package/dist/chunk-OIIKTGRL.mjs +380 -0
  91. package/dist/chunk-OIIKTGRL.mjs.map +1 -0
  92. package/dist/chunk-P3UWIUJS.mjs +1427 -0
  93. package/dist/chunk-P3UWIUJS.mjs.map +1 -0
  94. package/dist/chunk-PKN27UMH.mjs +136 -0
  95. package/dist/chunk-PKN27UMH.mjs.map +1 -0
  96. package/dist/chunk-QXV4667R.mjs +105 -0
  97. package/dist/chunk-QXV4667R.mjs.map +1 -0
  98. package/dist/chunk-S7FRYNSU.mjs +315 -0
  99. package/dist/chunk-S7FRYNSU.mjs.map +1 -0
  100. package/dist/chunk-TFLQX7K7.mjs +68 -0
  101. package/dist/chunk-TFLQX7K7.mjs.map +1 -0
  102. package/dist/chunk-UWE5PCYJ.mjs +279 -0
  103. package/dist/chunk-UWE5PCYJ.mjs.map +1 -0
  104. package/dist/chunk-UYFDNX2F.js +4469 -0
  105. package/dist/chunk-UYFDNX2F.js.map +1 -0
  106. package/dist/chunk-W4PALSGM.js +350 -0
  107. package/dist/chunk-W4PALSGM.js.map +1 -0
  108. package/dist/chunk-WECQ6KOB.js +1008 -0
  109. package/dist/chunk-WECQ6KOB.js.map +1 -0
  110. package/dist/chunk-XQQWI6WB.js +814 -0
  111. package/dist/chunk-XQQWI6WB.js.map +1 -0
  112. package/dist/chunk-XZJOZJB6.js +140 -0
  113. package/dist/chunk-XZJOZJB6.js.map +1 -0
  114. package/dist/chunk-ZSMWDLMK.js +63 -0
  115. package/dist/chunk-ZSMWDLMK.js.map +1 -0
  116. package/dist/cli/index.js +37243 -0
  117. package/dist/cli/index.js.map +1 -0
  118. package/dist/cli/index.mjs +37209 -0
  119. package/dist/cli/index.mjs.map +1 -0
  120. package/dist/commerce/index.d.mts +170 -0
  121. package/dist/commerce/index.d.ts +170 -0
  122. package/dist/commerce/index.js +174 -0
  123. package/dist/commerce/index.js.map +1 -0
  124. package/dist/commerce/index.mjs +5 -0
  125. package/dist/commerce/index.mjs.map +1 -0
  126. package/dist/commerce/server.d.mts +107 -0
  127. package/dist/commerce/server.d.ts +107 -0
  128. package/dist/commerce/server.js +187 -0
  129. package/dist/commerce/server.js.map +1 -0
  130. package/dist/commerce/server.mjs +177 -0
  131. package/dist/commerce/server.mjs.map +1 -0
  132. package/dist/config/index.d.mts +43 -0
  133. package/dist/config/index.d.ts +43 -0
  134. package/dist/config/index.js +66 -0
  135. package/dist/config/index.js.map +1 -0
  136. package/dist/config/index.mjs +64 -0
  137. package/dist/config/index.mjs.map +1 -0
  138. package/dist/engage/index.d.mts +33 -0
  139. package/dist/engage/index.d.ts +33 -0
  140. package/dist/engage/index.js +22 -0
  141. package/dist/engage/index.js.map +1 -0
  142. package/dist/engage/index.mjs +5 -0
  143. package/dist/engage/index.mjs.map +1 -0
  144. package/dist/forms/index.d.mts +437 -0
  145. package/dist/forms/index.d.ts +437 -0
  146. package/dist/forms/index.js +1168 -0
  147. package/dist/forms/index.js.map +1 -0
  148. package/dist/forms/index.mjs +1142 -0
  149. package/dist/forms/index.mjs.map +1 -0
  150. package/dist/generators-2XKQMPKH.mjs +4 -0
  151. package/dist/generators-2XKQMPKH.mjs.map +1 -0
  152. package/dist/generators-DTMO36DV.js +33 -0
  153. package/dist/generators-DTMO36DV.js.map +1 -0
  154. package/dist/images/index.d.mts +4 -0
  155. package/dist/images/index.d.ts +4 -0
  156. package/dist/images/index.js +46 -0
  157. package/dist/images/index.js.map +1 -0
  158. package/dist/images/index.mjs +5 -0
  159. package/dist/images/index.mjs.map +1 -0
  160. package/dist/images/server.d.mts +69 -0
  161. package/dist/images/server.d.ts +69 -0
  162. package/dist/images/server.js +21 -0
  163. package/dist/images/server.js.map +1 -0
  164. package/dist/images/server.mjs +4 -0
  165. package/dist/images/server.mjs.map +1 -0
  166. package/dist/index.d.mts +846 -0
  167. package/dist/index.d.ts +846 -0
  168. package/dist/index.js +2623 -0
  169. package/dist/index.js.map +1 -0
  170. package/dist/index.mjs +2416 -0
  171. package/dist/index.mjs.map +1 -0
  172. package/dist/layout/index.d.mts +53 -0
  173. package/dist/layout/index.d.ts +53 -0
  174. package/dist/layout/index.js +187 -0
  175. package/dist/layout/index.js.map +1 -0
  176. package/dist/layout/index.mjs +185 -0
  177. package/dist/layout/index.mjs.map +1 -0
  178. package/dist/llms/index.d.mts +448 -0
  179. package/dist/llms/index.d.ts +448 -0
  180. package/dist/llms/index.js +581 -0
  181. package/dist/llms/index.js.map +1 -0
  182. package/dist/llms/index.mjs +529 -0
  183. package/dist/llms/index.mjs.map +1 -0
  184. package/dist/manifest/index.d.mts +62 -0
  185. package/dist/manifest/index.d.ts +62 -0
  186. package/dist/manifest/index.js +85 -0
  187. package/dist/manifest/index.js.map +1 -0
  188. package/dist/manifest/index.mjs +83 -0
  189. package/dist/manifest/index.mjs.map +1 -0
  190. package/dist/middleware/index.d.mts +63 -0
  191. package/dist/middleware/index.d.ts +63 -0
  192. package/dist/middleware/index.js +54 -0
  193. package/dist/middleware/index.js.map +1 -0
  194. package/dist/middleware/index.mjs +51 -0
  195. package/dist/middleware/index.mjs.map +1 -0
  196. package/dist/migrator-2MQHOFDQ.mjs +4 -0
  197. package/dist/migrator-2MQHOFDQ.mjs.map +1 -0
  198. package/dist/migrator-THJCF6MZ.js +37 -0
  199. package/dist/migrator-THJCF6MZ.js.map +1 -0
  200. package/dist/redirects/index.d.mts +78 -0
  201. package/dist/redirects/index.d.ts +78 -0
  202. package/dist/redirects/index.js +26 -0
  203. package/dist/redirects/index.js.map +1 -0
  204. package/dist/redirects/index.mjs +5 -0
  205. package/dist/redirects/index.mjs.map +1 -0
  206. package/dist/reputation/index.d.mts +57 -0
  207. package/dist/reputation/index.d.ts +57 -0
  208. package/dist/reputation/index.js +21 -0
  209. package/dist/reputation/index.js.map +1 -0
  210. package/dist/reputation/index.mjs +4 -0
  211. package/dist/reputation/index.mjs.map +1 -0
  212. package/dist/robots/index.d.mts +38 -0
  213. package/dist/robots/index.d.ts +38 -0
  214. package/dist/robots/index.js +52 -0
  215. package/dist/robots/index.js.map +1 -0
  216. package/dist/robots/index.mjs +50 -0
  217. package/dist/robots/index.mjs.map +1 -0
  218. package/dist/routing-B5XS-6_W.d.mts +118 -0
  219. package/dist/routing-DZYzyDHw.d.ts +118 -0
  220. package/dist/scanner-GAF5PO5F.js +53 -0
  221. package/dist/scanner-GAF5PO5F.js.map +1 -0
  222. package/dist/scanner-LKJKW7IT.mjs +4 -0
  223. package/dist/scanner-LKJKW7IT.mjs.map +1 -0
  224. package/dist/securityHeaders-nwZ6nP4g.d.mts +24 -0
  225. package/dist/securityHeaders-nwZ6nP4g.d.ts +24 -0
  226. package/dist/seo/index.d.mts +600 -0
  227. package/dist/seo/index.d.ts +600 -0
  228. package/dist/seo/index.js +883 -0
  229. package/dist/seo/index.js.map +1 -0
  230. package/dist/seo/index.mjs +773 -0
  231. package/dist/seo/index.mjs.map +1 -0
  232. package/dist/seo/register-sitemap-cli.js +151 -0
  233. package/dist/seo/register-sitemap-cli.js.map +1 -0
  234. package/dist/seo/register-sitemap-cli.mjs +144 -0
  235. package/dist/seo/register-sitemap-cli.mjs.map +1 -0
  236. package/dist/seo/server.d.mts +107 -0
  237. package/dist/seo/server.d.ts +107 -0
  238. package/dist/seo/server.js +207 -0
  239. package/dist/seo/server.js.map +1 -0
  240. package/dist/seo/server.mjs +186 -0
  241. package/dist/seo/server.mjs.map +1 -0
  242. package/dist/server-api-EWXKOQZA.mjs +4 -0
  243. package/dist/server-api-EWXKOQZA.mjs.map +1 -0
  244. package/dist/server-api-GJPNRYUP.js +81 -0
  245. package/dist/server-api-GJPNRYUP.js.map +1 -0
  246. package/dist/setup/client.d.mts +60 -0
  247. package/dist/setup/client.d.ts +60 -0
  248. package/dist/setup/client.js +31 -0
  249. package/dist/setup/client.js.map +1 -0
  250. package/dist/setup/client.mjs +6 -0
  251. package/dist/setup/client.mjs.map +1 -0
  252. package/dist/setup/index.d.mts +5 -0
  253. package/dist/setup/index.d.ts +5 -0
  254. package/dist/setup/index.js +35 -0
  255. package/dist/setup/index.js.map +1 -0
  256. package/dist/setup/index.mjs +6 -0
  257. package/dist/setup/index.mjs.map +1 -0
  258. package/dist/setup/server.d.mts +14 -0
  259. package/dist/setup/server.d.ts +14 -0
  260. package/dist/setup/server.js +13 -0
  261. package/dist/setup/server.js.map +1 -0
  262. package/dist/setup/server.mjs +4 -0
  263. package/dist/setup/server.mjs.map +1 -0
  264. package/dist/site-config/index.d.mts +24 -0
  265. package/dist/site-config/index.d.ts +24 -0
  266. package/dist/site-config/index.js +17 -0
  267. package/dist/site-config/index.js.map +1 -0
  268. package/dist/site-config/index.mjs +4 -0
  269. package/dist/site-config/index.mjs.map +1 -0
  270. package/dist/sitemap/index.d.mts +96 -0
  271. package/dist/sitemap/index.d.ts +96 -0
  272. package/dist/sitemap/index.js +288 -0
  273. package/dist/sitemap/index.js.map +1 -0
  274. package/dist/sitemap/index.mjs +285 -0
  275. package/dist/sitemap/index.mjs.map +1 -0
  276. package/dist/socket-loader-J26QHHOB.js +16 -0
  277. package/dist/socket-loader-J26QHHOB.js.map +1 -0
  278. package/dist/socket-loader-R7S2YJ2J.mjs +14 -0
  279. package/dist/socket-loader-R7S2YJ2J.mjs.map +1 -0
  280. package/dist/types-0dmq3k20.d.mts +168 -0
  281. package/dist/types-0dmq3k20.d.ts +168 -0
  282. package/dist/types-Blb2QNkV.d.mts +263 -0
  283. package/dist/types-Blb2QNkV.d.ts +263 -0
  284. package/dist/types-BnCwwUX3.d.mts +250 -0
  285. package/dist/types-BnCwwUX3.d.ts +250 -0
  286. package/dist/types-CGlnp43R.d.mts +312 -0
  287. package/dist/types-CGlnp43R.d.ts +312 -0
  288. package/dist/types-D08004rU.d.mts +179 -0
  289. package/dist/types-D08004rU.d.ts +179 -0
  290. package/dist/types-DNSYU7qI.d.mts +127 -0
  291. package/dist/types-DNSYU7qI.d.ts +127 -0
  292. package/dist/types-KZP_VWZp.d.mts +266 -0
  293. package/dist/types-KZP_VWZp.d.ts +266 -0
  294. package/dist/useEventModal-BVTx69XE.d.mts +274 -0
  295. package/dist/useEventModal-Dx1dItTJ.d.ts +274 -0
  296. package/dist/web-vitals-444RLW3B.js +252 -0
  297. package/dist/web-vitals-444RLW3B.js.map +1 -0
  298. package/dist/web-vitals-KPICZIEF.mjs +241 -0
  299. package/dist/web-vitals-KPICZIEF.mjs.map +1 -0
  300. package/package.json +192 -0
@@ -0,0 +1,666 @@
1
+ import '../chunk-4XPGGLVP.mjs';
2
+
3
+ // src/blog/server.ts
4
+ function getConfig() {
5
+ return {
6
+ apiUrl: process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
7
+ apiKey: process.env.NEXT_PUBLIC_UPTRADE_API_KEY || ""
8
+ };
9
+ }
10
+ var SEO_LIMITS = {
11
+ title: { min: 30, max: 60, recommended: 55 },
12
+ metaDescription: { min: 120, max: 160, recommended: 155 },
13
+ excerpt: { min: 100, max: 300, recommended: 200 },
14
+ slug: { max: 75 },
15
+ focusKeyphrase: { min: 2, max: 4, words: true }
16
+ // 2-4 words
17
+ };
18
+ function validateSeoTitle(title, focusKeyphrase) {
19
+ const length = title.length;
20
+ let status = "good";
21
+ let message = "Title length is optimal";
22
+ if (length > SEO_LIMITS.title.max) {
23
+ status = "error";
24
+ message = `Title is ${length - SEO_LIMITS.title.max} characters too long (max ${SEO_LIMITS.title.max})`;
25
+ } else if (length < SEO_LIMITS.title.min) {
26
+ status = "warning";
27
+ message = `Title is short (${length}/${SEO_LIMITS.title.min} min)`;
28
+ } else if (focusKeyphrase && !title.toLowerCase().includes(focusKeyphrase.toLowerCase())) {
29
+ status = "warning";
30
+ message = "Focus keyphrase not found in title";
31
+ } else if (focusKeyphrase && !title.toLowerCase().startsWith(focusKeyphrase.toLowerCase())) {
32
+ status = "warning";
33
+ message = "Title should start with focus keyphrase for best results";
34
+ }
35
+ return { field: "title", value: title, length, limit: SEO_LIMITS.title, status, message };
36
+ }
37
+ function validateMetaDescription(description, focusKeyphrase) {
38
+ const length = description.length;
39
+ let status = "good";
40
+ let message = "Meta description is optimal";
41
+ if (length > SEO_LIMITS.metaDescription.max) {
42
+ status = "error";
43
+ message = `Description will be truncated (${length}/${SEO_LIMITS.metaDescription.max})`;
44
+ } else if (length < SEO_LIMITS.metaDescription.min) {
45
+ status = "warning";
46
+ message = `Description is short (${length}/${SEO_LIMITS.metaDescription.min} min)`;
47
+ } else if (focusKeyphrase && !description.toLowerCase().includes(focusKeyphrase.toLowerCase())) {
48
+ status = "warning";
49
+ message = "Focus keyphrase not found in description";
50
+ }
51
+ return { field: "meta_description", value: description, length, limit: SEO_LIMITS.metaDescription, status, message };
52
+ }
53
+ function validateBlogPostSeo(post) {
54
+ const results = [];
55
+ const focusKeyphrase = post.focus_keyphrase;
56
+ if (post.meta_title || post.title) {
57
+ results.push(validateSeoTitle(post.meta_title || post.title || "", focusKeyphrase));
58
+ }
59
+ if (post.meta_description) {
60
+ results.push(validateMetaDescription(post.meta_description, focusKeyphrase));
61
+ }
62
+ if (post.title && focusKeyphrase) {
63
+ const titleLower = post.title.toLowerCase();
64
+ const keyLower = focusKeyphrase.toLowerCase();
65
+ if (!titleLower.includes(keyLower)) {
66
+ results.push({
67
+ field: "h1_alignment",
68
+ value: post.title,
69
+ length: post.title.length,
70
+ limit: { max: 100 },
71
+ status: "warning",
72
+ message: "H1 (title) should include the focus keyphrase"
73
+ });
74
+ }
75
+ }
76
+ if (post.word_count !== void 0) {
77
+ let wordStatus = "good";
78
+ let wordMessage = "Content length is optimal for SEO";
79
+ if (post.word_count < 600) {
80
+ wordStatus = "error";
81
+ wordMessage = `Content is thin (${post.word_count} words). Aim for 1200+ words.`;
82
+ } else if (post.word_count < 1200) {
83
+ wordStatus = "warning";
84
+ wordMessage = `Content is short (${post.word_count} words). Long-form (1200-2500) ranks better.`;
85
+ } else if (post.word_count > 2500) {
86
+ wordMessage = `Comprehensive content (${post.word_count} words). Consider splitting into a topic cluster.`;
87
+ }
88
+ results.push({
89
+ field: "word_count",
90
+ value: String(post.word_count),
91
+ length: post.word_count,
92
+ limit: { min: 1200, max: 2500, recommended: 1800 },
93
+ status: wordStatus,
94
+ message: wordMessage
95
+ });
96
+ }
97
+ return results;
98
+ }
99
+ async function getBlogPost(slug) {
100
+ const { apiUrl, apiKey } = getConfig();
101
+ if (!apiKey) {
102
+ console.warn("[Blog] No API key configured");
103
+ return null;
104
+ }
105
+ try {
106
+ const response = await fetch(`${apiUrl}/public/blog/posts/${slug}`, {
107
+ headers: { "x-api-key": apiKey },
108
+ next: { revalidate: 60 }
109
+ });
110
+ if (!response.ok) return null;
111
+ const data = await response.json();
112
+ return data.post || null;
113
+ } catch (error) {
114
+ console.error("[Blog] Error fetching post:", error);
115
+ return null;
116
+ }
117
+ }
118
+ async function getAllBlogSlugs() {
119
+ const { apiUrl, apiKey } = getConfig();
120
+ if (!apiKey) {
121
+ console.warn("[Blog] No API key configured");
122
+ return [];
123
+ }
124
+ try {
125
+ const response = await fetch(`${apiUrl}/public/blog/slugs`, {
126
+ headers: { "x-api-key": apiKey },
127
+ next: { revalidate: 300 }
128
+ });
129
+ if (!response.ok) return [];
130
+ const data = await response.json();
131
+ return data.slugs || [];
132
+ } catch (error) {
133
+ console.error("[Blog] Error fetching slugs:", error);
134
+ return [];
135
+ }
136
+ }
137
+ async function getBlogCategories() {
138
+ const { apiUrl, apiKey } = getConfig();
139
+ if (!apiKey) return [];
140
+ try {
141
+ const response = await fetch(`${apiUrl}/public/blog/categories`, {
142
+ headers: { "x-api-key": apiKey },
143
+ next: { revalidate: 300 }
144
+ });
145
+ if (!response.ok) return [];
146
+ const data = await response.json();
147
+ return data.categories || [];
148
+ } catch (error) {
149
+ console.error("[Blog] Error fetching categories:", error);
150
+ return [];
151
+ }
152
+ }
153
+ async function generateBlogPostMetadata(slug, options = {}) {
154
+ const post = await getBlogPost(slug);
155
+ const { siteName, siteUrl = "", defaultImage, twitterHandle } = options;
156
+ if (!post) {
157
+ return {
158
+ title: "Post Not Found",
159
+ description: "The requested blog post could not be found."
160
+ };
161
+ }
162
+ const title = post.meta_title || post.title;
163
+ const description = post.meta_description || post.excerpt || "";
164
+ const image = post.og_image || post.featured_image || defaultImage;
165
+ const url = `${siteUrl}/blog/${post.slug}`;
166
+ return {
167
+ title,
168
+ description,
169
+ openGraph: {
170
+ title: post.og_title || title,
171
+ description: post.og_description || description,
172
+ url,
173
+ siteName,
174
+ type: "article",
175
+ publishedTime: post.published_at,
176
+ authors: post.author ? [typeof post.author === "string" ? post.author : post.author.name] : void 0,
177
+ images: image ? [
178
+ {
179
+ url: image,
180
+ width: post.featured_image_width || 1200,
181
+ height: post.featured_image_height || 630,
182
+ alt: post.featured_image_alt || post.title
183
+ }
184
+ ] : void 0
185
+ },
186
+ twitter: {
187
+ card: "summary_large_image",
188
+ title,
189
+ description,
190
+ images: image ? [image] : void 0,
191
+ creator: twitterHandle
192
+ },
193
+ alternates: {
194
+ canonical: post.canonical_url || url
195
+ },
196
+ other: {
197
+ "article:published_time": post.published_at || "",
198
+ "article:section": typeof post.category === "string" ? post.category : post.category?.name || ""
199
+ }
200
+ };
201
+ }
202
+ function generateBlogIndexMetadata(options) {
203
+ const {
204
+ title = "Blog",
205
+ description = "Read our latest articles and insights.",
206
+ siteName,
207
+ siteUrl = "",
208
+ defaultImage,
209
+ twitterHandle
210
+ } = options;
211
+ return {
212
+ title,
213
+ description,
214
+ openGraph: {
215
+ title,
216
+ description,
217
+ url: `${siteUrl}/blog`,
218
+ siteName,
219
+ type: "website",
220
+ images: defaultImage ? [{ url: defaultImage }] : void 0
221
+ },
222
+ twitter: {
223
+ card: "summary_large_image",
224
+ title,
225
+ description,
226
+ images: defaultImage ? [defaultImage] : void 0,
227
+ creator: twitterHandle
228
+ }
229
+ };
230
+ }
231
+ function generateBlogCategoryMetadata(categoryName, options = {}) {
232
+ const { siteName, siteUrl = "", defaultImage } = options;
233
+ const title = `${categoryName} - Blog`;
234
+ const description = `Browse all posts in ${categoryName}.`;
235
+ return {
236
+ title,
237
+ description,
238
+ openGraph: {
239
+ title,
240
+ description,
241
+ url: `${siteUrl}/blog/category/${categoryName.toLowerCase()}`,
242
+ siteName,
243
+ type: "website",
244
+ images: defaultImage ? [{ url: defaultImage }] : void 0
245
+ }
246
+ };
247
+ }
248
+ async function generateBlogStaticParams() {
249
+ const slugs = await getAllBlogSlugs();
250
+ return slugs.map((s) => ({ slug: s.slug }));
251
+ }
252
+ async function generateCategoryStaticParams() {
253
+ const categories = await getBlogCategories();
254
+ return categories.map((c) => ({ category: c.slug }));
255
+ }
256
+ async function generateBlogSitemap(siteUrl) {
257
+ const slugs = await getAllBlogSlugs();
258
+ const categories = await getBlogCategories();
259
+ const entries = [
260
+ // Blog index
261
+ {
262
+ url: `${siteUrl}/blog`,
263
+ changeFrequency: "daily",
264
+ priority: 0.8
265
+ }
266
+ ];
267
+ categories.forEach((cat) => {
268
+ entries.push({
269
+ url: `${siteUrl}/blog/category/${cat.slug}`,
270
+ changeFrequency: "weekly",
271
+ priority: 0.6
272
+ });
273
+ });
274
+ slugs.forEach((post) => {
275
+ entries.push({
276
+ url: `${siteUrl}/blog/${post.slug}`,
277
+ lastModified: post.last_modified ? new Date(post.last_modified) : void 0,
278
+ changeFrequency: "weekly",
279
+ priority: 0.7
280
+ });
281
+ });
282
+ return entries;
283
+ }
284
+ function generateBlogPostSchema(post, options = {}) {
285
+ const { siteUrl = "", siteName, logoUrl } = options;
286
+ const schema = {
287
+ "@context": "https://schema.org",
288
+ "@type": "BlogPosting",
289
+ headline: post.title,
290
+ description: post.excerpt || post.meta_description,
291
+ url: `${siteUrl}/blog/${post.slug}`,
292
+ datePublished: post.published_at,
293
+ dateModified: post.updated_at,
294
+ author: {
295
+ "@type": "Person",
296
+ name: typeof post.author === "string" ? post.author : post.author?.name
297
+ }
298
+ };
299
+ if (post.featured_image) {
300
+ schema.image = {
301
+ "@type": "ImageObject",
302
+ url: post.featured_image,
303
+ width: post.featured_image_width || 1200,
304
+ height: post.featured_image_height || 630
305
+ };
306
+ }
307
+ if (siteName || logoUrl) {
308
+ schema.publisher = {
309
+ "@type": "Organization",
310
+ name: siteName,
311
+ logo: logoUrl ? {
312
+ "@type": "ImageObject",
313
+ url: logoUrl
314
+ } : void 0
315
+ };
316
+ }
317
+ if (post.faq_items && Array.isArray(post.faq_items) && post.faq_items.length > 0) {
318
+ schema.mainEntity = {
319
+ "@type": "FAQPage",
320
+ mainEntity: post.faq_items.map((faq) => ({
321
+ "@type": "Question",
322
+ name: faq.question,
323
+ acceptedAnswer: {
324
+ "@type": "Answer",
325
+ text: faq.answer
326
+ }
327
+ }))
328
+ };
329
+ }
330
+ return schema;
331
+ }
332
+ function generateBlogListSchema(options = {}) {
333
+ const { siteUrl = "", siteName, description } = options;
334
+ return {
335
+ "@context": "https://schema.org",
336
+ "@type": "Blog",
337
+ name: siteName ? `${siteName} Blog` : "Blog",
338
+ description: description || "Our latest articles and insights.",
339
+ url: `${siteUrl}/blog`
340
+ };
341
+ }
342
+ function generateFaqSchema(faqItems) {
343
+ if (!faqItems || faqItems.length === 0) return null;
344
+ return {
345
+ "@context": "https://schema.org",
346
+ "@type": "FAQPage",
347
+ mainEntity: faqItems.map((faq) => ({
348
+ "@type": "Question",
349
+ name: faq.question,
350
+ acceptedAnswer: {
351
+ "@type": "Answer",
352
+ text: faq.answer
353
+ }
354
+ }))
355
+ };
356
+ }
357
+ function generateHowToSchema(post, steps, options = {}) {
358
+ const { siteUrl = "" } = options;
359
+ return {
360
+ "@context": "https://schema.org",
361
+ "@type": "HowTo",
362
+ name: post.title,
363
+ description: post.excerpt || post.meta_description,
364
+ image: post.featured_image,
365
+ totalTime: post.reading_time ? `PT${post.reading_time}M` : void 0,
366
+ step: steps.map((step, index) => ({
367
+ "@type": "HowToStep",
368
+ position: index + 1,
369
+ name: step.name,
370
+ text: step.text,
371
+ image: step.image,
372
+ url: step.url || `${siteUrl}/blog/${post.slug}#step-${index + 1}`
373
+ }))
374
+ };
375
+ }
376
+ async function getAllBlogPosts() {
377
+ const { apiUrl, apiKey } = getConfig();
378
+ if (!apiKey) {
379
+ console.warn("[Blog] No API key configured");
380
+ return [];
381
+ }
382
+ try {
383
+ const response = await fetch(`${apiUrl}/public/blog/posts?limit=100`, {
384
+ headers: { "x-api-key": apiKey },
385
+ next: { revalidate: 300 }
386
+ });
387
+ if (!response.ok) return [];
388
+ const data = await response.json();
389
+ return data.posts || [];
390
+ } catch (error) {
391
+ console.error("[Blog] Error fetching posts for RSS:", error);
392
+ return [];
393
+ }
394
+ }
395
+ function escapeXml(text) {
396
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
397
+ }
398
+ function stripHtml(html) {
399
+ return html.replace(/<[^>]*>/g, "").trim();
400
+ }
401
+ async function generateRssFeed(options) {
402
+ const {
403
+ siteUrl,
404
+ siteName,
405
+ description = "Latest blog posts and insights",
406
+ language = "en-us",
407
+ copyright,
408
+ managingEditor,
409
+ webMaster,
410
+ ttl = 60,
411
+ imageUrl
412
+ } = options;
413
+ const posts = await getAllBlogPosts();
414
+ const now = (/* @__PURE__ */ new Date()).toUTCString();
415
+ let rss = `<?xml version="1.0" encoding="UTF-8"?>
416
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
417
+ <channel>
418
+ <title>${escapeXml(siteName)}</title>
419
+ <link>${siteUrl}</link>
420
+ <description>${escapeXml(description)}</description>
421
+ <language>${language}</language>
422
+ <lastBuildDate>${now}</lastBuildDate>
423
+ <pubDate>${now}</pubDate>
424
+ <ttl>${ttl}</ttl>
425
+ <atom:link href="${siteUrl}/blog/rss.xml" rel="self" type="application/rss+xml"/>
426
+ `;
427
+ if (copyright) {
428
+ rss += ` <copyright>${escapeXml(copyright)}</copyright>
429
+ `;
430
+ }
431
+ if (managingEditor) {
432
+ rss += ` <managingEditor>${escapeXml(managingEditor)}</managingEditor>
433
+ `;
434
+ }
435
+ if (webMaster) {
436
+ rss += ` <webMaster>${escapeXml(webMaster)}</webMaster>
437
+ `;
438
+ }
439
+ if (imageUrl) {
440
+ rss += ` <image>
441
+ <url>${imageUrl}</url>
442
+ <title>${escapeXml(siteName)}</title>
443
+ <link>${siteUrl}</link>
444
+ </image>
445
+ `;
446
+ }
447
+ for (const post of posts) {
448
+ const postUrl = `${siteUrl}/blog/${post.slug}`;
449
+ const pubDate = post.published_at ? new Date(post.published_at).toUTCString() : now;
450
+ const author = typeof post.author === "string" ? post.author : post.author?.name || "Unknown";
451
+ const fullContent = post.content_html || post.content || "";
452
+ const description2 = post.excerpt || stripHtml(fullContent).substring(0, 300) + "...";
453
+ rss += ` <item>
454
+ <title>${escapeXml(post.title)}</title>
455
+ <link>${postUrl}</link>
456
+ <guid isPermaLink="true">${postUrl}</guid>
457
+ <description>${escapeXml(description2)}</description>
458
+ <content:encoded><![CDATA[${fullContent}]]></content:encoded>
459
+ <pubDate>${pubDate}</pubDate>
460
+ <author>${escapeXml(author)}</author>
461
+ `;
462
+ if (post.category) {
463
+ const categoryName = typeof post.category === "string" ? post.category : post.category.name;
464
+ rss += ` <category>${escapeXml(categoryName)}</category>
465
+ `;
466
+ }
467
+ if (post.tags && Array.isArray(post.tags)) {
468
+ for (const tag of post.tags) {
469
+ const tagName = typeof tag === "string" ? tag : tag.name;
470
+ rss += ` <category>${escapeXml(tagName)}</category>
471
+ `;
472
+ }
473
+ }
474
+ if (post.featured_image) {
475
+ rss += ` <enclosure url="${post.featured_image}" type="image/jpeg"/>
476
+ `;
477
+ }
478
+ rss += ` </item>
479
+ `;
480
+ }
481
+ rss += ` </channel>
482
+ </rss>`;
483
+ return rss;
484
+ }
485
+ async function generateAtomFeed(options) {
486
+ const {
487
+ siteUrl,
488
+ siteName,
489
+ description = "Latest blog posts and insights",
490
+ managingEditor
491
+ } = options;
492
+ const posts = await getAllBlogPosts();
493
+ const now = (/* @__PURE__ */ new Date()).toISOString();
494
+ let atom = `<?xml version="1.0" encoding="UTF-8"?>
495
+ <feed xmlns="http://www.w3.org/2005/Atom">
496
+ <title>${escapeXml(siteName)}</title>
497
+ <subtitle>${escapeXml(description)}</subtitle>
498
+ <link href="${siteUrl}/blog/feed.xml" rel="self"/>
499
+ <link href="${siteUrl}"/>
500
+ <id>${siteUrl}/blog</id>
501
+ <updated>${now}</updated>
502
+ `;
503
+ if (managingEditor) {
504
+ atom += ` <author>
505
+ <name>${escapeXml(managingEditor)}</name>
506
+ </author>
507
+ `;
508
+ }
509
+ for (const post of posts) {
510
+ const postUrl = `${siteUrl}/blog/${post.slug}`;
511
+ const updated = post.updated_at || post.published_at || now;
512
+ const published = post.published_at || now;
513
+ const author = typeof post.author === "string" ? post.author : post.author?.name || "Unknown";
514
+ const fullContent = post.content_html || post.content || "";
515
+ atom += ` <entry>
516
+ <title>${escapeXml(post.title)}</title>
517
+ <link href="${postUrl}"/>
518
+ <id>${postUrl}</id>
519
+ <updated>${new Date(updated).toISOString()}</updated>
520
+ <published>${new Date(published).toISOString()}</published>
521
+ <author>
522
+ <name>${escapeXml(author)}</name>
523
+ </author>
524
+ <summary>${escapeXml(post.excerpt || stripHtml(fullContent).substring(0, 300))}</summary>
525
+ <content type="html"><![CDATA[${fullContent}]]></content>
526
+ </entry>
527
+ `;
528
+ }
529
+ atom += `</feed>`;
530
+ return atom;
531
+ }
532
+ async function getPostsByCategory(categorySlug) {
533
+ const { apiUrl, apiKey } = getConfig();
534
+ if (!apiKey) return [];
535
+ try {
536
+ const response = await fetch(`${apiUrl}/public/blog/posts?category=${categorySlug}&limit=50`, {
537
+ headers: { "x-api-key": apiKey },
538
+ next: { revalidate: 300 }
539
+ });
540
+ if (!response.ok) return [];
541
+ const data = await response.json();
542
+ return data.posts || [];
543
+ } catch (error) {
544
+ console.error("[Blog] Error fetching category posts:", error);
545
+ return [];
546
+ }
547
+ }
548
+ async function identifyTopicClusters(categorySlug) {
549
+ const posts = await getPostsByCategory(categorySlug);
550
+ if (posts.length < 3) return null;
551
+ const sortedByLength = [...posts].sort((a, b) => (b.word_count || 0) - (a.word_count || 0));
552
+ const pillar = sortedByLength[0];
553
+ const supportingPosts = sortedByLength.slice(1);
554
+ const internalLinks = [];
555
+ for (const support of supportingPosts) {
556
+ internalLinks.push({
557
+ from: pillar.slug,
558
+ to: support.slug,
559
+ anchor: support.title
560
+ });
561
+ }
562
+ for (const support of supportingPosts) {
563
+ internalLinks.push({
564
+ from: support.slug,
565
+ to: pillar.slug,
566
+ anchor: pillar.title
567
+ });
568
+ }
569
+ return {
570
+ pillar,
571
+ supportingPosts,
572
+ internalLinks
573
+ };
574
+ }
575
+ async function getRelatedInsights(currentSlug, options = {}) {
576
+ const { apiUrl, apiKey } = getConfig();
577
+ const { limit = 3, category } = options;
578
+ if (!apiKey) return [];
579
+ try {
580
+ const response = await fetch(`${apiUrl}/public/blog/related`, {
581
+ method: "POST",
582
+ headers: {
583
+ "x-api-key": apiKey,
584
+ "Content-Type": "application/json"
585
+ },
586
+ body: JSON.stringify({
587
+ slug: currentSlug,
588
+ limit,
589
+ category
590
+ }),
591
+ next: { revalidate: 300 }
592
+ });
593
+ if (!response.ok) return [];
594
+ const data = await response.json();
595
+ return data.posts || [];
596
+ } catch (error) {
597
+ console.error("[Blog] Error fetching related posts:", error);
598
+ return [];
599
+ }
600
+ }
601
+ function generateBreadcrumbSchema(post, options = {}) {
602
+ const { siteUrl = "", siteName = "Home" } = options;
603
+ const items = [
604
+ {
605
+ "@type": "ListItem",
606
+ position: 1,
607
+ name: siteName,
608
+ item: siteUrl
609
+ },
610
+ {
611
+ "@type": "ListItem",
612
+ position: 2,
613
+ name: "Blog",
614
+ item: `${siteUrl}/blog`
615
+ }
616
+ ];
617
+ if (post.category) {
618
+ const categoryName = typeof post.category === "string" ? post.category : post.category.name;
619
+ const categorySlug = typeof post.category === "string" ? post.category.toLowerCase().replace(/\s+/g, "-") : post.category.slug;
620
+ items.push({
621
+ "@type": "ListItem",
622
+ position: 3,
623
+ name: categoryName,
624
+ item: `${siteUrl}/blog/category/${categorySlug}`
625
+ });
626
+ items.push({
627
+ "@type": "ListItem",
628
+ position: 4,
629
+ name: post.title,
630
+ item: `${siteUrl}/blog/${post.slug}`
631
+ });
632
+ } else {
633
+ items.push({
634
+ "@type": "ListItem",
635
+ position: 3,
636
+ name: post.title,
637
+ item: `${siteUrl}/blog/${post.slug}`
638
+ });
639
+ }
640
+ return {
641
+ "@context": "https://schema.org",
642
+ "@type": "BreadcrumbList",
643
+ itemListElement: items
644
+ };
645
+ }
646
+ function generateAllBlogSchemas(post, options = {}) {
647
+ const schemas = [];
648
+ const eeatSchema = post.schema;
649
+ if (eeatSchema) {
650
+ const arr = Array.isArray(eeatSchema) ? eeatSchema : [eeatSchema];
651
+ schemas.push(...arr);
652
+ } else {
653
+ schemas.push(generateBlogPostSchema(post, options));
654
+ const faqItems = post.faq_items ?? post.faqItems;
655
+ if (faqItems && faqItems.length > 0) {
656
+ const faqSchema = generateFaqSchema(faqItems);
657
+ if (faqSchema) schemas.push(faqSchema);
658
+ }
659
+ }
660
+ schemas.push(generateBreadcrumbSchema(post, options));
661
+ return schemas;
662
+ }
663
+
664
+ export { SEO_LIMITS, generateAllBlogSchemas, generateAtomFeed, generateBlogCategoryMetadata, generateBlogIndexMetadata, generateBlogListSchema, generateBlogPostMetadata, generateBlogPostSchema, generateBlogSitemap, generateBlogStaticParams, generateBreadcrumbSchema, generateCategoryStaticParams, generateFaqSchema, generateHowToSchema, generateRssFeed, getAllBlogPosts, getAllBlogSlugs, getBlogCategories, getBlogPost, getPostsByCategory, getRelatedInsights, identifyTopicClusters, validateBlogPostSeo, validateMetaDescription, validateSeoTitle };
665
+ //# sourceMappingURL=server.mjs.map
666
+ //# sourceMappingURL=server.mjs.map