@sonordev/site-kit 7.0.2 → 7.1.1

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 (126) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/agent-manifest.json +11 -5
  3. package/dist/{AnalyticsProvider-XXWTFKJH.js → AnalyticsProvider-3MI5RCTL.js} +4 -4
  4. package/dist/{ArticleViewTracker-V4KZB6QN.js → ArticleViewTracker-CMBSJR3N.js} +3 -3
  5. package/dist/{BlocksPopup-JHGHB6XW.js → BlocksPopup-X3OAAK2L.js} +4 -4
  6. package/dist/{ChatWidget-CG32POI3.js → ChatWidget-JMFBXJ75.js} +5 -5
  7. package/dist/{EngageWidget-LQMR4LEX.js → EngageWidget-A3QKZ3SU.js} +4 -4
  8. package/dist/{FileField-KUG3CKXG.js → FileField-QRSEGO6D.js} +3 -3
  9. package/dist/{FormSpotlight-FVNPOCU3.js → FormSpotlight-XKV7VTN4.js} +1 -1
  10. package/dist/{FormStage-C7VKRURJ.js → FormStage-C3YGYAUZ.js} +1 -1
  11. package/dist/{ManagedForm-VLNJKV65.js → ManagedForm-2GSSDJPP.js} +6 -6
  12. package/dist/{ManagedNewsletterForm-KJEU23BV.js → ManagedNewsletterForm-LPIKGJF4.js} +4 -4
  13. package/dist/{SignalCore-K2O46QG7.js → SignalCore-ICXXZBYI.js} +3 -3
  14. package/dist/{SiteDesignReporter-D7MD66GI.js → SiteDesignReporter-A7MS2CIP.js} +5 -5
  15. package/dist/SitemapSync-4U654SEA.js +8 -0
  16. package/dist/_client/booking-widget.js +5 -5
  17. package/dist/affiliates/index.js +3 -3
  18. package/dist/analytics/index.js +4 -4
  19. package/dist/articles/index.js +1 -1
  20. package/dist/articles/server-ui.js +1 -1
  21. package/dist/chat/index.js +5 -5
  22. package/dist/{chunk-KXPBMCFL.js → chunk-3QI26673.js} +3 -1
  23. package/dist/{chunk-6HDT4G4A.js → chunk-4YTYGG2C.js} +2 -2
  24. package/dist/{chunk-O52CH273.js → chunk-542MIHNP.js} +1 -1
  25. package/dist/{chunk-N2UVOR3X.js → chunk-662ILEZ6.js} +2 -0
  26. package/dist/chunk-6G43IRWR.js +4 -0
  27. package/dist/{chunk-B6RZ2NRH.js → chunk-6OL23QYD.js} +1 -1
  28. package/dist/{chunk-GGD4P7UW.js → chunk-7FWA3I6A.js} +1 -1
  29. package/dist/{chunk-T4SY3FMN.js → chunk-B73QPZPH.js} +3 -3
  30. package/dist/{chunk-KPAZG65P.js → chunk-CGWUXUYZ.js} +138 -46
  31. package/dist/{chunk-56JNI463.js → chunk-DUQMSNF2.js} +1 -1
  32. package/dist/{chunk-5FBY2ZIH.js → chunk-EFPS56JA.js} +1 -1
  33. package/dist/{chunk-P72ZJRSX.js → chunk-EQBZ24VH.js} +3 -3
  34. package/dist/{chunk-K5WZX776.js → chunk-H42PZKVC.js} +2 -2
  35. package/dist/{chunk-TT63HHIT.js → chunk-HAG4YIZY.js} +1 -1
  36. package/dist/{chunk-ZRE4ZYEG.js → chunk-ICFRH2ED.js} +1 -1
  37. package/dist/{chunk-7JIKGKWD.js → chunk-ILAW3XKD.js} +30 -7
  38. package/dist/{chunk-SSUQKA7L.js → chunk-J4D6ZXRW.js} +1 -1
  39. package/dist/{chunk-SWP36NCB.js → chunk-JC7KXNNM.js} +1 -1
  40. package/dist/{chunk-LVESVYCE.js → chunk-JIL5VY2R.js} +11 -117
  41. package/dist/{chunk-BS7FWUOY.js → chunk-KOPVJQ2P.js} +1 -1
  42. package/dist/chunk-LPH5FANE.js +169 -0
  43. package/dist/{chunk-ZSLRAMCK.js → chunk-NEKEH4CK.js} +1 -1
  44. package/dist/{chunk-DKTSGYLM.js → chunk-ODADQM6Z.js} +2 -2
  45. package/dist/{chunk-OIETJKIL.js → chunk-OUNRFXDC.js} +1 -1
  46. package/dist/{chunk-RU2RMTGT.js → chunk-PXZ7LAOY.js} +2 -2
  47. package/dist/{chunk-SAUTJMK6.js → chunk-Q3S26EQA.js} +1 -1
  48. package/dist/{chunk-BEL7YFMC.js → chunk-R3VKF43H.js} +1 -1
  49. package/dist/{chunk-P2GIIQH5.js → chunk-RK7GF7PQ.js} +1 -1
  50. package/dist/{chunk-42OXY4JV.js → chunk-RTMMHTEQ.js} +1 -1
  51. package/dist/chunk-RYVDGXC2.js +19 -0
  52. package/dist/{chunk-LJZ3SUET.js → chunk-V4HUSHZO.js} +2 -2
  53. package/dist/chunk-VCJYLYJV.js +49 -0
  54. package/dist/{chunk-GYY6ETGB.js → chunk-ZAWANWUS.js} +9 -5
  55. package/dist/chunk-ZETJTCMV.js +118 -0
  56. package/dist/client/index.js +3 -3
  57. package/dist/cms/CmsPage.d.ts +1 -0
  58. package/dist/cms/CmsPreview.d.ts +1 -0
  59. package/dist/cms/CmsSection.d.ts +1 -0
  60. package/dist/cms/index.d.ts +6 -0
  61. package/dist/cms/server-api.d.ts +3 -0
  62. package/dist/commerce/index.js +4 -4
  63. package/dist/config/index.js +1 -1
  64. package/dist/contracts/entries.d.ts +1 -1
  65. package/dist/contracts/site-cache.d.ts +55 -0
  66. package/dist/contracts/site-edit-param.d.ts +7 -0
  67. package/dist/contracts/site-edit.d.ts +77 -0
  68. package/dist/contracts/slot-content.d.ts +111 -0
  69. package/dist/contracts/slots.d.ts +39 -25
  70. package/dist/engage/index.js +6 -6
  71. package/dist/fleet/index.js +4 -4
  72. package/dist/forms/index.js +8 -8
  73. package/dist/forms/server.js +2 -2
  74. package/dist/images/index.js +4 -4
  75. package/dist/index.js +1 -1
  76. package/dist/layout/client.js +8 -7
  77. package/dist/layout/index.js +9 -8
  78. package/dist/llms/index.js +4 -2
  79. package/dist/llms/seo-revalidate.d.ts +8 -1
  80. package/dist/maps/index.js +3 -3
  81. package/dist/mcp/sonor.js +6 -6
  82. package/dist/overlay-RXV6U6QC.js +353 -0
  83. package/dist/proxy/index.js +2 -2
  84. package/dist/proxy/securityHeaders.d.ts +4 -0
  85. package/dist/revalidate/index.d.ts +44 -0
  86. package/dist/revalidate/index.js +27 -0
  87. package/dist/seo/ManagedContent.d.ts +2 -0
  88. package/dist/seo/client.js +4 -4
  89. package/dist/seo/index.js +9 -8
  90. package/dist/seo/llms.js +4 -2
  91. package/dist/seo/register-sitemap-cli.js +1 -1
  92. package/dist/seo/server.js +3 -2
  93. package/dist/seo/sitemap.js +2 -2
  94. package/dist/server/index.js +2 -2
  95. package/dist/{server-api-GJJQZVG7.js → server-api-BVCBLJKL.js} +2 -1
  96. package/dist/shared/build-entries.d.ts +1 -0
  97. package/dist/shared/edit-bridge.d.ts +8 -0
  98. package/dist/shared/version.d.ts +1 -1
  99. package/dist/signal/index.js +2 -2
  100. package/dist/sitemap/index.js +2 -2
  101. package/dist/slots/ManagedLink.d.ts +31 -0
  102. package/dist/slots/ManagedList.d.ts +30 -0
  103. package/dist/slots/ManagedRichText.d.ts +31 -0
  104. package/dist/slots/contract.js +2 -1
  105. package/dist/slots/edit/locate.d.ts +30 -0
  106. package/dist/slots/edit/overlay.d.ts +18 -0
  107. package/dist/slots/index.d.ts +12 -4
  108. package/dist/slots/index.js +4 -2
  109. package/dist/slots/revalidate.d.ts +8 -3
  110. package/dist/slots/rich.d.ts +7 -0
  111. package/dist/slots/server-api.d.ts +6 -2
  112. package/dist/sync/index.js +5 -5
  113. package/dist/website/images.js +4 -4
  114. package/dist/website/index.js +5 -5
  115. package/dist/website/popups.js +4 -4
  116. package/dist/website/slots/contract.js +2 -1
  117. package/dist/website/slots.js +4 -2
  118. package/dist/{writeLLMsTxt-UMHKGNRR.js → writeLLMsTxt-QR23OQUE.js} +1 -1
  119. package/docs.json +2 -0
  120. package/package.json +6 -1
  121. package/src/revalidate/README.md +82 -0
  122. package/src/seo/README.md +4 -17
  123. package/src/slots/README.md +168 -0
  124. package/src/sync/README.md +1 -1
  125. package/dist/SitemapSync-NMXGMPCQ.js +0 -8
  126. package/dist/chunk-SS636UDN.js +0 -35
