@uptrademedia/site-kit 1.0.4 → 1.0.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 (178) hide show
  1. package/README.md +96 -25
  2. package/dist/analytics/index.js +7 -7
  3. package/dist/analytics/index.mjs +3 -3
  4. package/dist/api-QUIPJJCX.js +77 -0
  5. package/dist/api-QUIPJJCX.js.map +1 -0
  6. package/dist/api-V3BA5PMX.mjs +4 -0
  7. package/dist/api-V3BA5PMX.mjs.map +1 -0
  8. package/dist/blog/index.d.mts +160 -5
  9. package/dist/blog/index.d.ts +160 -5
  10. package/dist/blog/index.js +1166 -18
  11. package/dist/blog/index.js.map +1 -1
  12. package/dist/blog/index.mjs +1156 -18
  13. package/dist/blog/index.mjs.map +1 -1
  14. package/dist/blog/server.d.mts +229 -0
  15. package/dist/blog/server.d.ts +229 -0
  16. package/dist/blog/server.js +692 -0
  17. package/dist/blog/server.js.map +1 -0
  18. package/dist/blog/server.mjs +666 -0
  19. package/dist/blog/server.mjs.map +1 -0
  20. package/dist/{chunk-FKVJOT2F.mjs → chunk-42EXHJTC.mjs} +196 -7
  21. package/dist/chunk-42EXHJTC.mjs.map +1 -0
  22. package/dist/{scanner-AZV5I6US.mjs → chunk-44OMJFCG.mjs} +354 -14
  23. package/dist/chunk-44OMJFCG.mjs.map +1 -0
  24. package/dist/chunk-4TGJYNHV.js +981 -0
  25. package/dist/chunk-4TGJYNHV.js.map +1 -0
  26. package/dist/chunk-4XPGGLVP.mjs +53 -0
  27. package/dist/{chunk-NYKRE2FL.mjs.map → chunk-4XPGGLVP.mjs.map} +1 -1
  28. package/dist/{generators-TO2FKJR6.mjs → chunk-6ONUXZDO.mjs} +26 -9
  29. package/dist/chunk-6ONUXZDO.mjs.map +1 -0
  30. package/dist/chunk-CG53ASWX.mjs +729 -0
  31. package/dist/chunk-CG53ASWX.mjs.map +1 -0
  32. package/dist/{scanner-ETJAMIT7.js → chunk-DERI27QC.js} +448 -102
  33. package/dist/chunk-DERI27QC.js.map +1 -0
  34. package/dist/chunk-DYM5ML2V.mjs +1518 -0
  35. package/dist/chunk-DYM5ML2V.mjs.map +1 -0
  36. package/dist/chunk-FLZZOX44.js +1526 -0
  37. package/dist/chunk-FLZZOX44.js.map +1 -0
  38. package/dist/{chunk-7H6I3ECV.mjs → chunk-FQVGK746.mjs} +63 -3
  39. package/dist/chunk-FQVGK746.mjs.map +1 -0
  40. package/dist/{chunk-GQ6ZOU2N.mjs → chunk-JGQPAXTL.mjs} +4 -4
  41. package/dist/{chunk-GQ6ZOU2N.mjs.map → chunk-JGQPAXTL.mjs.map} +1 -1
  42. package/dist/{chunk-V3F5J6CV.js → chunk-JUEVN4Q4.js} +196 -7
  43. package/dist/chunk-JUEVN4Q4.js.map +1 -0
  44. package/dist/chunk-KKMGTT7F.mjs +968 -0
  45. package/dist/chunk-KKMGTT7F.mjs.map +1 -0
  46. package/dist/{chunk-XQJX252G.mjs → chunk-MB3WR5KJ.mjs} +28 -18
  47. package/dist/chunk-MB3WR5KJ.mjs.map +1 -0
  48. package/dist/{chunk-2IHTEKHU.mjs → chunk-QD5CN2OI.mjs} +16 -7
  49. package/dist/chunk-QD5CN2OI.mjs.map +1 -0
  50. package/dist/chunk-QD66FTXZ.mjs +278 -0
  51. package/dist/chunk-QD66FTXZ.mjs.map +1 -0
  52. package/dist/{chunk-SBVEYCSV.js → chunk-QQB4FO4Q.js} +7 -7
  53. package/dist/{chunk-SBVEYCSV.js.map → chunk-QQB4FO4Q.js.map} +1 -1
  54. package/dist/{generators-YZWIGHCO.js → chunk-S2GXR5HY.js} +26 -9
  55. package/dist/chunk-S2GXR5HY.js.map +1 -0
  56. package/dist/{chunk-O2OHHBUD.js → chunk-TDK7DLCH.js} +30 -20
  57. package/dist/chunk-TDK7DLCH.js.map +1 -0
  58. package/dist/chunk-UJQ73OS6.js +282 -0
  59. package/dist/chunk-UJQ73OS6.js.map +1 -0
  60. package/dist/{chunk-QP5NCO2E.js → chunk-VDI7KYME.js} +67 -2
  61. package/dist/chunk-VDI7KYME.js.map +1 -0
  62. package/dist/chunk-VOR53RUR.js +753 -0
  63. package/dist/chunk-VOR53RUR.js.map +1 -0
  64. package/dist/{chunk-GAJLEDRD.js → chunk-ZKJ7JKFV.js} +16 -7
  65. package/dist/chunk-ZKJ7JKFV.js.map +1 -0
  66. package/dist/chunk-ZSMWDLMK.js +63 -0
  67. package/dist/{chunk-EQCVQC35.js.map → chunk-ZSMWDLMK.js.map} +1 -1
  68. package/dist/cli/index.js +37269 -0
  69. package/dist/cli/index.js.map +1 -0
  70. package/dist/cli/index.mjs +37233 -0
  71. package/dist/cli/index.mjs.map +1 -0
  72. package/dist/commerce/index.js +1 -1
  73. package/dist/commerce/index.mjs +1 -1
  74. package/dist/commerce/server.d.mts +12 -3
  75. package/dist/commerce/server.d.ts +12 -3
  76. package/dist/commerce/server.js +71 -70
  77. package/dist/commerce/server.js.map +1 -1
  78. package/dist/commerce/server.mjs +71 -70
  79. package/dist/commerce/server.mjs.map +1 -1
  80. package/dist/engage/index.d.mts +6 -4
  81. package/dist/engage/index.d.ts +6 -4
  82. package/dist/engage/index.js +8 -4
  83. package/dist/engage/index.mjs +2 -2
  84. package/dist/forms/index.js +1 -1
  85. package/dist/forms/index.mjs +1 -1
  86. package/dist/generators-5EU4PTVF.js +33 -0
  87. package/dist/generators-5EU4PTVF.js.map +1 -0
  88. package/dist/generators-TYPILCWD.mjs +4 -0
  89. package/dist/generators-TYPILCWD.mjs.map +1 -0
  90. package/dist/images/index.js +11 -11
  91. package/dist/images/index.mjs +4 -4
  92. package/dist/index.d.mts +155 -5
  93. package/dist/index.d.ts +155 -5
  94. package/dist/index.js +979 -50
  95. package/dist/index.js.map +1 -1
  96. package/dist/index.mjs +821 -8
  97. package/dist/index.mjs.map +1 -1
  98. package/dist/llms/index.d.mts +657 -0
  99. package/dist/llms/index.d.ts +657 -0
  100. package/dist/llms/index.js +101 -0
  101. package/dist/llms/index.js.map +1 -0
  102. package/dist/llms/index.mjs +4 -0
  103. package/dist/llms/index.mjs.map +1 -0
  104. package/dist/migrator-ARLHUNB3.mjs +4 -0
  105. package/dist/migrator-ARLHUNB3.mjs.map +1 -0
  106. package/dist/migrator-VZLBH3VY.js +37 -0
  107. package/dist/migrator-VZLBH3VY.js.map +1 -0
  108. package/dist/redirects/index.js +1 -1
  109. package/dist/redirects/index.mjs +1 -1
  110. package/dist/reputation/index.d.mts +57 -0
  111. package/dist/reputation/index.d.ts +57 -0
  112. package/dist/reputation/index.js +21 -0
  113. package/dist/reputation/index.js.map +1 -0
  114. package/dist/reputation/index.mjs +4 -0
  115. package/dist/reputation/index.mjs.map +1 -0
  116. package/dist/{routing-BWjUF7lp.d.ts → routing-CF91y6NO.d.ts} +1 -1
  117. package/dist/{routing-CgmRi9tD.d.mts → routing-CIOFpFCB.d.mts} +1 -1
  118. package/dist/scanner-7ZMUM2P5.mjs +4 -0
  119. package/dist/scanner-7ZMUM2P5.mjs.map +1 -0
  120. package/dist/scanner-OY7UF3WA.js +53 -0
  121. package/dist/scanner-OY7UF3WA.js.map +1 -0
  122. package/dist/seo/index.d.mts +267 -7
  123. package/dist/seo/index.d.ts +267 -7
  124. package/dist/seo/index.js +432 -24
  125. package/dist/seo/index.js.map +1 -1
  126. package/dist/seo/index.mjs +400 -11
  127. package/dist/seo/index.mjs.map +1 -1
  128. package/dist/seo/server.d.mts +11 -4
  129. package/dist/seo/server.d.ts +11 -4
  130. package/dist/seo/server.js +17 -17
  131. package/dist/seo/server.mjs +3 -3
  132. package/dist/setup/client.js +1 -1
  133. package/dist/setup/client.mjs +1 -1
  134. package/dist/setup/index.js +3 -3
  135. package/dist/setup/index.mjs +2 -2
  136. package/dist/setup/server.js +3 -3
  137. package/dist/setup/server.mjs +2 -2
  138. package/dist/sitemap/index.js +2 -2
  139. package/dist/sitemap/index.js.map +1 -1
  140. package/dist/sitemap/index.mjs +2 -2
  141. package/dist/sitemap/index.mjs.map +1 -1
  142. package/dist/{types-C0pJGfbH.d.mts → types-D6FHAVWX.d.mts} +99 -3
  143. package/dist/{types-C0pJGfbH.d.ts → types-D6FHAVWX.d.ts} +99 -3
  144. package/dist/{types-BDojCvvL.d.mts → types-DI0jnhjJ.d.mts} +31 -8
  145. package/dist/{types-BDojCvvL.d.ts → types-DI0jnhjJ.d.ts} +31 -8
  146. package/dist/{types-BmzutFwy.d.ts → types-j8X4vUhB.d.mts} +19 -2
  147. package/dist/{types-BmzutFwy.d.mts → types-j8X4vUhB.d.ts} +19 -2
  148. package/dist/{web-vitals-BH55V7EJ.js → web-vitals-444RLW3B.js} +11 -11
  149. package/dist/{web-vitals-BH55V7EJ.js.map → web-vitals-444RLW3B.js.map} +1 -1
  150. package/dist/{web-vitals-RJYPWAR3.mjs → web-vitals-KPICZIEF.mjs} +3 -3
  151. package/dist/{web-vitals-RJYPWAR3.mjs.map → web-vitals-KPICZIEF.mjs.map} +1 -1
  152. package/package.json +22 -10
  153. package/dist/api-N35S3EES.js +0 -57
  154. package/dist/api-N35S3EES.js.map +0 -1
  155. package/dist/api-SYBTK7Z7.mjs +0 -4
  156. package/dist/api-SYBTK7Z7.mjs.map +0 -1
  157. package/dist/chunk-2IHTEKHU.mjs.map +0 -1
  158. package/dist/chunk-7H6I3ECV.mjs.map +0 -1
  159. package/dist/chunk-BGJLOJ7T.mjs +0 -605
  160. package/dist/chunk-BGJLOJ7T.mjs.map +0 -1
  161. package/dist/chunk-EQCVQC35.js +0 -35
  162. package/dist/chunk-FKVJOT2F.mjs.map +0 -1
  163. package/dist/chunk-GAJLEDRD.js.map +0 -1
  164. package/dist/chunk-NYKRE2FL.mjs +0 -31
  165. package/dist/chunk-O2OHHBUD.js.map +0 -1
  166. package/dist/chunk-QAYJV4KK.js +0 -608
  167. package/dist/chunk-QAYJV4KK.js.map +0 -1
  168. package/dist/chunk-QP5NCO2E.js.map +0 -1
  169. package/dist/chunk-V3F5J6CV.js.map +0 -1
  170. package/dist/chunk-XQJX252G.mjs.map +0 -1
  171. package/dist/generators-TO2FKJR6.mjs.map +0 -1
  172. package/dist/generators-YZWIGHCO.js.map +0 -1
  173. package/dist/migrator-V6KS75EA.mjs +0 -265
  174. package/dist/migrator-V6KS75EA.mjs.map +0 -1
  175. package/dist/migrator-XKM7YQCY.js +0 -272
  176. package/dist/migrator-XKM7YQCY.js.map +0 -1
  177. package/dist/scanner-AZV5I6US.mjs.map +0 -1
  178. package/dist/scanner-ETJAMIT7.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  export { SitemapSync } from '../chunk-WPSRS352.mjs';
