@sonordev/site-kit 8.3.0 → 8.4.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 (100) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/agent-manifest.json +11 -5
  3. package/dist/{AnalyticsProvider-QXO33JUF.js → AnalyticsProvider-D6GWEJSB.js} +5 -5
  4. package/dist/{ArticleViewTracker-KN6ENIRS.js → ArticleViewTracker-YZ76HM64.js} +5 -5
  5. package/dist/{BlocksPopup-3RPN6YVJ.js → BlocksPopup-UJXZ4AXG.js} +5 -5
  6. package/dist/{ChatWidget-D2X445PO.js → ChatWidget-H4EOSH5I.js} +5 -5
  7. package/dist/{FileField-UNGSNGJ5.js → FileField-CKBYGCNT.js} +3 -3
  8. package/dist/{FormSpotlight-JFH2ENKQ.js → FormSpotlight-CGF5ZE6S.js} +1 -1
  9. package/dist/{FormStage-RC3WHHXP.js → FormStage-PQPUPQWW.js} +1 -1
  10. package/dist/{ManagedForm-FOQZ2IYG.js → ManagedForm-YOEZ7CFS.js} +6 -6
  11. package/dist/{ManagedNewsletterForm-NFNF33V3.js → ManagedNewsletterForm-EUWJ2HCA.js} +18 -13
  12. package/dist/{SignalCore-5KABGX5C.js → SignalCore-JZSU7A44.js} +13 -7
  13. package/dist/{SiteChat-JQ46ZIJ4.js → SiteChat-OAZH34W7.js} +2 -2
  14. package/dist/SiteDesignReporter-IWBMZJYU.js +12 -0
  15. package/dist/{SitePopups-WZBKX5Q3.js → SitePopups-MLAT35UN.js} +5 -5
  16. package/dist/SitemapSync-6KQLKFO2.js +9 -0
  17. package/dist/_client/booking-widget.js +5 -5
  18. package/dist/affiliates/index.js +3 -3
  19. package/dist/analytics/index.js +5 -5
  20. package/dist/analytics/publication-cta.d.ts +3 -0
  21. package/dist/{article-styles-7QONZJR5.js → article-styles-AOOVVKXH.js} +4 -2
  22. package/dist/articles/index.js +4 -4
  23. package/dist/articles/server-ui.js +54 -53
  24. package/dist/articles/server.js +4 -4
  25. package/dist/chat/index.js +7 -7
  26. package/dist/{chunk-OIWFXUVD.js → chunk-3VSFOD2Z.js} +2 -2
  27. package/dist/{chunk-DPSNMOMZ.js → chunk-44GVU5UH.js} +1 -1
  28. package/dist/{chunk-QCZHYO5M.js → chunk-4HYIDQJJ.js} +1 -1
  29. package/dist/{chunk-C2UROFGZ.js → chunk-5S7YTOCB.js} +1 -1
  30. package/dist/{chunk-LPKKSYKX.js → chunk-5TDZHIG6.js} +3 -3
  31. package/dist/{chunk-X4IJS7LV.js → chunk-BLCHQIWR.js} +1 -1
  32. package/dist/{chunk-E3UR37RX.js → chunk-BLENFNBW.js} +2 -2
  33. package/dist/{chunk-TT4JZDKM.js → chunk-DKKYZLKR.js} +3 -3
  34. package/dist/{chunk-JO76F5WP.js → chunk-DLM4FFNH.js} +5 -1
  35. package/dist/{chunk-2JMCBJM5.js → chunk-DS6LECIX.js} +1 -1
  36. package/dist/{chunk-SCTVADDL.js → chunk-HH2GOZTO.js} +2 -2
  37. package/dist/{chunk-GKJVSZ7H.js → chunk-ITY4UZEJ.js} +3 -3
  38. package/dist/{chunk-XLAXYPTK.js → chunk-J4KX2D3Q.js} +3 -3
  39. package/dist/{chunk-BOCYBDKV.js → chunk-JCSU6EJI.js} +1 -1
  40. package/dist/{chunk-LBDWAXAO.js → chunk-JHTPHA2D.js} +5 -2
  41. package/dist/{chunk-MQ4REMTH.js → chunk-JV5YWTJN.js} +12 -3
  42. package/dist/{chunk-QDILCQBL.js → chunk-KASKB7UK.js} +11 -10
  43. package/dist/{chunk-5RYSTKVU.js → chunk-LEGMPX6N.js} +1 -1
  44. package/dist/{chunk-WSOZ3FQN.js → chunk-LPVEBQVL.js} +1 -1
  45. package/dist/{chunk-EA6FZMYA.js → chunk-MTV5LYXN.js} +31 -2
  46. package/dist/{chunk-U7Y3QG3A.js → chunk-MZX4EN6Z.js} +1 -1
  47. package/dist/{chunk-3OY52UFM.js → chunk-OKITZFML.js} +1 -1
  48. package/dist/{chunk-QBCWVT2S.js → chunk-PEI6DRSK.js} +2 -2
  49. package/dist/{chunk-HND6BDXC.js → chunk-QD73RO67.js} +2 -2
  50. package/dist/{chunk-4PVMUPC6.js → chunk-QKOVP7WO.js} +1 -1
  51. package/dist/{chunk-C4AFNZT2.js → chunk-RBNHRZGD.js} +1 -1
  52. package/dist/{chunk-WBUG7XRL.js → chunk-S6QDNXDK.js} +1 -1
  53. package/dist/{chunk-KJVUOJMW.js → chunk-TY6WNQO3.js} +150 -67
  54. package/dist/{chunk-SNRE3ZVZ.js → chunk-UVYUTBT4.js} +11 -1
  55. package/dist/{chunk-FHUTOXS7.js → chunk-XENW66OF.js} +1 -1
  56. package/dist/{chunk-IEGDPRQB.js → chunk-XI2XM7UG.js} +1 -1
  57. package/dist/{chunk-NA4Z4CQG.js → chunk-YM5KMNTU.js} +1 -1
  58. package/dist/client/index.js +4 -4
  59. package/dist/fleet/index.js +5 -5
  60. package/dist/forms/index.js +8 -8
  61. package/dist/forms/server.js +2 -2
  62. package/dist/forms/types.d.ts +5 -0
  63. package/dist/forms/useForm.d.ts +4 -0
  64. package/dist/images/index.js +4 -4
  65. package/dist/index.js +1 -1
  66. package/dist/layout/client.js +8 -8
  67. package/dist/layout/index.js +9 -9
  68. package/dist/llms/index.js +4 -4
  69. package/dist/maps/index.js +3 -3
  70. package/dist/mcp/sonor.d.ts +4 -0
  71. package/dist/mcp/sonor.js +33 -16
  72. package/dist/press-kit/articles/artwork.d.ts +3 -0
  73. package/dist/press-kit/articles/index.d.ts +1 -0
  74. package/dist/press-kit/articles/news-sitemap.d.ts +4 -10
  75. package/dist/press-kit/articles/public-reads.d.ts +18 -2
  76. package/dist/press-kit/articles/publication-definition.d.ts +41 -0
  77. package/dist/press-kit/articles/publication-reads.d.ts +54 -0
  78. package/dist/press-kit/articles/server-core.d.ts +21 -3
  79. package/dist/press-kit/articles/types.d.ts +23 -0
  80. package/dist/revalidate/index.js +2 -2
  81. package/dist/runtime/index.js +3 -3
  82. package/dist/seo/client.js +5 -5
  83. package/dist/seo/index.js +5 -5
  84. package/dist/seo/llms.js +4 -4
  85. package/dist/seo/register-sitemap-cli.js +1 -1
  86. package/dist/seo/sitemap.js +2 -2
  87. package/dist/server/index.js +2 -2
  88. package/dist/shared/reporting-gate.d.ts +2 -0
  89. package/dist/shared/version.d.ts +1 -1
  90. package/dist/signal/index.js +2 -2
  91. package/dist/sitemap/index.js +2 -2
  92. package/dist/sync/index.js +5 -5
  93. package/dist/website/images.js +4 -4
  94. package/dist/website/index.js +6 -6
  95. package/dist/website/popups.js +5 -5
  96. package/dist/{writeLLMsTxt-VKHCM5IS.js → writeLLMsTxt-N4Y4YC2H.js} +1 -1
  97. package/package.json +1 -1
  98. package/dist/SiteDesignReporter-N7L6DR4J.js +0 -12
  99. package/dist/SitemapSync-GY6UADRI.js +0 -9
  100. /package/dist/{chunk-D6ZIQMHB.js → chunk-CE7VPAEH.js} +0 -0
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- export { SiteKitClientProviders } from '../chunk-QDILCQBL.js';
2
+ export { SiteKitClientProviders } from '../chunk-KASKB7UK.js';
3
3
  import '../chunk-6G43IRWR.js';
