@sonordev/site-kit 7.0.2 → 7.1.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.
- package/CHANGELOG.md +67 -0
- package/agent-manifest.json +11 -5
- package/dist/{AnalyticsProvider-XXWTFKJH.js → AnalyticsProvider-ZMQUV33M.js} +4 -4
- package/dist/{ArticleViewTracker-V4KZB6QN.js → ArticleViewTracker-V7NUXYBY.js} +3 -3
- package/dist/{BlocksPopup-JHGHB6XW.js → BlocksPopup-52EU7OUY.js} +4 -4
- package/dist/{ChatWidget-CG32POI3.js → ChatWidget-5BHMNR57.js} +5 -5
- package/dist/{EngageWidget-LQMR4LEX.js → EngageWidget-PCGLX7SO.js} +4 -4
- package/dist/{FileField-KUG3CKXG.js → FileField-TSFGNAMY.js} +3 -3
- package/dist/{FormSpotlight-FVNPOCU3.js → FormSpotlight-XLBWEOTE.js} +1 -1
- package/dist/{FormStage-C7VKRURJ.js → FormStage-IJQ5Q2X6.js} +1 -1
- package/dist/{ManagedForm-VLNJKV65.js → ManagedForm-Z3PKOPIZ.js} +6 -6
- package/dist/{ManagedNewsletterForm-KJEU23BV.js → ManagedNewsletterForm-QAO3POLC.js} +4 -4
- package/dist/{SignalCore-K2O46QG7.js → SignalCore-RBA3VDBL.js} +3 -3
- package/dist/{SiteDesignReporter-D7MD66GI.js → SiteDesignReporter-C4LR5X2V.js} +5 -5
- package/dist/SitemapSync-XVMGKCF3.js +8 -0
- package/dist/_client/booking-widget.js +5 -5
- package/dist/affiliates/index.js +3 -3
- package/dist/analytics/index.js +4 -4
- package/dist/articles/index.js +1 -1
- package/dist/articles/server-ui.js +1 -1
- package/dist/chat/index.js +5 -5
- package/dist/{chunk-GYY6ETGB.js → chunk-3G2SE2J4.js} +1 -1
- package/dist/{chunk-GGD4P7UW.js → chunk-3J2ERO3I.js} +1 -1
- package/dist/{chunk-KXPBMCFL.js → chunk-3QI26673.js} +3 -1
- package/dist/{chunk-7JIKGKWD.js → chunk-3XPJKZ6D.js} +30 -7
- package/dist/{chunk-B6RZ2NRH.js → chunk-4JQQDCMO.js} +1 -1
- package/dist/{chunk-6HDT4G4A.js → chunk-4YTYGG2C.js} +2 -2
- package/dist/{chunk-N2UVOR3X.js → chunk-662ILEZ6.js} +2 -0
- package/dist/chunk-6G43IRWR.js +4 -0
- package/dist/{chunk-LVESVYCE.js → chunk-7MHHWZKC.js} +11 -117
- package/dist/{chunk-5FBY2ZIH.js → chunk-7QTMMHUO.js} +1 -1
- package/dist/{chunk-KPAZG65P.js → chunk-CGWUXUYZ.js} +138 -46
- package/dist/{chunk-LJZ3SUET.js → chunk-DUAO4Q75.js} +2 -2
- package/dist/{chunk-P72ZJRSX.js → chunk-EIULXXUJ.js} +3 -3
- package/dist/{chunk-OIETJKIL.js → chunk-EVFZ7KEW.js} +1 -1
- package/dist/{chunk-BEL7YFMC.js → chunk-F42R35NV.js} +1 -1
- package/dist/{chunk-42OXY4JV.js → chunk-FLR3EMK6.js} +1 -1
- package/dist/{chunk-ZRE4ZYEG.js → chunk-GIAOPEN6.js} +1 -1
- package/dist/{chunk-O52CH273.js → chunk-GWUKQ26F.js} +1 -1
- package/dist/{chunk-TT63HHIT.js → chunk-HAG4YIZY.js} +1 -1
- package/dist/{chunk-SAUTJMK6.js → chunk-HVH37YPX.js} +1 -1
- package/dist/{chunk-SSUQKA7L.js → chunk-J4D6ZXRW.js} +1 -1
- package/dist/{chunk-56JNI463.js → chunk-L2DJD5Y4.js} +1 -1
- package/dist/{chunk-BS7FWUOY.js → chunk-LFXVE32I.js} +1 -1
- package/dist/chunk-LPH5FANE.js +169 -0
- package/dist/{chunk-P2GIIQH5.js → chunk-PLUP2KN5.js} +1 -1
- package/dist/chunk-RYVDGXC2.js +19 -0
- package/dist/{chunk-RU2RMTGT.js → chunk-U35H2JIQ.js} +2 -2
- package/dist/chunk-VCJYLYJV.js +49 -0
- package/dist/{chunk-DKTSGYLM.js → chunk-W2CL2DB3.js} +2 -2
- package/dist/{chunk-ZSLRAMCK.js → chunk-XD3ZQET6.js} +1 -1
- package/dist/{chunk-K5WZX776.js → chunk-XNVSCQ2O.js} +2 -2
- package/dist/{chunk-SWP36NCB.js → chunk-YLSEB32F.js} +1 -1
- package/dist/chunk-ZETJTCMV.js +118 -0
- package/dist/{chunk-T4SY3FMN.js → chunk-ZIMFQWGJ.js} +3 -3
- package/dist/client/index.js +3 -3
- package/dist/cms/CmsPage.d.ts +1 -0
- package/dist/cms/CmsPreview.d.ts +1 -0
- package/dist/cms/CmsSection.d.ts +1 -0
- package/dist/cms/index.d.ts +6 -0
- package/dist/cms/server-api.d.ts +3 -0
- package/dist/commerce/index.js +4 -4
- package/dist/config/index.js +1 -1
- package/dist/contracts/entries.d.ts +1 -1
- package/dist/contracts/site-cache.d.ts +55 -0
- package/dist/contracts/site-edit-param.d.ts +7 -0
- package/dist/contracts/site-edit.d.ts +77 -0
- package/dist/contracts/slot-content.d.ts +111 -0
- package/dist/contracts/slots.d.ts +39 -25
- package/dist/engage/index.js +6 -6
- package/dist/fleet/index.js +4 -4
- package/dist/forms/index.js +8 -8
- package/dist/forms/server.js +2 -2
- package/dist/images/index.js +4 -4
- package/dist/index.js +1 -1
- package/dist/layout/client.js +8 -7
- package/dist/layout/index.js +9 -8
- package/dist/llms/index.js +4 -2
- package/dist/llms/seo-revalidate.d.ts +8 -1
- package/dist/maps/index.js +3 -3
- package/dist/mcp/sonor.js +6 -6
- package/dist/overlay-RXV6U6QC.js +353 -0
- package/dist/proxy/index.js +2 -2
- package/dist/proxy/securityHeaders.d.ts +4 -0
- package/dist/revalidate/index.d.ts +44 -0
- package/dist/revalidate/index.js +27 -0
- package/dist/seo/ManagedContent.d.ts +2 -0
- package/dist/seo/client.js +4 -4
- package/dist/seo/index.js +9 -8
- package/dist/seo/llms.js +4 -2
- package/dist/seo/register-sitemap-cli.js +1 -1
- package/dist/seo/server.js +3 -2
- package/dist/seo/sitemap.js +2 -2
- package/dist/server/index.js +2 -2
- package/dist/{server-api-GJJQZVG7.js → server-api-BVCBLJKL.js} +2 -1
- package/dist/shared/build-entries.d.ts +1 -0
- package/dist/shared/edit-bridge.d.ts +8 -0
- package/dist/shared/version.d.ts +1 -1
- package/dist/signal/index.js +2 -2
- package/dist/sitemap/index.js +2 -2
- package/dist/slots/ManagedLink.d.ts +31 -0
- package/dist/slots/ManagedList.d.ts +30 -0
- package/dist/slots/ManagedRichText.d.ts +31 -0
- package/dist/slots/contract.js +2 -1
- package/dist/slots/edit/locate.d.ts +30 -0
- package/dist/slots/edit/overlay.d.ts +18 -0
- package/dist/slots/index.d.ts +12 -4
- package/dist/slots/index.js +4 -2
- package/dist/slots/revalidate.d.ts +8 -3
- package/dist/slots/rich.d.ts +7 -0
- package/dist/slots/server-api.d.ts +6 -2
- package/dist/sync/index.js +5 -5
- package/dist/website/images.js +4 -4
- package/dist/website/index.js +5 -5
- package/dist/website/popups.js +4 -4
- package/dist/website/slots/contract.js +2 -1
- package/dist/website/slots.js +4 -2
- package/dist/{writeLLMsTxt-UMHKGNRR.js → writeLLMsTxt-QR23OQUE.js} +1 -1
- package/docs.json +2 -0
- package/package.json +6 -1
- package/src/revalidate/README.md +82 -0
- package/src/seo/README.md +4 -17
- package/src/slots/README.md +168 -0
- package/dist/SitemapSync-NMXGMPCQ.js +0 -8
- package/dist/chunk-SS636UDN.js +0 -35
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Managed copy — `@sonordev/site-kit/website/slots`
|
|
2
|
+
|
|
3
|
+
Let the owner edit a site's words in Sonor without a deploy, while you keep
|
|
4
|
+
the markup. Wrap the copy you'd like managed; the text in your code is the
|
|
5
|
+
fallback, so the page renders exactly as written until someone edits it, and
|
|
6
|
+
whenever Sonor can't be reached.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { ManagedSlot, ManagedRichText, ManagedLink, ManagedList } from '@sonordev/site-kit/website/slots'
|
|
10
|
+
|
|
11
|
+
<h1 className="hero-title">
|
|
12
|
+
<ManagedSlot id="home-hero-headline" page="/">
|
|
13
|
+
New homes in Cincinnati and Northern Kentucky
|
|
14
|
+
</ManagedSlot>
|
|
15
|
+
</h1>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Every managed region shows up in Sonor under **Website → Content** the first
|
|
19
|
+
time a page renders it. There's no schema to define.
|
|
20
|
+
|
|
21
|
+
## Four kinds of copy
|
|
22
|
+
|
|
23
|
+
| Component | For | The owner edits |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `<ManagedSlot>` | a headline, tagline, label | plain text |
|
|
26
|
+
| `<ManagedRichText>` | an intro, an about section | paragraphs, bold, italic, links, lists |
|
|
27
|
+
| `<ManagedLink>` | a call to action | its label and where it goes |
|
|
28
|
+
| `<ManagedList>` | services, team, FAQs, a gallery | items: title, body, link, image |
|
|
29
|
+
|
|
30
|
+
### `ManagedRichText`
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<div className="prose">
|
|
34
|
+
<ManagedRichText id="about-intro" page="/about">
|
|
35
|
+
{`We've built homes here since **1998**.\n\n- Custom builds\n- Remodels`}
|
|
36
|
+
</ManagedRichText>
|
|
37
|
+
</div>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A string child is read as the same rich-text subset the owner edits, so the
|
|
41
|
+
fallback and the managed copy render alike. It renders `<p>`, `<ul>`, `<ol>`,
|
|
42
|
+
`<strong>`, `<em>`, `<a>` and `<br>`, and nothing else: managed copy is
|
|
43
|
+
parsed into elements, never inserted as HTML.
|
|
44
|
+
|
|
45
|
+
The subset: blank-line paragraphs, single newlines as line breaks,
|
|
46
|
+
`**bold**`, `*italic*`, `[a link](https://…)`, and `-` or `1.` lists.
|
|
47
|
+
|
|
48
|
+
### `ManagedLink`
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<ManagedLink id="home-hero-cta" href="/contact" label="Get a free quote">
|
|
52
|
+
{({ href, label }) => <Link href={href} className="btn">{label}</Link>}
|
|
53
|
+
</ManagedLink>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Without a render function it draws a plain `<a>` with your `className`.
|
|
57
|
+
Managed links may point to `https:`, `http:`, `mailto:`, `tel:`, a `#anchor`
|
|
58
|
+
or a `/path`.
|
|
59
|
+
|
|
60
|
+
### `ManagedList`
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<ManagedList id="home-services" items={SERVICES}>
|
|
64
|
+
{(items) => (
|
|
65
|
+
<ul className="grid">
|
|
66
|
+
{items.map((s) => (
|
|
67
|
+
<li key={s.title}>
|
|
68
|
+
<a href={s.href}>{s.title}</a>
|
|
69
|
+
<p>{s.body}</p>
|
|
70
|
+
</li>
|
|
71
|
+
))}
|
|
72
|
+
</ul>
|
|
73
|
+
)}
|
|
74
|
+
</ManagedList>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Each item may have a `title`, a plain-text `body`, an `href` and an
|
|
78
|
+
`image: { src, alt, width?, height? }` (https only). Up to 50 items.
|
|
79
|
+
|
|
80
|
+
## Props every component takes
|
|
81
|
+
|
|
82
|
+
| Prop | |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `id` | stable, lowercase, page-scoped by convention: `home-hero-headline` |
|
|
85
|
+
| `page` | the route it renders on (`"/pricing"`), a literal; Sonor ties the copy to that page's analytics |
|
|
86
|
+
| `site` | the host, on a multi-site project (defaults to `NEXT_PUBLIC_SITE_URL`) |
|
|
87
|
+
| `revalidate` | seconds before a cached value is checked again (default 300); with [Live updates](../revalidate/README.md) installed, edits don't wait for it |
|
|
88
|
+
|
|
89
|
+
## How it stays fast and safe
|
|
90
|
+
|
|
91
|
+
- **Static.** Copy resolves in the server render with a cached fetch, so a
|
|
92
|
+
page using it stays prerendered. Nothing ships to the browser.
|
|
93
|
+
- **Fallback first.** No key, no network, a bad response, or content of a
|
|
94
|
+
different kind than the code now renders: the fallback shows. It never
|
|
95
|
+
breaks a build or a page.
|
|
96
|
+
- **Signed.** Sonor signs every value with your project key and site-kit
|
|
97
|
+
refuses anything that doesn't verify.
|
|
98
|
+
- **Live in seconds.** Publishing in Sonor calls the site's revalidation
|
|
99
|
+
route, so the change shows without waiting for the next check. It's one
|
|
100
|
+
file, shared with the rest of site-kit (see [Live updates](../revalidate/README.md)):
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// app/api/seo-revalidate/route.ts
|
|
104
|
+
export { POST } from '@sonordev/site-kit/revalidate'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Without it, a published edit shows once the cached copy ages out (the
|
|
108
|
+
`revalidate` window, five minutes by default).
|
|
109
|
+
|
|
110
|
+
## Edit on page
|
|
111
|
+
|
|
112
|
+
In Sonor, Website → Content → **Edit on page** opens the live site with its
|
|
113
|
+
managed copy outlined. Click any of it to edit it in a side panel. Text, rich
|
|
114
|
+
text and links show the change on the page as you type, before you publish.
|
|
115
|
+
Links on the page take you to its other pages, still in edit mode.
|
|
116
|
+
|
|
117
|
+
The site needs nothing beyond site-kit 7.1: no login, no secret, no extra
|
|
118
|
+
route. It works like this:
|
|
119
|
+
|
|
120
|
+
- Sonor frames the page with `?sonor_edit=1`. For any other visitor the
|
|
121
|
+
page is unchanged; the overlay is a separate chunk that only loads when
|
|
122
|
+
Sonor frames the page.
|
|
123
|
+
- The overlay finds the copy by its visible text, so your markup and CSS are
|
|
124
|
+
exactly what you wrote (no wrapper elements, no attributes).
|
|
125
|
+
- The page only reports what it found and what was clicked. The edit itself
|
|
126
|
+
is saved by Sonor with the signed-in person's own account.
|
|
127
|
+
- It needs Sonor's dashboard to be allowed to frame the site, which
|
|
128
|
+
`securityHeaders` does by default. A site that sets its own
|
|
129
|
+
`frame-ancestors` should use `DEFAULT_FRAME_ANCESTORS`.
|
|
130
|
+
|
|
131
|
+
## Lists with photos
|
|
132
|
+
|
|
133
|
+
A list item can carry an image (`{ src, alt, width, height }`). In Sonor,
|
|
134
|
+
**Upload photo** on a list item stores it with the project's files and fills
|
|
135
|
+
in its size, so the page can reserve the space. A gallery is a list:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
<ManagedList id="gallery" items={[]}>
|
|
139
|
+
{(items) => (
|
|
140
|
+
<ul className="gallery">
|
|
141
|
+
{items.map((item, i) =>
|
|
142
|
+
item.image ? (
|
|
143
|
+
<li key={item.image.src}>
|
|
144
|
+
<img src={item.image.src} alt={item.image.alt} width={item.image.width} height={item.image.height} loading={i < 3 ? 'eager' : 'lazy'} />
|
|
145
|
+
{item.title && <p>{item.title}</p>}
|
|
146
|
+
</li>
|
|
147
|
+
) : null,
|
|
148
|
+
)}
|
|
149
|
+
</ul>
|
|
150
|
+
)}
|
|
151
|
+
</ManagedList>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Drafts and history
|
|
155
|
+
|
|
156
|
+
Edits in Sonor can be saved as drafts and published when they're ready.
|
|
157
|
+
Every publish is kept, so any earlier version can be restored. Signal can
|
|
158
|
+
propose copy too; proposals wait for approval in Sonor unless the project
|
|
159
|
+
lets it publish on its own.
|
|
160
|
+
|
|
161
|
+
## Using it without a component
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { getSlot } from '@sonordev/site-kit/website/slots'
|
|
165
|
+
|
|
166
|
+
const slot = await getSlot('home-services', { type: 'list' })
|
|
167
|
+
// slot?.value → { items: [...] }, or null (render your fallback)
|
|
168
|
+
```
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export { SitemapSync } from './chunk-BS7FWUOY.js';
|
|
2
|
-
import './chunk-YVKRYQRG.js';
|
|
3
|
-
import './chunk-43OCZ3JA.js';
|
|
4
|
-
import './chunk-EKBEOXTH.js';
|
|
5
|
-
import './chunk-RU2RMTGT.js';
|
|
6
|
-
import './chunk-OIETJKIL.js';
|
|
7
|
-
import './chunk-B6RZ2NRH.js';
|
|
8
|
-
import './chunk-PKBMQBKP.js';
|
package/dist/chunk-SS636UDN.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
-
|
|
3
|
-
// src/contracts/slots.ts
|
|
4
|
-
var SLOTS_CONTRACT_VERSION = 1;
|
|
5
|
-
var SLOT_CONTENT_TYPES = ["text"];
|
|
6
|
-
var SLOT_CONTENT_MAX_LENGTH = 1e4;
|
|
7
|
-
var SLOT_ID_PATTERN = /^[a-z0-9][a-z0-9-_.:]{0,127}$/;
|
|
8
|
-
function isValidSlotId(id) {
|
|
9
|
-
return SLOT_ID_PATTERN.test(id);
|
|
10
|
-
}
|
|
11
|
-
function slotSigningPayload(content) {
|
|
12
|
-
return `sonor-slot.v${SLOTS_CONTRACT_VERSION}
|
|
13
|
-
${content.slot_id}
|
|
14
|
-
${content.type}
|
|
15
|
-
${content.content}`;
|
|
16
|
-
}
|
|
17
|
-
function signSlotContent(content, apiKey) {
|
|
18
|
-
return createHmac("sha256", apiKey).update(slotSigningPayload(content), "utf8").digest("hex");
|
|
19
|
-
}
|
|
20
|
-
function verifySlotContent(content, apiKey) {
|
|
21
|
-
if (!content || typeof content !== "object") return false;
|
|
22
|
-
const c = content;
|
|
23
|
-
if (typeof c.slot_id !== "string" || !isValidSlotId(c.slot_id) || typeof c.content !== "string" || c.content.length === 0 || c.content.length > SLOT_CONTENT_MAX_LENGTH || typeof c.signature !== "string" || !SLOT_CONTENT_TYPES.includes(c.type)) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
const expected = signSlotContent(
|
|
27
|
-
{ slot_id: c.slot_id, type: c.type, content: c.content },
|
|
28
|
-
apiKey
|
|
29
|
-
);
|
|
30
|
-
const a = Buffer.from(expected, "utf8");
|
|
31
|
-
const b = Buffer.from(c.signature, "utf8");
|
|
32
|
-
return a.length === b.length && timingSafeEqual(a, b);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent };
|