2
- export { generateSitemap, getRedirect, getRobotsDirective, isIndexable } from '../chunk-GQ6ZOU2N.mjs';
3
- import { getSEOPageData, getABTest, recordABImpression, getSchemaMarkups, getFAQData, getInternalLinks, getContentBlock, getManagedScripts } from '../chunk-7H6I3ECV.mjs';
4
- import '../chunk-NYKRE2FL.mjs';
2
+ export { generateSitemap, getRedirect, getRobotsDirective, isIndexable } from '../chunk-JGQPAXTL.mjs';
3
+ import { getSEOPageData, getABTest, recordABImpression, getSchemaMarkups, getEntityEnhancedSchema, getFAQData, getInternalLinks, getContentBlock, getEntities, getManagedScripts } from '../chunk-FQVGK746.mjs';
4
+ import '../chunk-4XPGGLVP.mjs';
5
+ import { cache } from 'react';
6
+ import 'server-only';
5
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
8
 
7
9
  // src/seo/getManagedMetadata.ts
@@ -25,8 +27,8 @@ async function getManagedMetadata(options) {
25
27
  } else if (fallback.title) {
26
28
  metadata.title = fallback.title;
27
29
  }
28
- if (pageData.managed_description) {
29
- metadata.description = pageData.managed_description;
30
+ if (pageData.managed_meta_description || pageData.managed_description) {
31
+ metadata.description = pageData.managed_meta_description || pageData.managed_description;
30
32
  } else if (fallback.description) {
31
33
  metadata.description = fallback.description;
32
34
  }
@@ -45,7 +47,7 @@ async function getManagedMetadata(options) {
45
47
  };
46
48
  }