4
- import '../chunk-U7Y3QG3A.js';
5
- import '../chunk-LPKKSYKX.js';
4
+ import '../chunk-MZX4EN6Z.js';
5
+ import '../chunk-5TDZHIG6.js';
6
6
  import '../chunk-ZTZQPYP6.js';
7
7
  import '../chunk-YC7ELZS3.js';
8
8
  import '../chunk-FESKKJAS.js';
@@ -10,11 +10,11 @@ import '../chunk-A4CC4ZG3.js';
10
10
  import '../chunk-ZKADJNR2.js';
11
11
  import '../chunk-5TM3WPCI.js';
12
12
  import '../chunk-GJWI74ZZ.js';
13
- import '../chunk-JO76F5WP.js';
13
+ import '../chunk-DLM4FFNH.js';
14
14
  import '../chunk-EKBEOXTH.js';
15
- import '../chunk-OIWFXUVD.js';
16
- import '../chunk-C4AFNZT2.js';
15
+ import '../chunk-3VSFOD2Z.js';
16
+ import '../chunk-RBNHRZGD.js';
17
17
  import '../chunk-W7OPEMGJ.js';
18
- import '../chunk-C2UROFGZ.js';
19
- import '../chunk-QCZHYO5M.js';
18
+ import '../chunk-5S7YTOCB.js';
19
+ import '../chunk-4HYIDQJJ.js';
20
20
  import '../chunk-PKBMQBKP.js';
@@ -1,11 +1,11 @@
1
- import { SiteKitClientProviders } from '../chunk-QDILCQBL.js';
1
+ import { SiteKitClientProviders } from '../chunk-KASKB7UK.js';
2
2
  import '../chunk-6G43IRWR.js';
3
- import '../chunk-U7Y3QG3A.js';
4
- import '../chunk-LPKKSYKX.js';
3
+ import '../chunk-MZX4EN6Z.js';
4
+ import '../chunk-5TDZHIG6.js';
5
5
  import '../chunk-ZTZQPYP6.js';
6
6
  import { ManagedFavicon } from '../chunk-GW7GSBO7.js';
7
7
  import '../chunk-YC7ELZS3.js';
8
- import { resolveClientCredential } from '../chunk-WSOZ3FQN.js';
8
+ import { resolveClientCredential } from '../chunk-LPVEBQVL.js';
9
9
  import '../chunk-UAYAJPEP.js';
10
10
  import { ManagedScripts } from '../chunk-MVLMXC44.js';
11
11
  import { resolveApiOrigin } from '../chunk-C7TLGL3R.js';
@@ -15,13 +15,13 @@ import '../chunk-A4CC4ZG3.js';
15
15
  import '../chunk-ZKADJNR2.js';
16
16
  import '../chunk-5TM3WPCI.js';
17
17
  import '../chunk-GJWI74ZZ.js';
18
- import '../chunk-JO76F5WP.js';
18
+ import '../chunk-DLM4FFNH.js';
19
19
  import '../chunk-EKBEOXTH.js';
20
- import '../chunk-OIWFXUVD.js';
21
- import '../chunk-C4AFNZT2.js';
20
+ import '../chunk-3VSFOD2Z.js';
21
+ import '../chunk-RBNHRZGD.js';
22
22
  import '../chunk-W7OPEMGJ.js';