@@ -0,0 +1,30 @@
1
+ import * as React from 'react';
2
+ import { type SlotListItem } from './contract';
3
+ import type { SlotResolveOptions } from './server-api';
4
+ export interface ManagedListProps extends Omit<SlotResolveOptions, 'fallback' | 'type' | 'fallbackValue'> {
5
+ /** Stable slot id, e.g. "home-services" or "gallery". */
6
+ id: string;
7
+ /** The static items: `{ title?, body?, href?, image?: { src, alt, width?, height? } }`. */
8
+ items: SlotListItem[];
9
+ /** Draw the items. Called with the managed items, or `items` when nothing's managed. */
10
+ children: (items: SlotListItem[]) => React.ReactNode;
11
+ }
12
+ /**
13
+ * ManagedList — a list the owner edits in Sonor: services, team, FAQs, a
14
+ * gallery. You keep the markup; Sonor manages the items. Each item has an
15
+ * optional title, plain-text body, link and image (https only).
16
+ *
17
+ * Server component, fallback-first and signature-verified like ManagedSlot.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <ManagedList id="home-services" items={SERVICES}>
22
+ * {(items) => (
23
+ * <ul className="grid">
24
+ * {items.map((s) => <li key={s.title}><a href={s.href}>{s.title}</a><p>{s.body}</p></li>)}
25
+ * </ul>
26
+ * )}
27
+ * </ManagedList>
28
+ * ```
29
+ */
30
+ export declare function ManagedList({ id, items, children, site, revalidate, page, }: ManagedListProps): Promise<React.ReactElement>;
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import type { SlotResolveOptions } from './server-api';
3
+ export interface ManagedRichTextProps extends Omit<SlotResolveOptions, 'fallback' | 'type' | 'fallbackValue'> {
4
+ /** Stable slot id, lowercase, page-scoped by convention (e.g. "about-intro"). */
5
+ id: string;
6
+ /**
7
+ * The static content. A string is read as the rich-text subset (so the
8
+ * fallback and the managed copy render the same way); JSX renders as is.
9
+ */
10
+ children: React.ReactNode;
11
+ }
12
+ /**
13
+ * ManagedRichText — a Sonor-managed block of formatted copy: paragraphs,
14
+ * line breaks, bold, italic, links and lists. Renders a fragment of `<p>`,
15
+ * `<ul>`/`<ol>`, `<strong>`, `<em>`, `<a>` and `<br>`; wrap it in your own
16
+ * element for styling.
17
+ *
18
+ * Server component, fallback-first and signature-verified like ManagedSlot.
19
+ * Managed content is parsed by the contract's rich-text parser and rendered
20
+ * as elements, never as HTML.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * <div className="prose">
25
+ * <ManagedRichText id="about-intro" page="/about">
26
+ * {`We've built homes in **Cincinnati** since 1998.\n\n- Custom builds\n- Remodels`}
27
+ * </ManagedRichText>
28
+ * </div>
29
+ * ```
30
+ */
31
+ export declare function ManagedRichText({ id, children, site, revalidate, page, }: ManagedRichTextProps): Promise<React.ReactElement>;
@@ -1,2 +1,3 @@
1
- export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-SS636UDN.js';
1
+ export { SLOTS_CONTRACT_VERSION, SUPPORTED_SLOTS_CONTRACT_VERSIONS, canonicalJson, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-VCJYLYJV.js';
2
+ export { SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_FIELD_MAX_LENGTH, SLOT_HREF_MAX_LENGTH, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, SLOT_V1_CONTENT_TYPES, isSafeSlotHref, isSafeSlotImageSrc, isSlotContentType, isValidSlotId, normalizeSlotValue, parseSlotRichText, slotListPlainText } from '../chunk-LPH5FANE.js';
2
3
  import '../chunk-PKBMQBKP.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Find managed copy on a rendered page by what it says.
3
+ *
4
+ * The slot components render plain text: no wrapper element, no attribute,
5
+ * so a site's markup and CSS are exactly what it wrote (a marker element would
6
+ * shift `:first-child`, `h2 + p` and Tailwind's `space-y-*` for every visitor
7
+ * to serve one editor). The edit overlay finds each slot the way a person
8
+ * would, by its visible text, using the needles from `slotNeedles`.
9
+ */
10
+ import { type EditableSlot } from '../../contracts/site-edit';
11
+ export interface LocatedSlot {
12
+ key: string;
13
+ /** The element to outline and click. */
14
+ element: HTMLElement;
15
+ /** For a text slot rendered as one text node: the node, so a preview can swap just it. */
16
+ textNode?: Text;
17
+ }
18
+ /**
19
+ * The deepest unclaimed element whose whole visible text is `needle`: first a
20
+ * single text node, then an element whose text is split across inline tags
21
+ * (`Fast <em>and</em> local`).
22
+ */
23
+ export declare function findByText(root: Element, needle: string, claimed: Set<Element>): {
24
+ element: HTMLElement;
25
+ textNode?: Text;
26
+ } | null;
27
+ /** Locate one slot, or null when its text isn't on this page. */
28
+ export declare function locateSlot(root: Element, slot: EditableSlot, claimed: Set<Element>): LocatedSlot | null;
29
+ /** Locate every slot on the page; each element belongs to at most one slot. */
30
+ export declare function locateSlots(root: Element, slots: EditableSlot[]): LocatedSlot[];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The edit overlay: what a site shows inside Sonor's "Edit on page".
3
+ *
4
+ * Loaded only when the page is framed cross-origin with `?sonor_edit`
5
+ * (`shared/edit-bridge.ts`), and it only acts for the Sonor dashboard
6
+ * (`isSonorAppOrigin`). It outlines the managed copy it can find, reports a
7
+ * click as a selection, shows a draft in place, and reloads on request. It
8
+ * never writes anything: the dashboard saves with the person's own session.
9
+ *
10
+ * Plain DOM, no React, so it adds nothing to a visitor's bundle; the bridge
11
+ * imports it on demand.
12
+ */
13
+ /** The dashboard's origin, from the browser's own record of who framed us. */
14
+ export declare function framingOrigin(win: Window): string | null;
15
+ /** The same URL with the edit parameter, so the overlay starts again after a navigation. */
16
+ export declare function withEditParam(href: string, base: string): string | null;
17
+ /** Start the overlay. Returns a function that removes every trace of it. */
18
+ export declare function startEditOverlay(win?: Window): () => void;
@@ -1,14 +1,22 @@
1
1
  /**
2
- * @sonordev/site-kit/slots — Managed Slots (3.0 spine, Pillar 1)
2
+ * @sonordev/site-kit/website/slots — Managed Slots (3.0 spine, Pillar 1)
3
3
  *
4
- * Named, Sonor-managed text regions inside developer-owned markup.
4
+ * Named, Sonor-managed copy inside developer-owned markup: text, rich text,
5
+ * links and lists (the Website CMS; docs/WEBSITE-CMS.md).
5
6
  * Static-safe, fallback-first, signature-verified.
6
7
  * See docs/SITE-KIT-3.0-SPEC-SLOTS.md.
7
8
  */