47
49
  const ogTitle = pageData.managed_og_title || pageData.managed_title;
48
- const ogDescription = pageData.managed_og_description || pageData.managed_description;
50
+ const ogDescription = pageData.managed_og_description || pageData.managed_meta_description || pageData.managed_description;
49
51
  const ogImage = pageData.managed_og_image;
50
52
  if (ogTitle || ogDescription || ogImage) {
51
53
  metadata.openGraph = {
@@ -115,21 +117,235 @@ async function getManagedMetadataWithAB(options) {
115
117
  }
116
118
  return metadata;
117
119
  }
120
+ function getSecureApiConfig() {
121
+ const apiUrl = process.env.UPTRADE_API_URL || process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com";
122
+ const apiKey = process.env.UPTRADE_API_KEY || "";
123
+ const projectId = process.env.UPTRADE_PROJECT_ID || "";
124
+ if (!apiKey) {
125
+ throw new Error("@uptrade/seo: UPTRADE_API_KEY environment variable is required for server-side SEO functions");
126
+ }
127
+ return { apiUrl, apiKey, projectId };
128
+ }
129
+ async function secureApiPost(endpoint, body = {}) {
130
+ const { apiUrl, apiKey } = getSecureApiConfig();
131
+ try {
132
+ const response = await fetch(`${apiUrl}${endpoint}`, {
133
+ method: "POST",
134
+ headers: {
135
+ "Content-Type": "application/json",
136
+ "x-api-key": apiKey
137
+ },
138
+ body: JSON.stringify(body),
139
+ next: { revalidate: 60 }
140
+ // Cache for 60 seconds
141
+ });
142
+ if (!response.ok) {
143
+ console.error(`@uptrade/seo: API error ${response.status}: ${response.statusText}`);
144
+ return null;
145
+ }
146
+ return await response.json();
147
+ } catch (error) {
148
+ console.error("@uptrade/seo: Network error:", error);
149
+ return null;
150
+ }
151
+ }
152
+ var getSEOPageData2 = cache(async (projectId, path) => {
153
+ const result = await secureApiPost("/api/public/seo/page", {
154
+ projectId,
155
+ path
156
+ });
157
+ return result?.page || null;
158
+ });
159
+ var getSchemaMarkups2 = cache(async (projectId, path, options) => {
160
+ const result = await secureApiPost("/api/public/seo/schemas", {
161
+ projectId,
162
+ path,
163
+ includeTypes: options?.includeTypes,
164
+ excludeTypes: options?.excludeTypes
165
+ });
166
+ return result?.schemas || [];
167
+ });
168
+ var getFAQData2 = cache(async (projectId, path) => {
169
+ const result = await secureApiPost("/api/public/seo/faq", {
170
+ projectId,
171
+ path
172
+ });
173
+ return result?.faq || null;
174
+ });
175
+ var getInternalLinks2 = cache(async (projectId, sourcePath, options) => {
176
+ const result = await secureApiPost("/api/public/seo/internal-links", {
177
+ projectId,
178
+ sourcePath,
179
+ position: options?.position,
180
+ limit: options?.limit
181
+ });
182
+ return result?.links || [];
183
+ });
184
+ var getContentBlock2 = cache(async (projectId, path, section) => {
185
+ const result = await secureApiPost("/api/public/seo/content", {
186
+ projectId,
187
+ path,
188
+ section
189
+ });
190
+ return result?.content || null;
191
+ });
192
+ var getABTest2 = cache(async (projectId, path, field) => {
193
+ const result = await secureApiPost("/api/public/seo/ab-test", {
194
+ projectId,
195
+ path,
196
+ field
197
+ });
198
+ return result?.test || null;
199
+ });
200
+ async function recordABImpression2(testId, variant, sessionId) {
201
+ await secureApiPost("/api/public/seo/ab-impression", { testId, variant, sessionId });
202
+ }
203
+ var getRedirectData = cache(async (projectId, path) => {
204
+ const result = await secureApiPost("/api/public/seo/redirect", {
205
+ projectId,
206
+ path
207
+ });
208
+ return result?.redirect || null;
209
+ });
210
+ var getManagedScripts2 = cache(async (projectId, position, currentPath) => {
211
+ const result = await secureApiPost("/api/public/seo/scripts", {
212
+ projectId,
213
+ position,
214
+ currentPath
215
+ });
216
+ return result?.scripts || [];
217
+ });
218
+ var getRobotsData = cache(async (projectId, path) => {
219
+ const result = await secureApiPost("/api/public/seo/page", {
220
+ projectId,
221
+ path
222
+ });
223
+ return result?.page?.managed_robots || null;
224
+ });
225
+ var getSitemapEntries = cache(async (projectId, options) => {
226
+ const result = await secureApiPost("/api/public/seo/sitemap", {
227
+ projectId,
228
+ publishedOnly: options?.publishedOnly
229
+ });
230
+ return result?.entries || [];
231
+ });
232
+ async function registerSitemap(entries) {
233
+ const { apiUrl, apiKey, projectId } = getSecureApiConfig();
234
+ try {
235
+ const response = await fetch(`${apiUrl}/api/public/seo/register-sitemap`, {
236
+ method: "POST",
237
+ headers: {
238
+ "Content-Type": "application/json",
239
+ "x-api-key": apiKey
240
+ },
241
+ body: JSON.stringify({ projectId, entries })
242
+ });
243
+ if (!response.ok) {
244
+ console.error(`@uptrade/seo: Sitemap registration failed: ${response.statusText}`);
245
+ return { success: false, created: 0, updated: 0 };
246
+ }
247
+ return await response.json();
248
+ } catch (error) {
249
+ console.error("@uptrade/seo: Sitemap registration error:", error);
250
+ return { success: false, created: 0, updated: 0 };
251
+ }
252
+ }
253
+ function getSignalApiConfig() {
254
+ const apiUrl = process.env.SIGNAL_API_URL || process.env.NEXT_PUBLIC_SIGNAL_API_URL || "https://signal.uptrademedia.com";
255
+ const apiKey = process.env.UPTRADE_API_KEY || "";
256
+ return { apiUrl, apiKey };
257
+ }
258
+ async function signalApiGet(endpoint) {
259
+ const { apiUrl, apiKey } = getSignalApiConfig();
260
+ if (!apiKey) {
261
+ return null;
262
+ }
263
+ try {
264
+ const response = await fetch(`${apiUrl}${endpoint}`, {
265
+ method: "GET",
266
+ headers: {
267
+ "Content-Type": "application/json",
268
+ "x-api-key": apiKey
269
+ },
270
+ next: { revalidate: 300 }
271
+ // Cache for 5 minutes
272
+ });
273
+ if (!response.ok) {
274
+ return null;
275
+ }
276
+ const result = await response.json();
277
+ return result?.data || result;
278
+ } catch (error) {
279
+ console.error("@uptrade/seo: Signal API error:", error);
280
+ return null;
281
+ }
282
+ }
283
+ var getEntities2 = cache(async (projectId, options) => {
284
+ let endpoint = `/skills/seo/entities/${projectId}`;
285
+ if (options?.type) {
286
+ endpoint += `?type=${options.type}`;
287
+ }
288
+ const result = await signalApiGet(endpoint);
289
+ return result || [];
290
+ });
291
+ var getPrimaryEntity = cache(async (projectId) => {
292
+ return signalApiGet(`/skills/seo/entities/${projectId}/primary`);
293
+ });
294
+ var getEntityEnhancedSchema2 = cache(async (projectId, pagePath) => {
295
+ const result = await signalApiGet(
296
+ `/skills/seo/schema/${projectId}/entity-enhanced?pagePath=${encodeURIComponent(pagePath)}`
297
+ );
298
+ return result?.schemas || [];
299
+ });
300
+ var getVisibilityScore = cache(async (projectId, pagePath) => {
301
+ const result = await signalApiGet(`/skills/seo/visibility/${projectId}`);
302
+ if (!result) return null;
303
+ return result.find((s) => s.page_path === pagePath) || null;
304
+ });
305
+ var getVisibilitySummary = cache(async (projectId) => {
306
+ return signalApiGet(`/skills/seo/visibility/${projectId}/summary`);
307
+ });
308
+ var DEFAULT_SPEAKABLE_SELECTORS = [
309
+ "h1",
310
+ '[data-speakable="true"]',
311
+ ".page-summary",
312
+ ".key-points",
313
+ '.aeo-block[data-speakable="true"]'
314
+ ];
118
315
  async function ManagedSchema({
119
316
  projectId,
120
317
  path,
121
318
  additionalSchemas = [],
122
319
  includeTypes,
123
- excludeTypes
320
+ excludeTypes,
321
+ speakable,
322
+ pageType = "WebPage",
323
+ pageName,
324
+ pageUrl,
325
+ includeEntityGraph = false
124
326
  }) {
125
327
  const schemas = await getSchemaMarkups(projectId, path, {
126
328
  includeTypes,
127
329
  excludeTypes
128
330
  });
331
+ let entitySchemas = [];
332
+ if (includeEntityGraph) {
333
+ entitySchemas = await getEntityEnhancedSchema(projectId, path);
334
+ }
129
335
  const allSchemas = [
336
+ ...entitySchemas,
130
337
  ...schemas.map((s) => s.schema_json),
131
338
  ...additionalSchemas
132
339
  ];
340
+ if (speakable && pageName && pageUrl) {
341
+ const speakableSchema = createSpeakableWebPageSchema(
342
+ pageType,
343
+ pageName,
344
+ pageUrl,
345
+ typeof speakable === "object" ? speakable : void 0
346
+ );
347
+ allSchemas.push(speakableSchema);
348
+ }
133
349
  if (allSchemas.length === 0) {
134
350
  return null;
135
351
  }
@@ -158,6 +374,25 @@ function createSchema(type, data) {
158
374
  ...data
159
375
  };
160
376
  }
377
+ function createSpeakableWebPageSchema(type, name, url, speakable) {
378
+ const speakableSpec = {
379
+ "@type": "SpeakableSpecification"
380
+ };
381
+ if (speakable?.cssSelector?.length) {
382
+ speakableSpec.cssSelector = speakable.cssSelector;
383
+ } else if (speakable?.xpath?.length) {
384
+ speakableSpec.xpath = speakable.xpath;
385
+ } else {
386
+ speakableSpec.cssSelector = DEFAULT_SPEAKABLE_SELECTORS;
387
+ }
388
+ return {
389
+ "@context": "https://schema.org",
390
+ "@type": type,
391
+ name,
392
+ url,
393
+ speakable: speakableSpec
394
+ };
395
+ }
161
396
  function createBreadcrumbSchema(baseUrl, path, labels) {
162
397
  const segments = path.split("/").filter(Boolean);
163
398
  const items = segments.map((segment, index) => {
@@ -297,13 +532,46 @@ async function ManagedInternalLinks({
297
532
  function renderMarkdown(content) {
298
533
  return content.replace(/^### (.*$)/gim, "<h3>$1</h3>").replace(/^## (.*$)/gim, "<h2>$1</h2>").replace(/^# (.*$)/gim, "<h1>$1</h1>").replace(/\*\*(.*)\*\*/gim, "<strong>$1</strong>").replace(/\*(.*)\*/gim, "<em>$1</em>").replace(/\[(.*?)\]\((.*?)\)/gim, '<a href="$2">$1</a>').replace(/\n\n/gim, "</p><p>").replace(/^(.+)$/gim, "<p>$1</p>");
299
534
  }
535
+ function injectEntityAnnotations(html, entities) {
536
+ if (!entities.length) return html;
537
+ let annotatedHtml = html;
538
+ const sortedEntities = [...entities].sort((a, b) => b.name.length - a.name.length);
539
+ for (const entity of sortedEntities) {
540
+ if (entity.name.length < 3) continue;
541
+ const regex = new RegExp(
542
+ `(?<![\\w-])(?<!data-sonor-entity=")${escapeRegExp(entity.name)}(?![\\w-])`,
543
+ "gi"
544
+ );
545
+ const schemaType = getSchemaTypeForEntity(entity.entity_type);
546
+ annotatedHtml = annotatedHtml.replace(regex, (match) => {
547
+ return `<span class="aeo-entity aeo-entity-${entity.entity_type}" data-sonor-entity="${entity.id}" data-sonor-entity-type="${entity.entity_type}" data-sonor-entity-name="${entity.name}" itemscope itemtype="https://schema.org/${schemaType}"><span itemprop="name">${match}</span></span>`;
548
+ });
549
+ }
550
+ return annotatedHtml;
551
+ }
552
+ function escapeRegExp(string) {
553
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
554
+ }
555
+ function getSchemaTypeForEntity(entityType) {
556
+ const typeMap = {
557
+ organization: "Organization",
558
+ person: "Person",
559
+ service: "Service",
560
+ product: "Product",
561
+ location: "Place",
562
+ concept: "Thing",
563
+ credential: "EducationalOccupationalCredential"
564
+ };
565
+ return typeMap[entityType] || "Thing";
566
+ }
300
567
  async function ManagedContent({
301
568
  projectId,
302
569
  path,
303
570
  section,
304
571
  fallback,
305
572
  className,
306
- components = {}
573
+ components = {},
574
+ injectEntityAnnotations: shouldInjectEntities = false
307
575
  }) {
308
576
  const block = await getContentBlock(projectId, path, section);
309
577
  if (!block) {
@@ -312,18 +580,32 @@ async function ManagedContent({
312
580
  }
313
581
  return null;
314
582
  }
583
+ let entities = [];
584
+ if (shouldInjectEntities) {
585
+ try {
586
+ entities = await getEntities(projectId);
587
+ } catch (err) {
588
+ console.warn("@uptrade/seo: Failed to fetch entities for annotation:", err);
589
+ }
590
+ }
315
591
  const containerClass = className || `uptrade-content uptrade-content--${section}`;
592
+ const processHtml = (html) => {
593
+ if (shouldInjectEntities && entities.length > 0) {
594
+ return injectEntityAnnotations(html, entities);
595
+ }
596
+ return html;
597
+ };
316
598
  switch (block.content_type) {
317
599
  case "html":
318
600
  return /* @__PURE__ */ jsx(
319
601
  "div",
320
602
  {
321
603
  className: containerClass,
322
- dangerouslySetInnerHTML: { __html: block.content }
604
+ dangerouslySetInnerHTML: { __html: processHtml(block.content) }
323
605
  }
324
606
  );
325
607
  case "markdown":
326
- const htmlContent = renderMarkdown(block.content);
608
+ const htmlContent = processHtml(renderMarkdown(block.content));
327
609
  return /* @__PURE__ */ jsx(
328
610
  "div",
329
611
  {
@@ -405,7 +687,114 @@ async function ManagedNoScripts({
405
687
  }
406
688
  return /* @__PURE__ */ jsx("noscript", { dangerouslySetInnerHTML: { __html: noscriptContent } });
407
689
  }
690
+ function getApiConfig() {
691
+ const apiUrl = process.env.UPTRADE_API_URL || process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com";
692
+ return { apiUrl };
693
+ }
694
+ var getLocationSection = cache(async (projectId, path, section) => {
695
+ const { apiUrl } = getApiConfig();
696
+ try {
697
+ const response = await fetch(`${apiUrl}/api/public/seo/location-content`, {
698
+ method: "POST",
699
+ headers: { "Content-Type": "application/json" },
700
+ body: JSON.stringify({
701
+ project_id: projectId,
702
+ path,
703
+ section
704
+ }),
705
+ next: { revalidate: 3600 }
706
+ // Cache for 1 hour
707
+ });
708
+ if (!response.ok) {
709
+ console.error(`LocationPageContent: Failed to fetch section "${section}" for path "${path}"`);
710
+ return null;
711
+ }
712
+ const data = await response.json();
713
+ return data;
714
+ } catch (error) {
715
+ console.error("LocationPageContent: API error:", error);
716
+ return null;
717
+ }
718
+ });
719
+ function HeroRenderer({ data, className }) {
720
+ return /* @__PURE__ */ jsxs("section", { className: className || "location-hero", children: [
721
+ /* @__PURE__ */ jsx("h1", { children: data.title }),
722
+ data.subtitle && /* @__PURE__ */ jsx("p", { className: "subtitle", children: data.subtitle }),
723
+ data.stats && /* @__PURE__ */ jsx("div", { className: "stats-grid", children: data.stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { className: "stat", children: [
724
+ /* @__PURE__ */ jsx("span", { className: "stat-value", children: stat.value }),
725
+ /* @__PURE__ */ jsx("span", { className: "stat-label", children: stat.label })
726
+ ] }, i)) }),
727
+ data.cta_text && data.cta_href && /* @__PURE__ */ jsx("a", { href: data.cta_href, className: "cta-button", children: data.cta_text })
728
+ ] });
729
+ }
730
+ function TextRenderer({ data, className }) {
731
+ if (data.html) {
732
+ return /* @__PURE__ */ jsx(
733
+ "section",
734
+ {
735
+ className: className || "location-text",
736
+ dangerouslySetInnerHTML: { __html: data.html }
737
+ }
738
+ );
739
+ }
740
+ return /* @__PURE__ */ jsxs("section", { className: className || "location-text", children: [
741
+ data.heading && /* @__PURE__ */ jsx("h2", { children: data.heading }),
742
+ data.paragraphs.map((p, i) => /* @__PURE__ */ jsx("p", { children: p }, i))
743
+ ] });
744
+ }
745
+ function ServicesGridRenderer({ data, className }) {
746
+ return /* @__PURE__ */ jsx("section", { className: className || "location-services-grid", children: /* @__PURE__ */ jsx("div", { className: "services-list", children: data.services.map((service, i) => /* @__PURE__ */ jsxs("a", { href: service.href, className: "service-card", children: [
747
+ service.icon && /* @__PURE__ */ jsx("span", { className: "service-icon", children: service.icon }),
748
+ /* @__PURE__ */ jsx("h3", { children: service.title }),
749
+ /* @__PURE__ */ jsx("p", { children: service.description })
750
+ ] }, i)) }) });
751
+ }
752
+ function DefaultRenderer({ data, className }) {
753
+ if (process.env.NODE_ENV === "development") {
754
+ return /* @__PURE__ */ jsx("section", { className, children: /* @__PURE__ */ jsx("pre", { style: { fontSize: "12px", background: "#f0f0f0", padding: "1rem" }, children: JSON.stringify(data, null, 2) }) });
755
+ }
756
+ if (typeof data.content === "string") {
757
+ return /* @__PURE__ */ jsx(
758
+ "section",
759
+ {
760
+ className,
761
+ dangerouslySetInnerHTML: { __html: data.content }
762
+ }
763
+ );
764
+ }
765
+ return null;
766
+ }
767
+ async function LocationPageContent({
768
+ projectId,
769
+ path,
770
+ section,
771
+ fallback = null,
772
+ className,
773
+ render
774
+ }) {
775
+ const data = await getLocationSection(projectId, path, section);
776
+ if (!data) {
777
+ return fallback;
778
+ }
779
+ if (render) {
780
+ return /* @__PURE__ */ jsx(Fragment, { children: render(data) });
781
+ }
782
+ switch (data.type) {
783
+ case "hero":
784
+ return /* @__PURE__ */ jsx(HeroRenderer, { data: data.content, className });
785
+ case "text":
786
+ case "intro":
787
+ case "about":
788
+ case "about_location":
789
+ return /* @__PURE__ */ jsx(TextRenderer, { data: data.content, className });
790
+ case "services_grid":
791
+ case "services":
792
+ return /* @__PURE__ */ jsx(ServicesGridRenderer, { data: data.content, className });
793
+ default:
794
+ return /* @__PURE__ */ jsx(DefaultRenderer, { data, className });
795
+ }
796
+ }
408
797
 
409
- export { ManagedContent, ManagedFAQ, ManagedInternalLinks, ManagedNoScripts, ManagedSchema, ManagedScripts, createBreadcrumbSchema, createSchema, ManagedSchema as default, getABVariant, getManagedContentData, getManagedMetadata, getManagedMetadataWithAB };
798
+ export { LocationPageContent, ManagedContent, ManagedFAQ, ManagedInternalLinks, ManagedNoScripts, ManagedSchema, ManagedScripts, createBreadcrumbSchema, createSchema, ManagedSchema as default, getABTest2 as getABTest, getABVariant, getContentBlock2 as getContentBlock, getEntities2 as getEntities, getEntityEnhancedSchema2 as getEntityEnhancedSchema, getFAQData2 as getFAQData, getInternalLinks2 as getInternalLinks, getLocationSection, getManagedContentData, getManagedMetadata, getManagedMetadataWithAB, getManagedScripts2 as getManagedScripts, getPrimaryEntity, getRedirectData, getRobotsData, getSEOPageData2 as getSEOPageData, getSchemaMarkups2 as getSchemaMarkups, getSitemapEntries, getVisibilityScore, getVisibilitySummary, recordABImpression2 as recordABImpression, registerSitemap };
410
799
  //# sourceMappingURL=index.mjs.map
411
800
  //# sourceMappingURL=index.mjs.map