23
- import '../chunk-C2UROFGZ.js';
24
- import '../chunk-QCZHYO5M.js';
23
+ import '../chunk-5S7YTOCB.js';
24
+ import '../chunk-4HYIDQJJ.js';
25
25
  import '../chunk-PKBMQBKP.js';
26
26
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
27
27
 
@@ -1,10 +1,10 @@
1
- export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders } from '../chunk-2JMCBJM5.js';
1
+ export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders } from '../chunk-DS6LECIX.js';
2
2
  export { buildAiDiscoveryHeaders, wantsLlmsDiscoveryLink } from '../chunk-PO2TRJPY.js';
3
3
  export { AI_RETRIEVAL_CRAWLERS, AI_TRAINING_CRAWLERS, buildAiCrawlerRules, createRobotsTxtHandler } from '../chunk-QLTDFUJ7.js';
4
- export { LEGACY_PUBLICATION_PATH, SEO_REVALIDATION_MAX_BODY_BYTES, SEO_REVALIDATION_MAX_ITEMS, createLlmsRevalidateHandler, createSeoRevalidationHandler, parseSeoRevalidationPayload } from '../chunk-FHUTOXS7.js';
4
+ export { LEGACY_PUBLICATION_PATH, SEO_REVALIDATION_MAX_BODY_BYTES, SEO_REVALIDATION_MAX_ITEMS, createLlmsRevalidateHandler, createSeoRevalidationHandler, parseSeoRevalidationPayload } from '../chunk-XENW66OF.js';
5
5
  export { AEOBlock, AEOCitedContent, AEOClaim, AEOComparison, AEODefinition, AEOEntity, AEOProvenanceList, AEOStep, AEOSteps, AEOSummary, DEFAULT_SPEAKABLE_SELECTORS, SpeakableSchema, createSpeakableSchema, getSpeakableSelectorsForPage } from '../chunk-PSAG45YF.js';
6
6
  import '../chunk-XTRZGXB2.js';
7
- export { AGENT_ACCESS_HEADING, appendAgentAccessSection, describesMcp, detectMcpServer, formatAgentAccessSection, generateLLMsFullTxt, generateLLMsTxt, writeLLMsTxtToPublic } from '../chunk-D6ZIQMHB.js';
7
+ export { AGENT_ACCESS_HEADING, appendAgentAccessSection, describesMcp, detectMcpServer, formatAgentAccessSection, generateLLMsFullTxt, generateLLMsTxt, writeLLMsTxtToPublic } from '../chunk-CE7VPAEH.js';
8
8
  export { getBusinessInfo, getFAQItems, getLLMsData, getOptimizedLLMsTxt, getPageSummaries, getServices } from '../chunk-XA7Q2VQA.js';
9
9
  import '../chunk-5D7ZISUP.js';
10
10
  import '../chunk-4K735O3J.js';
@@ -16,5 +16,5 @@ import '../chunk-YVKRYQRG.js';
16
16
  import '../chunk-FESKKJAS.js';
17
17
  import '../chunk-A4CC4ZG3.js';
18
18
  import '../chunk-W7OPEMGJ.js';
19
- import '../chunk-C2UROFGZ.js';
19
+ import '../chunk-5S7YTOCB.js';
20
20
  import '../chunk-PKBMQBKP.js';
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { monotonicMs } from '../chunk-MH2DSGC6.js';
3
- import { sonorFetch } from '../chunk-OIWFXUVD.js';
4
- import '../chunk-C4AFNZT2.js';
3
+ import { sonorFetch } from '../chunk-3VSFOD2Z.js';
4
+ import '../chunk-RBNHRZGD.js';
5
5
  import { getClientApiConfig } from '../chunk-W7OPEMGJ.js';
6
- import '../chunk-C2UROFGZ.js';
6
+ import '../chunk-5S7YTOCB.js';
7
7
  import '../chunk-PKBMQBKP.js';
8
8
  import { useState, useCallback, useEffect } from 'react';
9
9
  import { APIProvider, Map as Map$1, AdvancedMarker, Pin, InfoWindow } from '@vis.gl/react-google-maps';
@@ -41,6 +41,7 @@
41
41
  import 'server-only';
42
42
  import { type McpServerDefinition } from './protocol';
43
43
  import type { McpServerInfo, SiteKitMcpTool } from './types';
44
+ import { type PublicationRoutingOptions } from '../articles/routes';
44
45
  export { reportToolCallsToSonor } from './report';
45
46
  export type { ReportToolCallsOptions } from './report';
46
47
  /** The public fields article tools read. Readers can come from Press Kit or a site's own CMS. */
