@sonordev/site-kit 2.5.0 → 2.5.2
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/README.md +184 -301
- package/dist/EngageWidget-VCMSEZIF.mjs +4 -0
- package/dist/{EngageWidget-GI5YY4GM.mjs.map → EngageWidget-VCMSEZIF.mjs.map} +1 -1
- package/dist/EngageWidget-XUYC27CV.js +13 -0
- package/dist/{EngageWidget-IJGEP44J.js.map → EngageWidget-XUYC27CV.js.map} +1 -1
- package/dist/blog/index.d.mts +4 -32
- package/dist/blog/index.d.ts +4 -32
- package/dist/blog/index.js +13 -189
- package/dist/blog/index.js.map +1 -1
- package/dist/blog/index.mjs +4 -183
- package/dist/blog/index.mjs.map +1 -1
- package/dist/blog/server-ui.d.mts +32 -2
- package/dist/blog/server-ui.d.ts +32 -2
- package/dist/blog/server-ui.js +12 -3
- package/dist/blog/server-ui.mjs +2 -1
- package/dist/{chunk-K4AUQZG5.js → chunk-7EPMDUE6.js} +2 -2
- package/dist/{chunk-K4AUQZG5.js.map → chunk-7EPMDUE6.js.map} +1 -1
- package/dist/{chunk-T5UU7I4V.mjs → chunk-EHNJDXBY.mjs} +266 -59
- package/dist/chunk-EHNJDXBY.mjs.map +1 -0
- package/dist/{chunk-GVDPTXN3.js → chunk-G63AH3C6.js} +3 -3
- package/dist/{chunk-GVDPTXN3.js.map → chunk-G63AH3C6.js.map} +1 -1
- package/dist/{chunk-SROW253N.js → chunk-GQU4LXOP.js} +2 -2
- package/dist/chunk-GQU4LXOP.js.map +1 -0
- package/dist/{chunk-QJIEREW4.mjs → chunk-LBII2GAF.mjs} +2 -2
- package/dist/chunk-LBII2GAF.mjs.map +1 -0
- package/dist/{chunk-F54HGPDM.js → chunk-REJSE5AU.js} +266 -58
- package/dist/chunk-REJSE5AU.js.map +1 -0
- package/dist/{chunk-DV2BURIN.mjs → chunk-S7OMNQTY.mjs} +3 -3
- package/dist/{chunk-DV2BURIN.mjs.map → chunk-S7OMNQTY.mjs.map} +1 -1
- package/dist/{chunk-EHKM5Y7Z.mjs → chunk-YX23HISG.mjs} +2 -2
- package/dist/{chunk-EHKM5Y7Z.mjs.map → chunk-YX23HISG.mjs.map} +1 -1
- package/dist/engage/index.js +4 -4
- package/dist/engage/index.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/index.mjs +1 -1
- package/dist/layout/client.js +2 -2
- package/dist/layout/client.mjs +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/index.mjs +1 -1
- package/dist/signal/index.d.mts +6 -2
- package/dist/signal/index.d.ts +6 -2
- package/dist/signal/index.js +4 -4
- package/dist/signal/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/EngageWidget-GI5YY4GM.mjs +0 -4
- package/dist/EngageWidget-IJGEP44J.js +0 -13
- package/dist/chunk-F54HGPDM.js.map +0 -1
- package/dist/chunk-QJIEREW4.mjs.map +0 -1
- package/dist/chunk-SROW253N.js.map +0 -1
- package/dist/chunk-T5UU7I4V.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,378 +1,261 @@
|
|
|
1
1
|
# @sonordev/site-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All-in-one integration kit for [Sonor](https://sonor.io)-powered Next.js sites. One package, one env var, every module: SEO, Analytics, Forms, Blog, Commerce, Engage, GEO/AEO, Booking, Reputation, A/B Testing, and more — all managed from the Sonor dashboard at [app.sonor.io](https://app.sonor.io).
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @sonordev/site-kit
|
|
9
|
-
# or
|
|
10
|
-
pnpm add @sonordev/site-kit
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## Setup
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
**One environment variable:**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# .env.local
|
|
17
|
+
SONOR_API_KEY=sonor_xxxxxxxx_xxxxx
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**One layout component:**
|
|
16
21
|
|
|
17
22
|
```tsx
|
|
18
23
|
// app/layout.tsx
|
|
19
|
-
import {
|
|
24
|
+
import { SiteKitLayout } from '@sonordev/site-kit/layout'
|
|
20
25
|
|
|
21
|
-
export default function RootLayout({ children }) {
|
|
26
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
22
27
|
return (
|
|
23
|
-
<html>
|
|
28
|
+
<html lang="en">
|
|
24
29
|
<body>
|
|
25
|
-
<
|
|
26
|
-
projectId={process.env.UPTRADE_PROJECT_ID!}
|
|
27
|
-
supabaseUrl={process.env.NEXT_PUBLIC_SUPABASE_URL!}
|
|
28
|
-
supabaseAnonKey={process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!}
|
|
29
|
-
analytics={{ enabled: true }}
|
|
30
|
-
engage={{ enabled: true }}
|
|
31
|
-
>
|
|
30
|
+
<SiteKitLayout>
|
|
32
31
|
{children}
|
|
33
|
-
</
|
|
32
|
+
</SiteKitLayout>
|
|
34
33
|
</body>
|
|
35
34
|
</html>
|
|
36
35
|
)
|
|
37
36
|
}
|
|
38
37
|
```
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
`SiteKitLayout` is an RSC-compatible async Server Component that auto-composes: analytics tracking, Engage widgets, favicons, managed scripts, and sitemap sync. No client-side provider wrapping needed.
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
**Middleware (optional but recommended):**
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
import { ManagedSchema, ManagedFAQ, ManagedInternalLinks } from '@sonordev/site-kit/seo'
|
|
48
|
-
import { getManagedMetadata, generateSitemap, generateRobots } from '@sonordev/site-kit/seo/server'
|
|
49
|
-
|
|
50
|
-
// In a page component
|
|
51
|
-
export async function generateMetadata({ params }) {
|
|
52
|
-
return getManagedMetadata('your-project-id', `/blog/${params.slug}`)
|
|
53
|
-
}
|
|
43
|
+
```ts
|
|
44
|
+
// middleware.ts
|
|
45
|
+
import { createMiddleware, siteKitMatcher } from '@sonordev/site-kit/middleware'
|
|
54
46
|
|
|
55
|
-
export default
|
|
56
|
-
|
|
57
|
-
<article>
|
|
58
|
-
<h1>My Blog Post</h1>
|
|
59
|
-
<p>Content...</p>
|
|
60
|
-
|
|
61
|
-
{/* Managed structured data */}
|
|
62
|
-
<ManagedSchema projectId="..." path="/blog/my-post" />
|
|
63
|
-
|
|
64
|
-
{/* Managed FAQ section */}
|
|
65
|
-
<ManagedFAQ projectId="..." path="/blog/my-post" />
|
|
66
|
-
|
|
67
|
-
{/* Managed internal links */}
|
|
68
|
-
<ManagedInternalLinks projectId="..." path="/blog/my-post" />
|
|
69
|
-
</article>
|
|
70
|
-
)
|
|
71
|
-
}
|
|
47
|
+
export default createMiddleware()
|
|
48
|
+
export const config = siteKitMatcher
|
|
72
49
|
```
|
|
73
50
|
|
|
74
|
-
|
|
51
|
+
Handles Portal-managed redirects, security headers, and AI discovery headers.
|
|
75
52
|
|
|
76
|
-
|
|
53
|
+
**CLI (optional — scaffolds everything):**
|
|
77
54
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const { trackEvent, trackConversion } = useAnalytics()
|
|
83
|
-
|
|
84
|
-
const handleClick = () => {
|
|
85
|
-
trackEvent('button_click', { button: 'cta' })
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const handlePurchase = () => {
|
|
89
|
-
trackConversion('purchase', 99.99)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<>
|
|
94
|
-
<button onClick={handleClick}>CTA Button</button>
|
|
95
|
-
<WebVitals /> {/* Reports LCP, CLS, FID, etc. */}
|
|
96
|
-
</>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
55
|
+
```bash
|
|
56
|
+
npx sonor-setup init
|
|
57
|
+
npx sonor-setup scaffold # sitemap, robots, llms.txt, middleware, manifest
|
|
58
|
+
npx sonor-setup status # health check
|
|
99
59
|
```
|
|
100
60
|
|
|
101
|
-
|
|
61
|
+
---
|
|
102
62
|
|
|
103
|
-
|
|
63
|
+
## Modules
|
|
104
64
|
|
|
105
|
-
|
|
106
|
-
import { EngageWidget, ChatWidget } from '@sonordev/site-kit/engage'
|
|
65
|
+
Every module has its own README in `src/<module>/README.md` with full API docs, types, and examples.
|
|
107
66
|
|
|
108
|
-
|
|
109
|
-
// Or add manually:
|
|
67
|
+
### Core (every site)
|
|
110
68
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
```
|
|
69
|
+
| Module | Import | Purpose | Docs |
|
|
70
|
+
|--------|--------|---------|------|
|
|
71
|
+
| **Layout** | `@sonordev/site-kit/layout` | RSC layout that composes all features | [README](src/layout/README.md) |
|
|
72
|
+
| **SEO** | `@sonordev/site-kit/seo` | Managed metadata, schemas, FAQs, internal links | [README](src/seo/README.md) |
|
|
73
|
+
| **Analytics** | `@sonordev/site-kit/analytics` | Page views, events, conversions, Web Vitals | [README](src/analytics/README.md) |
|
|
74
|
+
| **Sitemap** | `@sonordev/site-kit/sitemap` | Auto-generated sitemap with Portal sync | [README](src/sitemap/README.md) |
|
|
75
|
+
| **Middleware** | `@sonordev/site-kit/middleware` | Redirects, security headers, AI discovery | [README](src/middleware/README.md) |
|
|
76
|
+
| **Redirects** | `@sonordev/site-kit/redirects` | Portal-managed 301/302 redirect rules | [README](src/redirects/README.md) |
|
|
121
77
|
|
|
122
|
-
###
|
|
78
|
+
### Content
|
|
123
79
|
|
|
124
|
-
|
|
80
|
+
| Module | Import | Purpose | Docs |
|
|
81
|
+
|--------|--------|---------|------|
|
|
82
|
+
| **Blog** | `@sonordev/site-kit/blog` | Portal-managed blog with SSG, topic clusters, E-E-A-T | [README](src/blog/README.md) |
|
|
83
|
+
| **Images** | `@sonordev/site-kit/images` | Managed image slots with dev-mode editing | [README](src/images/README.md) |
|
|
84
|
+
| **Reputation** | `@sonordev/site-kit/reputation` | Reviews, testimonials, rating stats | [README](src/reputation/README.md) |
|
|
125
85
|
|
|
126
|
-
|
|
127
|
-
import { ManagedForm } from '@sonordev/site-kit/forms'
|
|
86
|
+
### Engagement
|
|
128
87
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
formSlug="contact-form"
|
|
135
|
-
onSuccess={(data) => console.log('Submitted:', data)}
|
|
136
|
-
/>
|
|
137
|
-
)
|
|
138
|
-
}
|
|
88
|
+
| Module | Import | Purpose | Docs |
|
|
89
|
+
|--------|--------|---------|------|
|
|
90
|
+
| **Engage** | `@sonordev/site-kit/engage` | Popups, nudges, bars, chat widgets | [README](src/engage/README.md) |
|
|
91
|
+
| **Forms** | `@sonordev/site-kit/forms` | Managed forms with CRM routing, multi-step, validation | [README](src/forms/README.md) |
|
|
92
|
+
| **Signal** | `@sonordev/site-kit/signal` | A/B experiments, behavior tracking, real-time config | [README](src/signal/README.md) |
|
|
139
93
|
|
|
140
|
-
|
|
141
|
-
export default function CustomContactPage() {
|
|
142
|
-
return (
|
|
143
|
-
<ManagedForm
|
|
144
|
-
projectId="..."
|
|
145
|
-
formSlug="contact-form"
|
|
146
|
-
render={({ fields, step, totalSteps, values, errors, handleSubmit, isSubmitting }) => (
|
|
147
|
-
<form onSubmit={handleSubmit}>
|
|
148
|
-
{fields.map(field => (
|
|
149
|
-
<CustomField key={field.slug} {...field} />
|
|
150
|
-
))}
|
|
151
|
-
<button disabled={isSubmitting}>Submit</button>
|
|
152
|
-
</form>
|
|
153
|
-
)}
|
|
154
|
-
/>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
```
|
|
94
|
+
### Commerce
|
|
158
95
|
|
|
159
|
-
|
|
96
|
+
| Module | Import | Purpose | Docs |
|
|
97
|
+
|--------|--------|---------|------|
|
|
98
|
+
| **Commerce** | `@sonordev/site-kit/commerce` | Products, services, events, checkout | [README](src/commerce/README.md) |
|
|
99
|
+
| **Sync** | `@sonordev/site-kit/sync` | Booking/scheduling widget (Calendly-like) | [README](src/sync/README.md) |
|
|
160
100
|
|
|
161
|
-
|
|
101
|
+
### GEO / AEO (AI Visibility)
|
|
162
102
|
|
|
163
|
-
|
|
103
|
+
| Module | Import | Purpose | Docs |
|
|
104
|
+
|--------|--------|---------|------|
|
|
105
|
+
| **LLMs** | `@sonordev/site-kit/llms` | llms.txt, AEO components, Speakable schema | [README](src/llms/README.md) |
|
|
106
|
+
| **LLMs Contract** | `@sonordev/site-kit/llms/contract` | Shared types/sanitizers for API consumers | [README](src/llms/LLM_GEO_CONTRACT.md) |
|
|
164
107
|
|
|
165
|
-
|
|
166
|
-
// app/blog/page.tsx - Blog Index
|
|
167
|
-
import { BlogList, BlogLayout } from '@sonordev/site-kit/blog'
|
|
168
|
-
import { generateBlogIndexMetadata } from '@sonordev/site-kit/blog/server'
|
|
108
|
+
---
|
|
169
109
|
|
|
170
|
-
|
|
171
|
-
title: 'Blog',
|
|
172
|
-
siteName: 'My Company',
|
|
173
|
-
siteUrl: 'https://example.com',
|
|
174
|
-
})
|
|
110
|
+
## How It Works
|
|
175
111
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
112
|
+
```
|
|
113
|
+
SONOR_API_KEY in .env.local
|
|
114
|
+
│
|
|
115
|
+
▼
|
|
116
|
+
SiteKitLayout (RSC server component)
|
|
117
|
+
├── Server-side:
|
|
118
|
+
│ ├── ManagedFavicon (Portal logo → <link> tags)
|
|
119
|
+
│ ├── ManagedScripts (tracking pixels, analytics tags)
|
|
120
|
+
│ └── API preconnect hints
|
|
121
|
+
│
|
|
122
|
+
├── Client-side (lazy-loaded island):
|
|
123
|
+
│ ├── AnalyticsProvider (page views, scroll depth, Web Vitals)
|
|
124
|
+
│ ├── EngageWidget (popups, nudges, chat)
|
|
125
|
+
│ ├── SignalBridge (A/B experiments, behavior tracking)
|
|
126
|
+
│ ├── SitemapSync (keeps Portal seo_pages in sync)
|
|
127
|
+
│ └── IdentityProvider (unified visitor/session IDs)
|
|
128
|
+
│
|
|
129
|
+
└── Middleware (separate):
|
|
130
|
+
├── Redirects (Portal-managed 301/302)
|
|
131
|
+
├── Security headers (CSP, X-Frame-Options, etc.)
|
|
132
|
+
└── AI discovery (Link: rel="describedby" → /llms.txt)
|
|
191
133
|
```
|
|
192
134
|
|
|
193
|
-
|
|
194
|
-
// app/blog/[slug]/page.tsx - Individual Post
|
|
195
|
-
import { BlogPost } from '@sonordev/site-kit/blog'
|
|
196
|
-
import {
|
|
197
|
-
generateBlogPostMetadata,
|
|
198
|
-
generateBlogStaticParams,
|
|
199
|
-
getBlogPost,
|
|
200
|
-
generateBlogPostSchema
|
|
201
|
-
} from '@sonordev/site-kit/blog/server'
|
|
202
|
-
|
|
203
|
-
// Pre-generate all post pages at build time
|
|
204
|
-
export const generateStaticParams = generateBlogStaticParams
|
|
205
|
-
|
|
206
|
-
// Dynamic metadata for SEO
|
|
207
|
-
export async function generateMetadata({ params }) {
|
|
208
|
-
return generateBlogPostMetadata(params.slug, {
|
|
209
|
-
siteName: 'My Company',
|
|
210
|
-
siteUrl: 'https://example.com',
|
|
211
|
-
})
|
|
212
|
-
}
|
|
135
|
+
All data flows through the Portal API (`api.sonor.io`) authenticated by your API key. No direct database access, no Supabase keys exposed to the client.
|
|
213
136
|
|
|
214
|
-
|
|
215
|
-
const post = await getBlogPost(params.slug)
|
|
216
|
-
|
|
217
|
-
return (
|
|
218
|
-
<>
|
|
219
|
-
{/* JSON-LD Schema */}
|
|
220
|
-
<script
|
|
221
|
-
type="application/ld+json"
|
|
222
|
-
dangerouslySetInnerHTML={{
|
|
223
|
-
__html: JSON.stringify(generateBlogPostSchema(post, {
|
|
224
|
-
siteUrl: 'https://example.com',
|
|
225
|
-
siteName: 'My Company',
|
|
226
|
-
})),
|
|
227
|
-
}}
|
|
228
|
-
/>
|
|
229
|
-
|
|
230
|
-
{/* Blog Post with TOC and Related Posts */}
|
|
231
|
-
<BlogPost
|
|
232
|
-
slug={params.slug}
|
|
233
|
-
showToc
|
|
234
|
-
showRelated
|
|
235
|
-
showAuthor
|
|
236
|
-
/>
|
|
237
|
-
</>
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
```
|
|
137
|
+
---
|
|
241
138
|
|
|
242
|
-
|
|
139
|
+
## Import Paths
|
|
243
140
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
| `BlogLayout` | Complete layout with sidebar |
|
|
249
|
-
| `BlogSidebar` | Categories, recent posts, tags, search |
|
|
250
|
-
| `TableOfContents` | Sticky TOC from headings |
|
|
251
|
-
| `AuthorCard` | Author info with social links |
|
|
252
|
-
| `RelatedPosts` | Related posts by category |
|
|
141
|
+
```ts
|
|
142
|
+
// Core
|
|
143
|
+
import { SiteKitLayout } from '@sonordev/site-kit/layout'
|
|
144
|
+
import { createMiddleware, siteKitMatcher } from '@sonordev/site-kit/middleware'
|
|
253
145
|
|
|
254
|
-
|
|
146
|
+
// SEO
|
|
147
|
+
import { getManagedMetadata, ManagedSchema, ManagedFAQ } from '@sonordev/site-kit/seo'
|
|
255
148
|
|
|
256
|
-
|
|
257
|
-
import {
|
|
258
|
-
getBlogPost, // Fetch single post
|
|
259
|
-
getAllBlogSlugs, // For generateStaticParams
|
|
260
|
-
getBlogCategories, // All categories
|
|
261
|
-
generateBlogPostMetadata, // Post page metadata
|
|
262
|
-
generateBlogIndexMetadata, // Index page metadata
|
|
263
|
-
generateBlogStaticParams, // SSG params
|
|
264
|
-
generateBlogSitemap, // Sitemap entries
|
|
265
|
-
generateBlogPostSchema, // JSON-LD
|
|
266
|
-
} from '@sonordev/site-kit/blog/server'
|
|
267
|
-
```
|
|
149
|
+
// Analytics
|
|
150
|
+
import { AnalyticsProvider, useAnalytics, WebVitals } from '@sonordev/site-kit/analytics'
|
|
268
151
|
|
|
269
|
-
|
|
152
|
+
// Forms
|
|
153
|
+
import { ManagedForm, useForm, formsApi, field } from '@sonordev/site-kit/forms'
|
|
270
154
|
|
|
271
|
-
|
|
155
|
+
// Blog
|
|
156
|
+
import { BlogPost, BlogList, ClusterLandingPage } from '@sonordev/site-kit/blog'
|
|
157
|
+
import { getBlogPost, generateBlogStaticParams } from '@sonordev/site-kit/blog/server'
|
|
272
158
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Required
|
|
276
|
-
projectId="your-project-id"
|
|
277
|
-
supabaseUrl="https://xxx.supabase.co"
|
|
278
|
-
supabaseAnonKey="your-anon-key"
|
|
279
|
-
|
|
280
|
-
// Analytics options
|
|
281
|
-
analytics={{
|
|
282
|
-
enabled: true,
|
|
283
|
-
trackPageViews: true, // Auto-track route changes
|
|
284
|
-
trackWebVitals: true, // Report Core Web Vitals
|
|
285
|
-
trackScrollDepth: false, // Track scroll milestones
|
|
286
|
-
sessionDuration: 30, // Session timeout in minutes
|
|
287
|
-
excludePaths: ['/admin'] // Don't track these paths
|
|
288
|
-
}}
|
|
289
|
-
|
|
290
|
-
// Engage widget options
|
|
291
|
-
engage={{
|
|
292
|
-
enabled: true,
|
|
293
|
-
position: 'bottom-right', // Widget position
|
|
294
|
-
zIndex: 9999,
|
|
295
|
-
chatEnabled: true // Enable AI/live chat
|
|
296
|
-
}}
|
|
297
|
-
|
|
298
|
-
// Forms options
|
|
299
|
-
forms={{
|
|
300
|
-
enabled: true,
|
|
301
|
-
honeypotField: '_hp' // Spam protection field name
|
|
302
|
-
}}
|
|
303
|
-
|
|
304
|
-
// Debug mode - logs to console
|
|
305
|
-
debug={false}
|
|
306
|
-
>
|
|
307
|
-
```
|
|
159
|
+
// Commerce
|
|
160
|
+
import { OfferingCard, ProductPage, EventCalendar } from '@sonordev/site-kit/commerce'
|
|
308
161
|
|
|
309
|
-
|
|
162
|
+
// Booking
|
|
163
|
+
import { BookingWidget } from '@sonordev/site-kit/sync'
|
|
310
164
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
SONOR_API_KEY=your_api_key_here
|
|
165
|
+
// Engage
|
|
166
|
+
import { EngageWidget, ChatWidget } from '@sonordev/site-kit/engage'
|
|
314
167
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
# NEXT_PUBLIC_UPTRADE_API_KEY=your_api_key_here
|
|
318
|
-
```
|
|
168
|
+
// Signal (A/B)
|
|
169
|
+
import { SignalBridge, SignalExperiment, useSignal } from '@sonordev/site-kit/signal'
|
|
319
170
|
|
|
320
|
-
|
|
171
|
+
// GEO / AEO
|
|
172
|
+
import { createLLMsTxtHandler, buildAiDiscoveryHeaders } from '@sonordev/site-kit/llms'
|
|
173
|
+
import { AEOBlock, AEOSummary, AEOSteps, SpeakableSchema } from '@sonordev/site-kit/llms'
|
|
321
174
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
import { generateSitemap } from '@sonordev/site-kit/seo/server'
|
|
175
|
+
// Sitemap
|
|
176
|
+
import { createSitemap } from '@sonordev/site-kit/sitemap'
|
|
325
177
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
178
|
+
// Images
|
|
179
|
+
import { ManagedImage } from '@sonordev/site-kit/images'
|
|
329
180
|
|
|
330
|
-
//
|
|
331
|
-
import {
|
|
181
|
+
// Reputation
|
|
182
|
+
import { TestimonialSection, fetchReviews } from '@sonordev/site-kit/reputation'
|
|
332
183
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
184
|
+
// Redirects
|
|
185
|
+
import { handleManagedRedirects } from '@sonordev/site-kit/redirects'
|
|
336
186
|
|
|
337
|
-
//
|
|
338
|
-
import {
|
|
187
|
+
// Robots
|
|
188
|
+
import { createRobots } from '@sonordev/site-kit/robots'
|
|
339
189
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
190
|
+
// Styles (optional)
|
|
191
|
+
import '@sonordev/site-kit/brand.css'
|
|
192
|
+
import '@sonordev/site-kit/forms/styles.css'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Environment Variables
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Required (server-only — SiteKitLayout injects into client automatically):
|
|
201
|
+
SONOR_API_KEY=sonor_xxxxxxxx_xxxxx
|
|
202
|
+
|
|
203
|
+
# Optional:
|
|
204
|
+
SONOR_API_URL=https://api.sonor.io # Portal API (default)
|
|
205
|
+
NEXT_PUBLIC_SITE_URL=https://example.com # For CLI status checks + sitemap
|
|
206
|
+
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=6Le... # reCAPTCHA Enterprise (forms)
|
|
207
|
+
REVALIDATION_SECRET=your_secret # On-demand ISR for llms.txt
|
|
346
208
|
```
|
|
347
209
|
|
|
348
|
-
|
|
210
|
+
Legacy env vars (`UPTRADE_API_KEY`, `NEXT_PUBLIC_UPTRADE_API_KEY`) are still supported for older sites.
|
|
349
211
|
|
|
350
|
-
|
|
212
|
+
---
|
|
351
213
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
214
|
+
## CLI
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
npx sonor-setup <command>
|
|
218
|
+
|
|
219
|
+
Commands:
|
|
220
|
+
init Initialize site-kit in a Next.js project
|
|
221
|
+
scaffold Scaffold sitemap, robots, llms.txt, middleware, manifest
|
|
222
|
+
setup AI-powered SEO setup (metadata, schemas, FAQs)
|
|
223
|
+
scan Scan codebase for integration opportunities
|
|
224
|
+
migrate Migrate detected components to site-kit
|
|
225
|
+
sync Sync local content to Portal
|
|
226
|
+
status Health check (API, llms.txt, sitemap, layout)
|
|
227
|
+
geo Check GEO/llms.txt wiring
|
|
228
|
+
images Scan, upload, and manage images
|
|
229
|
+
locations Generate location pages
|
|
230
|
+
faqs Sync ManagedFAQ paths
|
|
231
|
+
api-routes Generate API proxy routes
|
|
232
|
+
install Install @sonordev/site-kit
|
|
233
|
+
upgrade Upgrade to latest version
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
360
237
|
|
|
361
238
|
## TypeScript
|
|
362
239
|
|
|
363
|
-
All
|
|
240
|
+
Fully typed. All types are exported from their respective module paths:
|
|
364
241
|
|
|
365
|
-
```
|
|
366
|
-
import type {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
ManagedFormConfig,
|
|
372
|
-
BlogPostType
|
|
373
|
-
} from '@sonordev/site-kit'
|
|
242
|
+
```ts
|
|
243
|
+
import type { LLMsDataResponse, GenerateLLMSTxtOptions } from '@sonordev/site-kit/llms'
|
|
244
|
+
import type { ManagedFormConfig, UseFormReturn } from '@sonordev/site-kit/forms'
|
|
245
|
+
import type { BlogPost, TopicCluster } from '@sonordev/site-kit/blog'
|
|
246
|
+
import type { CommerceOffering, SizeChart } from '@sonordev/site-kit/commerce'
|
|
247
|
+
import type { SiteKitLayoutProps } from '@sonordev/site-kit/layout'
|
|
374
248
|
```
|
|
375
249
|
|
|
376
|
-
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Architecture Note
|
|
253
|
+
|
|
254
|
+
`@sonordev/site-kit` is the client bridge between Next.js marketing sites and the Sonor platform. All persistent data (forms, blog posts, SEO config, analytics) lives in the Portal API — site-kit fetches, renders, and tracks.
|
|
255
|
+
|
|
256
|
+
- **Server components** (SEO, Blog, Images): Import directly, RSC-compatible, no provider needed
|
|
257
|
+
- **Client modules** (Analytics, Engage, Signal): Lazy-loaded via `SiteKitLayout`, tree-shaken
|
|
258
|
+
- **Build-time** (Sitemap, llms.txt): Run during `next build`, sync to Portal
|
|
259
|
+
- **Middleware** (Redirects, Security, AI Discovery): Runs on every request edge
|
|
377
260
|
|
|
378
|
-
|
|
261
|
+
For the full platform architecture, see the root `CLAUDE.md`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-VCMSEZIF.mjs"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk7EPMDUE6_js = require('./chunk-7EPMDUE6.js');
|
|
4
|
+
require('./chunk-ZSMWDLMK.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "EngageWidget", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunk7EPMDUE6_js.EngageWidget; }
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=EngageWidget-XUYC27CV.js.map
|
|
13
|
+
//# sourceMappingURL=EngageWidget-XUYC27CV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"EngageWidget-XUYC27CV.js"}
|
package/dist/blog/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { BlogList, BlogListServerProps, BlogListStyles, BlogPost, BlogPostServerProps, BlogPostStyles } from './server-ui.mjs';
|
|
1
|
+
export { BlogList, BlogListServerProps, BlogListStyles, BlogPost, BlogPostServerProps, BlogPostStyles, ClusterLandingPage, ClusterLandingPageProps, ClusterNavigation, ClusterNavigationProps } from './server-ui.mjs';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import React__default from 'react';
|
|
4
|
-
import { A as AuthorCardProps, c as AuthorPageProps, B as BlogPost, d as TableOfContentsProps
|
|
5
|
-
export { m as BlogAnalytics, a as BlogAuthor, b as BlogCategory, f as BlogListOptions, j as BlogListProps, k as BlogListRenderProps, g as BlogListResult, h as BlogPostProps, i as BlogPostRenderProps, e as BlogTag, R as RelatedPostsProps, l as TocItem } from '../types-5P5B9RgV.mjs';
|
|
4
|
+
import { A as AuthorCardProps, c as AuthorPageProps, B as BlogPost, d as TableOfContentsProps } from '../types-5P5B9RgV.mjs';
|
|
5
|
+
export { m as BlogAnalytics, a as BlogAuthor, b as BlogCategory, f as BlogListOptions, j as BlogListProps, k as BlogListRenderProps, g as BlogListResult, h as BlogPostProps, i as BlogPostRenderProps, e as BlogTag, R as RelatedPostsProps, l as TocItem, T as TopicCluster } from '../types-5P5B9RgV.mjs';
|
|
6
6
|
|
|
7
7
|
interface BlogSidebarProps {
|
|
8
8
|
/** Portal API URL */
|
|
@@ -206,34 +206,6 @@ interface BlogFAQProps {
|
|
|
206
206
|
}
|
|
207
207
|
declare function BlogFAQ({ items, title, allowMultiple, styles, unstyled, }: BlogFAQProps): react_jsx_runtime.JSX.Element | null;
|
|
208
208
|
|
|
209
|
-
interface ClusterNavigationProps {
|
|
210
|
-
/** Cluster navigation data (from getClusterNavigation) */
|
|
211
|
-
navigation: ClusterNavigation$1;
|
|
212
|
-
/** Base path for blog URLs (default: /blog) */
|
|
213
|
-
basePath?: string;
|
|
214
|
-
/** Disable default styles */
|
|
215
|
-
unstyled?: boolean;
|
|
216
|
-
/** Custom class name */
|
|
217
|
-
className?: string;
|
|
218
|
-
}
|
|
219
|
-
declare function ClusterNavigation({ navigation, basePath, unstyled, className, }: ClusterNavigationProps): react_jsx_runtime.JSX.Element | null;
|
|
220
|
-
|
|
221
|
-
interface ClusterLandingPageProps {
|
|
222
|
-
/** Cluster slug to fetch */
|
|
223
|
-
slug: string;
|
|
224
|
-
/** Base path for blog URLs (default: /blog) */
|
|
225
|
-
basePath?: string;
|
|
226
|
-
/** Disable default styles */
|
|
227
|
-
unstyled?: boolean;
|
|
228
|
-
/** Custom class name */
|
|
229
|
-
className?: string;
|
|
230
|
-
/** Render prop for full data access */
|
|
231
|
-
children?: (props: {
|
|
232
|
-
cluster: TopicCluster;
|
|
233
|
-
}) => React__default.ReactNode;
|
|
234
|
-
}
|
|
235
|
-
declare function ClusterLandingPage({ slug, basePath, unstyled, className, children, }: ClusterLandingPageProps): Promise<string | number | bigint | boolean | Iterable<React__default.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined>;
|
|
236
|
-
|
|
237
209
|
/**
|
|
238
210
|
* Post-process Portal/blog HTML for safe, consistent link behavior.
|
|
239
211
|
* Server-safe string transforms only (no DOM).
|
|
@@ -252,4 +224,4 @@ declare function resolveBlogSiteUrl(explicit?: string): string;
|
|
|
252
224
|
*/
|
|
253
225
|
declare function addExternalLinkTargets(html: string, siteUrl: string): string;
|
|
254
226
|
|
|
255
|
-
export { AuthorCard, AuthorCardProps, AuthorPage, AuthorPageProps, BlogFAQ, type BlogFAQProps, BlogLayout, type BlogLayoutProps, BlogPage, type BlogPageProps, BlogPostPage, type BlogPostPageProps, BlogSidebar, type BlogSidebarProps, CategoryPage, type CategoryPageProps,
|
|
227
|
+
export { AuthorCard, AuthorCardProps, AuthorPage, AuthorPageProps, BlogFAQ, type BlogFAQProps, BlogLayout, type BlogLayoutProps, BlogPage, type BlogPageProps, BlogPostPage, type BlogPostPageProps, BlogSidebar, type BlogSidebarProps, CategoryPage, type CategoryPageProps, type FaqItem, NewsletterWidget, type NewsletterWidgetProps, RelatedPosts, ServiceCallout, type ServiceCalloutProps, ServiceCallouts, TableOfContents, TableOfContentsProps, addExternalLinkTargets, normalizeSiteHost, resolveBlogSiteUrl };
|