8
9
  export { ManagedSlot, default } from './ManagedSlot';
9
10
  export type { ManagedSlotProps } from './ManagedSlot';
11
+ export { ManagedRichText } from './ManagedRichText';
12
+ export type { ManagedRichTextProps } from './ManagedRichText';
13
+ export { ManagedLink } from './ManagedLink';
14
+ export type { ManagedLinkProps } from './ManagedLink';
15
+ export { ManagedList } from './ManagedList';
16
+ export type { ManagedListProps } from './ManagedList';
17
+ export { renderSlotRichText } from './rich';
10
18
  export { getSlot, resolveSlots } from './server-api';
11
19
  export type { SlotResolveOptions } from './server-api';
12
20
  export { createSlotsRevalidateHandler, SLOTS_CACHE_TAG } from './revalidate';
13
- export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, slotSigningPayload, signSlotContent, verifySlotContent, } from './contract';
14
- export type { SlotContent, SlotContentType, SlotsResolveRequest, SlotsResolveResponse, } from './contract';
21
+ export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, isSafeSlotHref, isValidSlotId, normalizeSlotValue, parseSlotRichText, slotSigningPayload, signSlotContent, verifySlotContent, } from './contract';
22
+ export type { SlotContent, SlotContentType, SlotLinkValue, SlotListImage, SlotListItem, SlotListValue, SlotRichBlock, SlotRichInline, SlotValue, SlotsResolveRequest, SlotsResolveResponse, } from './contract';
@@ -1,5 +1,7 @@
1
- export { ManagedSlot, SLOTS_CACHE_TAG, createSlotsRevalidateHandler, ManagedSlot_default as default, getSlot, resolveSlots } from '../chunk-KPAZG65P.js';
2
- export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-SS636UDN.js';
1
+ export { ManagedLink, ManagedList, ManagedRichText, ManagedSlot, SLOTS_CACHE_TAG, createSlotsRevalidateHandler, ManagedSlot_default as default, getSlot, renderSlotRichText, resolveSlots } from '../chunk-CGWUXUYZ.js';
2
+ export { SLOTS_CONTRACT_VERSION, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-VCJYLYJV.js';
3
+ export { SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, isSafeSlotHref, isValidSlotId, normalizeSlotValue, parseSlotRichText } from '../chunk-LPH5FANE.js';
4
+ import '../chunk-RYVDGXC2.js';
3
5
  import '../chunk-X6F6SO4S.js';
4
6
  import '../chunk-MH2DSGC6.js';
5
7
  import '../chunk-PKBMQBKP.js';
@@ -1,20 +1,25 @@
1
1
  /**
2
- * @sonordev/site-kit/slots — On-demand Revalidation
2
+ * @sonordev/site-kit/website/slots — On-demand Revalidation
3
3
  *
4
4
  * Sonor calls this when slot content changes (owner edit or approved Signal
5
5
  * proposal) so the change is live in seconds instead of at the next ISR
6
6
  * window. Mirrors the llms revalidation handler pattern.
7
7
  *
8
+ * Sonor sends slot edits to the site's one revalidation route
9
+ * (`@sonordev/site-kit/revalidate`), which expires this tag too. Use this
10
+ * handler only for a separate slots-only route.
11
+ *
8
12
  * @example
9
13
  * ```ts
10
14
  * // app/api/revalidate-slots/route.ts
11
- * import { createSlotsRevalidateHandler } from '@sonordev/site-kit/slots'
15
+ * import { createSlotsRevalidateHandler } from '@sonordev/site-kit/website/slots'
12
16
  *
13
17
  * export const POST = createSlotsRevalidateHandler(process.env.REVALIDATION_SECRET!)
14
18
  * ```
15
19
  */
16
20
  type RevalidateTagFn = (tag: string) => void;
17
- export declare const SLOTS_CACHE_TAG = "sonor-slots";
21
+ /** The tag every slot fetch carries (SITE_CACHE_TAGS.slots, @sonordev/contracts/site-cache). */
22
+ export declare const SLOTS_CACHE_TAG: "sonor-slots";
18
23
  /**
19
24
  * Create a POST handler that busts the slots data cache.
20
25
  * Protected by a shared secret. (POST handlers are never statically
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Render the rich-text subset (see parseSlotRichText in the slots contract)
4
+ * as React elements. There's no HTML path: every node maps to one element,
5
+ * and text is React text, so managed rich content can't inject markup.
6
+ */
7
+ export declare function renderSlotRichText(markdown: string): React.ReactNode;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @sonordev/site-kit/slots — Server-Only Resolution
2
+ * @sonordev/site-kit/website/slots — Server-Only Resolution
3
3
  *
4
4
  * SECURITY: uses the private SONOR_API_KEY. Import only in server code
5
5
  * (RSC, route handlers, server actions).
@@ -12,7 +12,7 @@
12
12
  * - Signed: unsigned/tampered payloads are dropped with a warning.
13
13
  */
14
14
  import 'server-only';
15
- import { type SlotContent } from './contract';
15
+ import { type SlotContent, type SlotContentType, type SlotValue } from './contract';
16
16
  export interface SlotResolveOptions {
17
17
  /** Multi-site sub-dimension: normalized lowercase host. */
18
18
  site?: string;
@@ -29,6 +29,10 @@ export interface SlotResolveOptions {
29
29
  * the Copy Evidence Loop's slot↔page mapping.
30
30
  */
31
31
  page?: string;
32
+ /** What this slot renders as (default 'text'). The component sets it. */
33
+ type?: SlotContentType;
34
+ /** The structured fallback (link or list), reported so the editor starts from it. */
35
+ fallbackValue?: SlotValue;
32
36
  }
33
37
  /** Resolve one slot. Null on any failure → caller renders fallback. */
34
38
  export declare function getSlot(id: string, options?: SlotResolveOptions): Promise<SlotContent | null>;
@@ -1,6 +1,6 @@
1
- export { BookingWidget } from '../chunk-GYY6ETGB.js';
2
- export { createBooking, createSlotHold, detectTimezone, fetchAvailability, fetchAvailableDates, fetchBookingTypeDetails, fetchBookingTypes, formatDate, formatDuration, formatTime, prepareMeeting, releaseSlotHold } from '../chunk-5FBY2ZIH.js';
3
- import '../chunk-RU2RMTGT.js';
4
- import '../chunk-OIETJKIL.js';
5
- import '../chunk-B6RZ2NRH.js';
1
+ export { BookingWidget } from '../chunk-ZAWANWUS.js';
2
+ export { createBooking, createSlotHold, detectTimezone, fetchAvailability, fetchAvailableDates, fetchBookingTypeDetails, fetchBookingTypes, formatDate, formatDuration, formatTime, prepareMeeting, releaseSlotHold } from '../chunk-EFPS56JA.js';
3
+ import '../chunk-PXZ7LAOY.js';
4
+ import '../chunk-OUNRFXDC.js';
5
+ import '../chunk-6OL23QYD.js';
6
6
  import '../chunk-PKBMQBKP.js';
@@ -1,6 +1,6 @@
1
- export { ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage } from '../chunk-O52CH273.js';
1
+ export { ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage } from '../chunk-542MIHNP.js';
2
2
  export { ManagedFavicon, getManagedFaviconUrl } from '../chunk-GW7GSBO7.js';
3
- import '../chunk-RU2RMTGT.js';
4
- import '../chunk-OIETJKIL.js';
5
- import '../chunk-B6RZ2NRH.js';
3
+ import '../chunk-PXZ7LAOY.js';
4
+ import '../chunk-OUNRFXDC.js';
5
+ import '../chunk-6OL23QYD.js';
6
6
  import '../chunk-PKBMQBKP.js';
@@ -1,13 +1,13 @@
1
1
  'use client';
2
- export { SiteDesignReporter } from '../chunk-ZRE4ZYEG.js';
3
- export { SITE_DESIGN_PATH, cleanFontStack, colorToHex, getSiteDesignTokens, measureSiteDesign, readDeclaredDesign, sendSiteDesign } from '../chunk-DKTSGYLM.js';
2
+ export { SiteDesignReporter } from '../chunk-ICFRH2ED.js';
3
+ export { SITE_DESIGN_PATH, cleanFontStack, colorToHex, getSiteDesignTokens, measureSiteDesign, readDeclaredDesign, sendSiteDesign } from '../chunk-ODADQM6Z.js';
4
4
  export { DEFAULT_POPUP_TOKENS, PopupBlocks } from '../chunk-JSZN6LDZ.js';
5
5
  import '../chunk-IHG36STL.js';
6
6
  import '../chunk-YC7ELZS3.js';
7
7
  import '../chunk-S22FSH7C.js';
8
8
  import '../chunk-43OCZ3JA.js';
9
9
  import '../chunk-EKBEOXTH.js';
10
- import '../chunk-RU2RMTGT.js';
11
- import '../chunk-OIETJKIL.js';
12
- import '../chunk-B6RZ2NRH.js';
10
+ import '../chunk-PXZ7LAOY.js';
11
+ import '../chunk-OUNRFXDC.js';
12
+ import '../chunk-6OL23QYD.js';
13
13
  import '../chunk-PKBMQBKP.js';
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  export { DEFAULT_POPUP_TOKENS, PopupBlocks } from '../chunk-JSZN6LDZ.js';
3
3
  import '../chunk-IHG36STL.js';
4
- import { EngageWidget } from '../chunk-T4SY3FMN.js';
4
+ import { EngageWidget } from '../chunk-B73QPZPH.js';
5
5
  import '../chunk-NDF4A5JM.js';
6
6
  import '../chunk-6T6CQVNL.js';
7
7
  import '../chunk-S22FSH7C.js';
@@ -9,9 +9,9 @@ import '../chunk-24QZEO3Q.js';
9
9
  import '../chunk-GJWI74ZZ.js';
10
10
  import '../chunk-43OCZ3JA.js';
11
11
  import '../chunk-EKBEOXTH.js';
12
- import '../chunk-RU2RMTGT.js';
13
- import '../chunk-OIETJKIL.js';
14
- import '../chunk-B6RZ2NRH.js';
12
+ import '../chunk-PXZ7LAOY.js';
13
+ import '../chunk-OUNRFXDC.js';
14
+ import '../chunk-6OL23QYD.js';
15
15
  import '../chunk-PKBMQBKP.js';
16
16
  import { jsx } from 'react/jsx-runtime';
17
17
 
@@ -1,2 +1,3 @@
1
- export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from '../../chunk-SS636UDN.js';
1
+ export { SLOTS_CONTRACT_VERSION, SUPPORTED_SLOTS_CONTRACT_VERSIONS, canonicalJson, signSlotContent, slotSigningPayload, verifySlotContent } from '../../chunk-VCJYLYJV.js';
2
+ export { SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_FIELD_MAX_LENGTH, SLOT_HREF_MAX_LENGTH, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, SLOT_V1_CONTENT_TYPES, isSafeSlotHref, isSafeSlotImageSrc, isSlotContentType, isValidSlotId, normalizeSlotValue, parseSlotRichText, slotListPlainText } from '../../chunk-LPH5FANE.js';
2
3
  import '../../chunk-PKBMQBKP.js';
@@ -1,5 +1,7 @@
1
- export { ManagedSlot, SLOTS_CACHE_TAG, createSlotsRevalidateHandler, ManagedSlot_default as default, getSlot, resolveSlots } from '../chunk-KPAZG65P.js';
2
- export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-SS636UDN.js';
1
+ export { ManagedLink, ManagedList, ManagedRichText, ManagedSlot, SLOTS_CACHE_TAG, createSlotsRevalidateHandler, ManagedSlot_default as default, getSlot, renderSlotRichText, resolveSlots } from '../chunk-CGWUXUYZ.js';
2
+ export { SLOTS_CONTRACT_VERSION, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-VCJYLYJV.js';
3
+ export { SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, isSafeSlotHref, isValidSlotId, normalizeSlotValue, parseSlotRichText } from '../chunk-LPH5FANE.js';
4
+ import '../chunk-RYVDGXC2.js';
3
5
  import '../chunk-X6F6SO4S.js';
4
6
  import '../chunk-MH2DSGC6.js';
5
7
  import '../chunk-PKBMQBKP.js';
@@ -1,4 +1,4 @@
1
- export { writeLLMsTxtToPublic } from './chunk-SSUQKA7L.js';
1
+ export { writeLLMsTxtToPublic } from './chunk-J4D6ZXRW.js';
2
2
  import './chunk-GLHQ3LRO.js';
3
3
  import './chunk-L5UOHS6E.js';
4
4
  import './chunk-WMVK77ND.js';
package/docs.json CHANGED
@@ -16,6 +16,7 @@
16
16
  "title": "Core",
17
17
  "pages": [
18
18
  { "slug": "layout", "title": "Layout", "file": "src/layout/README.md" },
19
+ { "slug": "live-updates", "title": "Live updates", "file": "src/revalidate/README.md" },
19
20
  { "slug": "seo", "title": "SEO", "file": "src/seo/README.md" },
20
21
  { "slug": "analytics", "title": "Analytics", "file": "src/analytics/README.md" },
21
22
  { "slug": "sitemap", "title": "Sitemap", "file": "src/sitemap/README.md" },
@@ -27,6 +28,7 @@
27
28
  {
28
29
  "title": "Content",
29
30
  "pages": [
31
+ { "slug": "copy", "title": "Managed copy", "file": "src/slots/README.md" },
30
32
  { "slug": "articles", "title": "Articles", "file": "src/articles/README.md" },
31
33
  { "slug": "images", "title": "Images", "file": "src/images/README.md" },
32
34
  { "slug": "reputation", "title": "Reputation", "file": "src/reputation/README.md" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonordev/site-kit",
3
- "version": "7.0.2",
3
+ "version": "7.1.1",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@11.5.3",
6
6
  "description": "Complete client-side integration kit for Sonor - SEO, Analytics, Engage, Forms, Blog",
@@ -155,6 +155,10 @@
155
155
  "types": "./dist/slots/contract.d.ts",
156
156
  "default": "./dist/slots/contract.js"
157
157
  },
158
+ "./revalidate": {
159
+ "types": "./dist/revalidate/index.d.ts",
160
+ "default": "./dist/revalidate/index.js"
161
+ },
158
162
  "./sites/contract": {
159
163
  "types": "./dist/sites/contract.d.ts",
160
164
  "default": "./dist/sites/contract.js"
@@ -455,6 +459,7 @@
455
459
  "commander": "^15.0.0",
456
460
  "glob": "^13.0.6",
457
461
  "gsap": "^3.15.0",
462
+ "happy-dom": "^20.14.5",
458
463
  "inquirer": "^14.0.2",
459
464
  "next": "16.3.6",
460
465
  "open": "11.0.0",
@@ -0,0 +1,82 @@
1
+ # Live updates
2
+
3
+ Your pages stay cached, and when someone changes content in Sonor, the pages
4
+ that use it refresh within seconds. It takes one route file:
5
+
6
+ ```ts
7
+ // app/api/seo-revalidate/route.ts
8
+ export { POST } from '@sonordev/site-kit/revalidate'
9
+ ```
10
+
11
+ `npx sonor-setup scaffold` writes it for you, and `npx sonor-setup doctor`
12
+ tells you when it's missing.
13
+
14
+ ## How it works
15
+
16
+ Everything site-kit fetches from Sonor is cached, and pages built from those
17
+ fetches are served from your host's CDN. When content changes in Sonor
18
+ (managed copy, a page's title or description, an article, a portfolio item),
19
+ Sonor sends this route the paths and cache tags that changed. The route checks
20
+ the call was signed with your project's API key, then expires only those
21
+ pages and tags. The next visitor gets the new version, and every other page
22
+ stays cached.
23
+
24
+ It's the same `SONOR_API_KEY` the rest of site-kit reads, so there's no
25
+ extra secret to set. Sonor finds the route on its own: the first sitemap sync
26
+ after a deploy registers `https://your-domain/api/seo-revalidate` and checks
27
+ that it answers.
28
+
29
+ Without the route, nothing breaks. Edits still show up, but only once each
30
+ cached fetch ages out: about five minutes for managed copy, up to a day for
31
+ metadata.
32
+
33
+ ## Sites with a publication
34
+
35
+ If your site publishes articles, tell the route where they live so the index
36
+ and feeds refresh with each article:
37
+
38
+ ```ts
39
+ // app/api/seo-revalidate/route.ts
40
+ import { createRevalidateRoute } from '@sonordev/site-kit/revalidate'
41
+
42
+ export const POST = createRevalidateRoute({ publicationBasePath: '/insights' })
43
+ ```
44
+
45
+ | Option | What it does |
46
+ |---|---|
47
+ | `publicationBasePath` | Where your articles live, e.g. `/insights`. The index and its RSS and Atom feeds refresh on every call. |
48
+ | `extraPaths` | Local paths to refresh on every call, e.g. a hub page like `/work`. |
49
+ | `extendPayload` | Map extra fields Sonor sends to more paths or tags. The result is validated again, so it can't widen what a caller may refresh. |
50
+ | `secret` | The key calls are signed with. Defaults to `SONOR_API_KEY`, read on every call. |
51
+
52
+ ## What Sonor sends
53
+
54
+ A POST with `Authorization: Bearer <SONOR_API_KEY>` and a JSON body:
55
+
56
+ ```json
57
+ { "paths": ["/services/roofing"], "tags": ["sonor-slots"] }
58
+ ```
59
+
60
+ | Field | Meaning |
61
+ |---|---|
62
+ | `paths` | Local paths to regenerate. `/sitemap.xml`, `/llms.txt` and `/llms-full.txt` are always refreshed too. |
63
+ | `tags` | Cache tags to expire. The ones Sonor uses are `SITE_CACHE_TAGS`: `sonor-slots` (managed copy), `seo` (metadata, schema, FAQs), `blog` (articles), `editorial-taxonomy` and `portfolio`. |
64
+ | `revalidateAll` | Regenerate every page. |
65
+ | `ping` | `{ "ping": true }` on its own regenerates nothing and answers `{ "ok": true, "ping": true, "version": "…" }`. Sonor uses it to confirm the route is installed. |
66
+
67
+ The route refuses a bad key (401), a body over 16 KB (413) and any path that
68
+ isn't a plain local path or any malformed tag (400). A refused call refreshes
69
+ nothing.
70
+
71
+ ## Checking it
72
+
73
+ ```bash
74
+ curl -s -X POST https://your-domain/api/seo-revalidate \
75
+ -H "Authorization: Bearer $SONOR_API_KEY" \
76
+ -H "Content-Type: application/json" \
77
+ -d '{"ping":true}'
78
+ ```
79
+
80
+ `{"ok":true,"ping":true,...}` means Sonor's edits will go live in seconds. A
81
+ 404 means the route isn't deployed. A 401 means the key on the site doesn't
82
+ match the project's key.
package/src/seo/README.md CHANGED
@@ -219,26 +219,13 @@ Renders the internal links Sonor has for `path` at that position, or nothing whe
219
219
 
220
220
  `limit` defaults to 5. `renderLink(link)` replaces the default `<a>`, and `className` replaces the default `sk-internal-links sk-internal-links--{position}`. `site` pins the sub-site host (see [Multi-site projects](#multi-site-projects)).
221
221
 
222
- ## Content blocks: `<ManagedContent>`
222
+ ## Content blocks: `<ManagedContent>` (deprecated)
223
223
 
224
- ```tsx
225
- import { ManagedContent } from '@sonordev/site-kit/seo'
226
-
227
- <ManagedContent path="/services/plumbing" section="hero" fallback={<DefaultHero />} />
228
- ```
229
-
230
- Renders the block Sonor has for that path and section, by its content type:
231
-
232
- - `html`: injected as-is
233
- - `markdown`: a basic conversion (headings, bold, italic, links, paragraphs). For anything richer, fetch the raw block with `getManagedContentData(path, section)` and use your own parser.
234
- - `json`: renders `title`, `subtitle`, `content` (as HTML) and `items`
235
- - `react`: `{ component, props }`, looked up by name in the `components` prop
236
-
237
- It renders `fallback` (or nothing) when there's no block. `injectEntityAnnotations` depends on the entity graph and changes nothing today. `site` pins the sub-site host, and `getManagedContentData(path, section, site?)` takes it too.
224
+ Deprecated in 7.1 and removed in 8.0. Page copy is [managed copy](../slots/README.md) now: wrap the text in `<ManagedSlot>` or `<ManagedRichText>` and edit it in Sonor under Website → Content, with drafts, history and Edit on page. Sonor no longer creates content blocks, so `ManagedContent` renders its `fallback`.
238
225
 
239
226
  ## Multi-site projects
240
227
 
241
- One Sonor project can serve many domains (example.com plus its city microsites). Managed FAQs, internal links and content blocks can be project-wide (every host) or tagged with one host (that host only). `ManagedFAQ`, `ManagedInternalLinks` and `ManagedContent` send the site host with every read, so each microsite gets its own rows plus the project-wide ones, never a sibling's.
228
+ One Sonor project can serve many domains (example.com plus its city microsites). Managed FAQs and internal links can be project-wide (every host) or tagged with one host (that host only). `ManagedFAQ` and `ManagedInternalLinks` send the site host with every read, so each microsite gets its own rows plus the project-wide ones, never a sibling's.
242
229
 
243
230
  The host resolves from `NEXT_PUBLIC_SITE_URL`, which every microsite already sets, so most sites change nothing. To pin one, pass `site`:
244
231
 
@@ -311,7 +298,7 @@ Every component above is built on these. They're server-only, take paths rather
311
298
 
312
299
  ## Entity graph and AI visibility
313
300
 
314
- `getEntities`, `getPrimaryEntity`, `getEntityEnhancedSchema`, `getVisibilityScore` and `getVisibilitySummary` are exported, but called from a site they currently return empty results (`[]` or `null`): the Signal endpoints behind them don't accept a site key yet. That's also why `ManagedSchema`'s `includeEntityGraph` and `ManagedContent`'s `injectEntityAnnotations` have no effect. Don't build on them until that changes.
301
+ `getEntities`, `getPrimaryEntity`, `getEntityEnhancedSchema`, `getVisibilityScore` and `getVisibilitySummary` are exported, but called from a site they currently return empty results (`[]` or `null`): the Signal endpoints behind them don't accept a site key yet. That's also why `ManagedSchema`'s `includeEntityGraph` has no effect. Don't build on them until that changes.
315
302
 
316
303
  ## Caching
317
304