@@ -49,6 +50,8 @@ export interface McpArticle {
49
50
  title: string;
50
51
  excerpt?: string;
51
52
  content?: string;
53
+ content_html?: string;
54
+ canonical_url?: string;
52
55
  author?: unknown;
53
56
  category?: unknown;
54
57
  published_at?: string;
@@ -131,6 +134,7 @@ export interface SonorMcpOptions {
131
134
  * Omitting both readers retains the 8.x legacy adapter. false drops article tools. Default '/blog'. */
132
135
  articles?: ({
133
136
  path?: string;
137
+ routing?: PublicationRoutingOptions;
134
138
  } & Partial<McpArticleReaders>) | false;
135
139
  /** `false` drops get_reviews (a site with no reviews in Sonor). */
136
140
  reviews?: boolean;
package/dist/mcp/sonor.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { fetchReviews, fetchReviewStats } from '../chunk-BSQP6PRA.js';
2
2
  import { toolErrorResult } from '../chunk-FYAB6CXS.js';
3
- import { fetchBookingTypes, fetchAvailability } from '../chunk-WBUG7XRL.js';
3
+ import { fetchBookingTypes, fetchAvailability } from '../chunk-S6QDNXDK.js';
4
4
  import { getLLMsData, getServices, getFAQItems, getPageSummaries } from '../chunk-XA7Q2VQA.js';
5
- import { getFormConfig } from '../chunk-NA4Z4CQG.js';
6
- import '../chunk-5RYSTKVU.js';
5
+ import { getFormConfig } from '../chunk-YM5KMNTU.js';
6
+ import '../chunk-LEGMPX6N.js';
7
7
  import '../chunk-C2FZBUSS.js';
8
8
  import '../chunk-YC7ELZS3.js';
9
9
  import '../chunk-LNSF6BJC.js';
@@ -12,6 +12,7 @@ import '../chunk-TYAVHRWH.js';
12
12
  import '../chunk-5SQK3D53.js';
13
13
  import '../chunk-HW7B43E5.js';
14
14
  import '../chunk-NJCTQH2P.js';
15
+ import { resolveArticleUrl } from '../chunk-4K735O3J.js';
15
16
  import { resolveFetch } from '../chunk-UAYAJPEP.js';
16
17
  import '../chunk-NZ4RXN3G.js';
17
18
  import '../chunk-MJP63SSS.js';
@@ -19,10 +20,10 @@ import { resolveServerApiConfig } from '../chunk-C7TLGL3R.js';
19
20
  import '../chunk-MH2DSGC6.js';
20
21
  import { resolveSiteHost } from '../chunk-FESKKJAS.js';
21
22
  import '../chunk-A4CC4ZG3.js';
22
- import '../chunk-OIWFXUVD.js';
23
- import '../chunk-C4AFNZT2.js';
23
+ import '../chunk-3VSFOD2Z.js';
24
+ import '../chunk-RBNHRZGD.js';
24
25
  import '../chunk-W7OPEMGJ.js';
25
- import { SITE_KIT_VERSION } from '../chunk-C2UROFGZ.js';
26
+ import { SITE_KIT_VERSION } from '../chunk-5S7YTOCB.js';
26
27
  import '../chunk-PKBMQBKP.js';
27
28
  import 'server-only';
28
29
  import { after } from 'next/server.js';
@@ -197,24 +198,36 @@ function findPages(ctx) {
197
198
  }
198
199
  };
199
200
  }
201
+ function articleUrl(ctx, basePath, post) {
202
+ const articles = ctx.options.articles;
203
+ return resolveArticleUrl({ slug: post.slug, canonical_url: post.canonical_url, category: post.category }, {
204
+ ...articles && articles.routing,
205
+ basePath,
206
+ siteUrl: (linkTo(ctx, "/") || "").replace(/\/$/, "")
207
+ });
208
+ }
200
209
  function listArticles(ctx, basePath, readers) {
201
210
  return {
202
211
  name: "list_articles",
203
212
  title: "List articles",
204
213
  description: `${ctx.options.businessName}'s published articles, newest first, optionally matching a topic. Call get_article for the full text.`,
205
- inputSchema: { type: "object", properties: { query: QUERY, limit: LIMIT }, additionalProperties: false },
214
+ inputSchema: { type: "object", properties: { query: QUERY, limit: LIMIT, offset: { type: "integer", minimum: 0, description: "Start at this result offset when browsing the archive." } }, additionalProperties: false },
206
215
  annotations: READ,
207
216
  handler: async (args) => {
208
- const posts = await readers.readMany({ maxPosts: 100, site: ctx.site });
209
- const picked = rankByQuery(posts, text(args.query, 200), (p) => [p.title, [p.excerpt, nameOf(p.category)].filter(Boolean).join(" ")], limitOf(args.limit, 5));
217
+ const posts = await readers.readMany({ maxPosts: Infinity, site: ctx.site });
218
+ const picked = rankByQuery(posts, text(args.query, 200), (p) => [p.title, [p.excerpt, nameOf(p.category), p.content_html || p.content].filter(Boolean).join(" ")], posts.length);
219
+ const offset = Math.max(0, Math.floor(Number(args.offset) || 0));
220
+ const limit = limitOf(args.limit, 5);
210
221
  return {
211
- articles: picked.map((p) => ({
222
+ total: picked.length,
223
+ next_offset: offset + limit < picked.length ? offset + limit : null,
224
+ articles: picked.slice(offset, offset + limit).map((p) => ({
212
225
  slug: p.slug,
213
226
  title: p.title,
214
227
  excerpt: p.excerpt,
215
228
  category: nameOf(p.category),
216
229
  published_at: p.published_at,
217
- url: linkTo(ctx, `${basePath}/${p.slug}`)
230
+ url: articleUrl(ctx, basePath, p)
218
231
  }))
219
232
  };
220
233
  }
@@ -227,7 +240,7 @@ function getArticleTool(ctx, basePath, readers) {
227
240
  description: "One article in full, with approved supporting photo descriptions and section placements, by the slug list_articles returned. Cite it by its URL. Photo captions describe context; they do not verify claims beyond the article.",
228
241
  inputSchema: {
229
242
  type: "object",
230
- properties: { slug: { type: "string", description: "The article slug from list_articles." } },
243
+ properties: { slug: { type: "string", description: "The article slug from list_articles." }, offset: { type: "integer", minimum: 0, description: "Character offset. Follow next_offset until null to read the entire article." } },
231
244
  required: ["slug"],
232
245
  additionalProperties: false
233
246
  },
@@ -236,16 +249,20 @@ function getArticleTool(ctx, basePath, readers) {
236
249
  const slug = text(args.slug, 200).replace(/^\/+|\/+$/g, "").split("/").pop() ?? "";
237
250
  const post = slug ? await readers.readOne(slug, { site: ctx.site }) : null;
238
251
  if (!post) return { found: false, note: `No published article with the slug "${slug}". Call list_articles for the slugs that exist.` };
239
- const content = post.content ?? "";
252
+ const content = post.content_html || post.content || "";
253
+ const offset = Math.max(0, Math.floor(Number(args.offset) || 0));
240
254
  return {
241
255
  found: true,
242
256
  title: post.title,
243
- url: linkTo(ctx, `${basePath}/${post.slug}`),
257
+ url: articleUrl(ctx, basePath, post),
244
258
  author: nameOf(post.author),
245
259
  category: nameOf(post.category),
246
260
  published_at: post.published_at,
247
- content: content.slice(0, ARTICLE_CONTENT_MAX),
248
- truncated: content.length > ARTICLE_CONTENT_MAX,
261
+ content: content.slice(offset, offset + ARTICLE_CONTENT_MAX),
262
+ offset,
263
+ total_length: content.length,
264
+ next_offset: offset + ARTICLE_CONTENT_MAX < content.length ? offset + ARTICLE_CONTENT_MAX : null,
265
+ truncated: offset + ARTICLE_CONTENT_MAX < content.length,
249
266
  faq: post.faq_items,
250
267
  editorial_photos: post.editorial_photos?.slice(0, 8).map(({ url, alt, caption, width, height, placement }) => ({ url, alt, caption, width, height, placement }))
251
268
  };
@@ -10,4 +10,7 @@ type ArtworkSource = Pick<Article, 'title' | 'featured_image' | 'featured_image_
10
10
  export declare function resolveArticleArtwork(post: ArtworkSource, placement?: 'article' | 'card'): ArticleArtwork;
11
11
  /** Preserve generated text and graphics across every stock card surface. */
12
12
  export declare function articleArtworkImageStyle(artwork: ArticleArtwork): CSSProperties | undefined;
13
+ /** Search/discovery artwork is the representative photograph; social cards stay available separately. */
14
+ export declare function resolveRepresentativeImage(post: Partial<Pick<Article, 'title' | 'representative_image' | 'editorial_image' | 'editorial_image_alt' | 'editorial_image_width' | 'editorial_image_height' | 'featured_image' | 'featured_image_alt' | 'featured_image_width' | 'featured_image_height'>>): import('./types').ArticleImage | undefined;
15
+ export declare function articleImageSchema(post: Partial<Article>): object | string | undefined;
13
16
  export {};
@@ -39,6 +39,7 @@
39
39
  * }
40
40
  * ```
41
41
  */
42
+ export type { PublicationDefinition } from './publication-definition';
42
43
  export type { ArticleServerProps, ArticleStyles } from './Article';
43
44
  export type { ArticleListServerProps, ArticleListStyles } from './ArticleList';
44
45
  export type { PublicationSidebarProps } from './PublicationSidebar';
@@ -1,13 +1,4 @@
1
- /**
2
- * Google News sitemap and feed-size helpers. Pure, environment-free: the
3
- * server helpers in server-core fetch the posts and hand them here, so the XML
4
- * rules can be tested without a network.
5
- *
6
- * News sitemap rules (https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap):
7
- * only articles published in the last two days, at most 1,000 URLs, each with
8
- * a publication name, language and publication date. Older articles belong in
9
- * the ordinary sitemap, which already lists them.
10
- */
1
+ import type { Article } from './types';
11
2
  import { type PublicationRoutingOptions, type ArticleRoutePost } from './routes';
12
3
  /** Google only reads articles from the last two days in a News sitemap. */
13
4
  export declare const NEWS_SITEMAP_WINDOW_MS: number;
@@ -23,6 +14,9 @@ export declare const FEED_DEFAULT_ITEMS = 20;
23
14
  export interface NewsSitemapPost extends ArticleRoutePost {
24
15
  title: string;
25
16
  published_at?: string | null;
17
+ representative_image?: Article['representative_image'];
18
+ editorial_image?: string;
19
+ featured_image?: string;
26
20
  }
27
21
  export interface NewsSitemapOptions extends PublicationRoutingOptions {
28
22
  siteUrl: string;
@@ -5,8 +5,24 @@ export interface ArticleReadConfig {
5
5
  /** Sub-site host override. Omit to resolve from NEXT_PUBLIC_SITE_URL. */
6
6
  site?: string;
7
7
  }
8
- /** GET /public/articles/posts/:slug — null when missing or on any failure. */
9
- export declare function fetchArticleBySlug({ apiUrl, apiKey, site }: ArticleReadConfig, slug: string): Promise<Article | null>;
8
+ /** A failed upstream read must never masquerade as a missing article. */
9
+ export declare class ArticleUnavailableError extends Error {
10
+ readonly status?: number | undefined;
11
+ readonly code = "ARTICLE_UNAVAILABLE";
12
+ constructor(status?: number | undefined);
13
+ }
14
+ export type ArticleReadResult<T> = {
15
+ state: 'ready';
16
+ value: T;
17
+ } | {
18
+ state: 'not_found';
19
+ } | {
20
+ state: 'unavailable';
21
+ status?: number;
22
+ };
23
+ export declare function readArticleBySlug({ apiUrl, apiKey, site }: ArticleReadConfig, slug: string): Promise<ArticleReadResult<Article>>;
24
+ /** Returns null only on a confirmed 404; outages propagate to the route error boundary. */
25
+ export declare function fetchArticleBySlug(config: ArticleReadConfig, slug: string): Promise<Article | null>;
10
26
  /** GET /public/articles/categories — [] on any failure. */
11
27
  export declare function fetchArticleCategories({ apiUrl, apiKey, site }: ArticleReadConfig): Promise<ArticleCategory[]>;
12
28
  /**
@@ -0,0 +1,41 @@
1
+ /** One portable, tenant-authored launch definition. Contains no credential or content. */
2
+ export interface PublicationDefinition {
3
+ version: 1;
4
+ name: string;
5
+ description: string;
6
+ siteUrl: string;
7
+ basePath: string;
8
+ style: 'newsroom' | 'journal' | 'briefing';
9
+ sections: Array<{
10
+ slug: string;
11
+ title: string;
12
+ }>;
13
+ newsletterFormId: string;
14
+ brand: {
15
+ primary: string;
16
+ };
17
+ }
18
+ export declare const PUBLICATION_STARTERS: {
19
+ readonly newsroom: {
20
+ readonly label: 'Newsroom';
21
+ readonly description: 'A strong masthead and a visual grid for frequent reporting.';
22
+ readonly columns: 3;
23
+ readonly maxWidth: 1280;
24
+ readonly serif: false;
25
+ };
26
+ readonly journal: {
27
+ readonly label: 'Journal';
28
+ readonly description: 'A quieter serif masthead and roomy stories for expert essays.';
29
+ readonly columns: 2;
30
+ readonly maxWidth: 1040;
31
+ readonly serif: true;
32
+ };
33
+ readonly briefing: {
34
+ readonly label: 'Briefing';
35
+ readonly description: 'A compact single-column reading list for useful updates.';
36
+ readonly columns: 1;
37
+ readonly maxWidth: 820;
38
+ readonly serif: false;
39
+ };
40
+ };
41
+ export declare function definePublication(input: unknown): PublicationDefinition;
@@ -0,0 +1,54 @@
1
+ import 'server-only';
2
+ import type { Article } from './types';
3
+ import type { ArticleReadResult } from './public-reads';
4
+ export interface PublicationIssue {
5
+ id: string;
6
+ title: string;
7
+ subject: string;
8
+ sentAt: string;
9
+ window?: {
10
+ start?: string;
11
+ end?: string;
12
+ };
13
+ articles: Array<{
14
+ id: string;
15
+ title: string;
16
+ url: string;
17
+ }>;
18
+ /** Immutable, public-safe sent snapshot; no recipient tokens or private fields. */
19
+ html?: string;
20
+ text?: string;
21
+ }
22
+ export interface PublicationIssueList {
23
+ issues: PublicationIssue[];
24
+ nextCursor?: string;
25
+ }
26
+ export declare function getPublicationIssues(options?: {
27
+ site?: string;
28
+ limit?: number;
29
+ cursor?: string;
30
+ }): Promise<ArticleReadResult<PublicationIssueList>>;
31
+ export declare function getPublicationIssue(id: string, options?: {
32
+ site?: string;
33
+ }): Promise<ArticleReadResult<PublicationIssue>>;
34
+ export interface ArticlePreview {
35
+ post: Article;
36
+ revision: string;
37
+ expiresAt: string;
38
+ site: string;
39
+ }
40
+ export declare const PUBLICATION_PREVIEW_COOKIE = "_sk_press_preview";
41
+ export declare const PUBLICATION_PREVIEW_HEADERS: {
42
+ 'Cache-Control': string;
43
+ 'X-Robots-Tag': string;
44
+ 'Referrer-Policy': string;
45
+ };
46
+ /** Server-to-server exchange. Never render or log the token; every read rechecks its exact revision. */
47
+ export declare function getArticlePreview(token: string, options: {
48
+ site: string;
49
+ }): Promise<ArticleReadResult<ArticlePreview>>;
50
+ /** CMS entry: validate once, hide token in a scoped HttpOnly cookie, redirect only to configured local preview. */
51
+ export declare function createPublicationPreviewHandler(options: {
52
+ siteUrl: string;
53
+ previewPath: string;
54
+ }): (request: Request) => Promise<Response>;
@@ -7,10 +7,26 @@
7
7
  */
8
8
  import type { Metadata } from 'next';
9
9
  import type { Article as ArticleRecord, ArticleAuthor, TopicCluster, ClusterNavigation } from './types';
10
+ import { ArticleUnavailableError } from './public-reads';
10
11
  import { sanitizeExcerpt } from './excerpt';
12
+ import { articleImageSchema, resolveRepresentativeImage } from './artwork';
11
13
  import { type PublicationRoutingOptions } from './routes';
12
14
  import { type NewsSitemapOptions } from './news-sitemap';
13
- export { sanitizeExcerpt };
15
+ export { sanitizeExcerpt, ArticleUnavailableError, resolveRepresentativeImage, articleImageSchema };
16
+ export type { ArticleReadResult } from './public-reads';
17
+ export declare function getArticleResult(slug: string, options?: {
18
+ site?: string;
19
+ }): Promise<{
20
+ state: 'ready';
21
+ value: ArticleRecord;
22
+ } | {
23
+ state: 'not_found';
24
+ } | {
25
+ state: 'unavailable';
26
+ status?: number;
27
+ } | {
28
+ state: 'unavailable';
29
+ }>;
14
30
  export declare const SEO_LIMITS: {
15
31
  readonly title: {
16
32
  readonly min: 30;
@@ -276,8 +292,8 @@ export interface RssFeedOptions extends PublicationRoutingOptions {
276
292
  * Fetch all published articles for RSS feed.
277
293
  *
278
294
  * Paginates with `per_page`/`page` — the only pagination params the deployed
279
- * controller reads — accumulating up to FEED_MAX_POSTS. A mid-loop failure
280
- * returns what was accumulated so far: a partial feed beats an empty one.
295
+ * controller reads — accumulating up to FEED_MAX_POSTS. A mid-loop failure throws ArticleUnavailableError; a truncated archive must
296
+ * not be reported as a complete collection.
281
297
  */
282
298
  export declare function getAllArticles(options?: {
283
299
  maxPosts?: number; /** Sub-site host override (see getArticle). */
@@ -459,3 +475,5 @@ export { insertArticlePhotos, articlePhotoCss } from './article-photos';
459
475
  export type { ArticleEditorialPhoto } from './types';
460
476
  export { FEED_DEFAULT_ITEMS, NEWS_SITEMAP_MAX_URLS, NEWS_SITEMAP_WINDOW_MS, buildNewsSitemapXml, cdata, recentNewsPosts } from './news-sitemap';
461
477
  export type { NewsSitemapOptions, NewsSitemapPost } from './news-sitemap';
478
+ export { getPublicationIssues, getPublicationIssue, getArticlePreview, createPublicationPreviewHandler, PUBLICATION_PREVIEW_COOKIE, PUBLICATION_PREVIEW_HEADERS } from './publication-reads';
479
+ export type { PublicationIssue, PublicationIssueList, ArticlePreview } from './publication-reads';
@@ -17,6 +17,13 @@ export interface ArticleEditorialPhoto {
17
17
  headingId?: string;
18
18
  };
19
19
  }
20
+ export interface ArticleImage {
21
+ url: string;
22
+ alt?: string;
23
+ /** Actual asset dimensions, never an assumed social ratio. */
24
+ width?: number;
25
+ height?: number;
26
+ }
20
27
  export interface Article {
21
28
  id: string;
22
29
  project_id: string;
@@ -31,10 +38,15 @@ export interface Article {
31
38
  /** Complete promotional image, including composed share-card text when generated. */
32
39
  featured_image?: string;
33
40
  featured_image_alt?: string;
41
+ featured_image_width?: number;
42
+ featured_image_height?: number;
43
+ representative_image?: ArticleImage;
34
44
  /** Article artwork, separate from the promotional card. Optional on older APIs. */
35
45
  editorial_image?: string;
36
46
  /** Empty for decorative atmosphere artwork; preserve an explicitly empty value. */
37
47
  editorial_image_alt?: string;
48
+ editorial_image_width?: number;
49
+ editorial_image_height?: number;
38
50
  /** Dedicated index frame from Sonor's atmosphere + content-stage renderer. */
39
51
  index_image?: string;
40
52
  index_image_alt?: string;
@@ -71,6 +83,17 @@ export interface Article {
71
83
  description?: string;
72
84
  url?: string;
73
85
  }[];
86
+ /** An editor-approved public note, distinct from generation or update timestamps. */
87
+ publication_cta?: {
88
+ label: string;
89
+ url: string;
90
+ goal: string;
91
+ };
92
+ editorial_verification?: {
93
+ note: string;
94
+ reviewedAt?: string;
95
+ reviewDueAt?: string;
96
+ };
74
97
  /** Publishing */
75
98
  status: 'draft' | 'published' | 'scheduled' | 'archived';
76
99
  published_at?: string;
@@ -1,7 +1,7 @@
1
- import { createSeoRevalidationHandler } from '../chunk-FHUTOXS7.js';
1
+ import { createSeoRevalidationHandler } from '../chunk-XENW66OF.js';
2
2
  import '../chunk-XTRZGXB2.js';
3
3
  export { SITE_CACHE_TAGS, SITE_REVALIDATE_PATH, isSiteRevalidatePong } from '../chunk-6KU7STM3.js';
4
- import '../chunk-C2UROFGZ.js';
4
+ import '../chunk-5S7YTOCB.js';
5
5
  import '../chunk-PKBMQBKP.js';
6
6
 
7
7
  // src/revalidate/index.ts
@@ -1,8 +1,8 @@
1
1
  import '../chunk-B7T2SFUF.js';
2
2
  export { readAnalyticsIdentity } from '../chunk-5ROTTMBL.js';
3
3
  import '../chunk-5TM3WPCI.js';
4
- export { sonorFetch } from '../chunk-OIWFXUVD.js';
5
- import '../chunk-C4AFNZT2.js';
4
+ export { sonorFetch } from '../chunk-3VSFOD2Z.js';
5
+ import '../chunk-RBNHRZGD.js';
6
6
  export { DEFAULT_SONOR_API_URL, getClientApiConfig, readPublishedApiConfig } from '../chunk-W7OPEMGJ.js';
7
- import '../chunk-C2UROFGZ.js';
7
+ import '../chunk-5S7YTOCB.js';
8
8
  import '../chunk-PKBMQBKP.js';
@@ -1,10 +1,10 @@
1
1
  'use client';
2
- export { SitemapSync } from '../chunk-SCTVADDL.js';
2
+ export { SitemapSync } from '../chunk-HH2GOZTO.js';
3
3
  import '../chunk-YVKRYQRG.js';
4
- import '../chunk-JO76F5WP.js';
4
+ import '../chunk-DLM4FFNH.js';
5
5
  import '../chunk-EKBEOXTH.js';
6
- import '../chunk-OIWFXUVD.js';
7
- import '../chunk-C4AFNZT2.js';
6
+ import '../chunk-3VSFOD2Z.js';
7
+ import '../chunk-RBNHRZGD.js';
8
8
  import '../chunk-W7OPEMGJ.js';
9
- import '../chunk-C2UROFGZ.js';
9
+ import '../chunk-5S7YTOCB.js';
10
10
  import '../chunk-PKBMQBKP.js';
package/dist/seo/index.js CHANGED
@@ -2,7 +2,7 @@ export { ManagedNoScripts, ManagedScripts } from '../chunk-MVLMXC44.js';
2
2
  import { pickManagedLlmSchemaForJsonLd } from '../chunk-NZ4RXN3G.js';
3
3
  import { withoutSchemaPlaceholders } from '../chunk-VWWITFWM.js';
4
4
  import { safeJsonLd } from '../chunk-4KZ6W3ZH.js';
5
- export { SitemapSync } from '../chunk-SCTVADDL.js';
5
+ export { SitemapSync } from '../chunk-HH2GOZTO.js';
6
6
  import { getManagedMetadata, mergeAlternates } from '../chunk-URJN75ZG.js';
7
7
  export { generateSitemap, getABVariant, getManagedMetadata, getManagedMetadataWithAB, getRedirect, getRobotsDirective, isIndexable } from '../chunk-URJN75ZG.js';
8
8
  import { getSchemaMarkups, getSEOPageData, getEntityEnhancedSchema, getFAQData, getInternalLinks, pruneSchemaTypes } from '../chunk-Y35BWW2C.js';
@@ -16,12 +16,12 @@ import '../chunk-YVKRYQRG.js';
16
16
  import '../chunk-DIKNRN34.js';
17
17
  import '../chunk-FESKKJAS.js';
18
18
  import '../chunk-A4CC4ZG3.js';
19
- import '../chunk-JO76F5WP.js';
19
+ import '../chunk-DLM4FFNH.js';
20
20
  import '../chunk-EKBEOXTH.js';
21
- import '../chunk-OIWFXUVD.js';
22
- import '../chunk-C4AFNZT2.js';
21
+ import '../chunk-3VSFOD2Z.js';
22
+ import '../chunk-RBNHRZGD.js';
23
23
  import '../chunk-W7OPEMGJ.js';
24
- import '../chunk-C2UROFGZ.js';
24
+ import '../chunk-5S7YTOCB.js';
25
25
  import '../chunk-PKBMQBKP.js';
26
26
  import { Suspense } from 'react';
27
27
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
package/dist/seo/llms.js CHANGED
@@ -1,10 +1,10 @@
1
- export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders } from '../chunk-2JMCBJM5.js';
1
+ export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders } from '../chunk-DS6LECIX.js';
2
2
  export { buildAiDiscoveryHeaders, wantsLlmsDiscoveryLink } from '../chunk-PO2TRJPY.js';
3
3
  export { AI_RETRIEVAL_CRAWLERS, AI_TRAINING_CRAWLERS, buildAiCrawlerRules, createRobotsTxtHandler } from '../chunk-QLTDFUJ7.js';
4
- export { LEGACY_PUBLICATION_PATH, SEO_REVALIDATION_MAX_BODY_BYTES, SEO_REVALIDATION_MAX_ITEMS, createLlmsRevalidateHandler, createSeoRevalidationHandler, parseSeoRevalidationPayload } from '../chunk-FHUTOXS7.js';
4
+ export { LEGACY_PUBLICATION_PATH, SEO_REVALIDATION_MAX_BODY_BYTES, SEO_REVALIDATION_MAX_ITEMS, createLlmsRevalidateHandler, createSeoRevalidationHandler, parseSeoRevalidationPayload } from '../chunk-XENW66OF.js';
5
5
  export { AEOBlock, AEOCitedContent, AEOClaim, AEOComparison, AEODefinition, AEOEntity, AEOProvenanceList, AEOStep, AEOSteps, AEOSummary, DEFAULT_SPEAKABLE_SELECTORS, SpeakableSchema, createSpeakableSchema, getSpeakableSelectorsForPage } from '../chunk-PSAG45YF.js';
6
6
  import '../chunk-XTRZGXB2.js';
7
- export { AGENT_ACCESS_HEADING, appendAgentAccessSection, describesMcp, detectMcpServer, formatAgentAccessSection, generateLLMsFullTxt, generateLLMsTxt, writeLLMsTxtToPublic } from '../chunk-D6ZIQMHB.js';
7
+ export { AGENT_ACCESS_HEADING, appendAgentAccessSection, describesMcp, detectMcpServer, formatAgentAccessSection, generateLLMsFullTxt, generateLLMsTxt, writeLLMsTxtToPublic } from '../chunk-CE7VPAEH.js';
8
8
  export { getBusinessInfo, getFAQItems, getLLMsData, getOptimizedLLMsTxt, getPageSummaries, getServices } from '../chunk-XA7Q2VQA.js';
9
9
  import '../chunk-5D7ZISUP.js';
10
10
  import '../chunk-4K735O3J.js';
@@ -16,5 +16,5 @@ import '../chunk-YVKRYQRG.js';
16
16
  import '../chunk-FESKKJAS.js';
17
17
  import '../chunk-A4CC4ZG3.js';
18
18
  import '../chunk-W7OPEMGJ.js';
19
- import '../chunk-C2UROFGZ.js';
19
+ import '../chunk-5S7YTOCB.js';
20
20
  import '../chunk-PKBMQBKP.js';
@@ -458,7 +458,7 @@ async function registerLocalSitemap(options) {
458
458
  }
459
459
  if (options.writeLlms && result.success) {
460
460
  try {
461
- const { writeLLMsTxtToPublic } = await import('../writeLLMsTxt-VKHCM5IS.js');
461
+ const { writeLLMsTxtToPublic } = await import('../writeLLMsTxt-N4Y4YC2H.js');
462
462
  const written = await writeLLMsTxtToPublic({
463
463
  // Resolved here too: the sync's own `site` is scoped to the branch that
464
464
  // posts, and this runs on the skip path as well.
@@ -1,5 +1,5 @@
1
- export { createSitemap, getPortfolioPaths, syncSitemapToPortal } from '../chunk-BOCYBDKV.js';
2
- import '../chunk-D6ZIQMHB.js';
1
+ export { createSitemap, getPortfolioPaths, syncSitemapToPortal } from '../chunk-JCSU6EJI.js';
2
+ import '../chunk-CE7VPAEH.js';
3
3
  import '../chunk-XA7Q2VQA.js';
4
4
  import '../chunk-Z22QVHT4.js';
5
5
  import '../chunk-5D7ZISUP.js';
@@ -1,8 +1,8 @@
1
1
  import '../chunk-34ZYJTQR.js';
2
- export { resolveClientCredential } from '../chunk-WSOZ3FQN.js';
2
+ export { resolveClientCredential } from '../chunk-LPVEBQVL.js';
3
3
  import '../chunk-UAYAJPEP.js';
4
4
  export { SITE_CACHE_TAGS } from '../chunk-6KU7STM3.js';
5
5
  export { isNextControlFlow, requireServerApiConfig, resolveApiOrigin, resolveServerApiConfig, serverApiFetch } from '../chunk-C7TLGL3R.js';
6
6
  import '../chunk-MH2DSGC6.js';
7
- import '../chunk-C2UROFGZ.js';
7
+ import '../chunk-5S7YTOCB.js';
8
8
  import '../chunk-PKBMQBKP.js';
@@ -3,6 +3,8 @@
3
3
  * Analytics, fleet, SitemapSync, chat and popups all resolve through this gate.
4
4
  * Kept separate from transport wrappers so non-analytics modules stay lean.
5
5
  */
6
+ /** A server-rendered marker keeps private drafts out of every reporting path. */
7
+ export declare function isPrivatePreview(): boolean;
6
8
  /** Resolved destination for an analytics send. */
7
9
  export interface AnalyticsTarget {
8
10
  apiUrl: string;
@@ -7,4 +7,4 @@
7
7
  * it matches package.json at publish time, and version.test.ts asserts it in
8
8
  * dev, so it can't silently drift.
9
9
  */
10
- export declare const SITE_KIT_VERSION = "8.3.0";
10
+ export declare const SITE_KIT_VERSION = "8.4.0";