@sonordev/site-kit 2.5.0 → 2.5.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 (2) hide show
  1. package/README.md +184 -301
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,378 +1,261 @@
1
1
  # @sonordev/site-kit
2
2
 
3
- Complete client-side integration kit for Sonor. One package for SEO, Analytics, Engage widgets, Forms, and Blog - all managed from your Sonor dashboard.
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
- ## Installation
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
- ## Quick Start
11
+ ## Setup
14
12
 
15
- Wrap your app with `SiteKitProvider` in your root layout:
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 { SiteKitProvider } from '@sonordev/site-kit'
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
- <SiteKitProvider
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
- </SiteKitProvider>
32
+ </SiteKitLayout>
34
33
  </body>
35
34
  </html>
36
35
  )
37
36
  }
38
37
  ```
39
38
 
40
- ## Modules
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
- ### SEO (`@sonordev/site-kit/seo`)
41
+ **Middleware (optional but recommended):**
43
42
 
44
- Managed SEO components that automatically inject structured data, FAQs, internal links, and more based on your Portal configuration.
45
-
46
- ```tsx
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 async function BlogPost({ params }) {
56
- return (
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
- ### Analytics (`@sonordev/site-kit/analytics`)
51
+ Handles Portal-managed redirects, security headers, and AI discovery headers.
75
52
 
76
- Automatic page view tracking, custom events, and Core Web Vitals reporting.
53
+ **CLI (optional scaffolds everything):**
77
54
 
78
- ```tsx
79
- import { useAnalytics, WebVitals } from '@sonordev/site-kit/analytics'
80
-
81
- export default function MyComponent() {
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
- ### Engage (`@sonordev/site-kit/engage`)
61
+ ---
102
62
 
103
- Popups, nudges, banners, and chat widgets configured from Portal.
63
+ ## Modules
104
64
 
105
- ```tsx
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
- // Automatically included when engage.enabled = true in SiteKitProvider
109
- // Or add manually:
67
+ ### Core (every site)
110
68
 
111
- export default function Layout({ children }) {
112
- return (
113
- <>
114
- {children}
115
- <EngageWidget projectId="..." />
116
- <ChatWidget projectId="..." />
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
- ### Forms (`@sonordev/site-kit/forms`)
78
+ ### Content
123
79
 
124
- Managed forms with automatic routing to CRM, Support, or other destinations.
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
- ```tsx
127
- import { ManagedForm } from '@sonordev/site-kit/forms'
86
+ ### Engagement
128
87
 
129
- // Basic usage - fetches form config from Portal
130
- export default function ContactPage() {
131
- return (
132
- <ManagedForm
133
- projectId="..."
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
- // Custom rendering
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
- ### Blog (`@sonordev/site-kit/blog`)
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
- Complete Portal-managed blog system with beautiful layouts, dynamic routing, categories, and SEO.
101
+ ### GEO / AEO (AI Visibility)
162
102
 
163
- **Create posts in Portal Automatically appear on your site.**
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
- ```tsx
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
- export const metadata = generateBlogIndexMetadata({
171
- title: 'Blog',
172
- siteName: 'My Company',
173
- siteUrl: 'https://example.com',
174
- })
110
+ ## How It Works
175
111
 
176
- export default function BlogPage({ searchParams }) {
177
- return (
178
- <BlogLayout
179
- hero={{ title: 'Blog', subtitle: 'Latest insights and articles' }}
180
- layout="sidebar-right"
181
- >
182
- <BlogList
183
- category={searchParams.category}
184
- page={parseInt(searchParams.page || '1')}
185
- showCategoryFilter
186
- showPagination
187
- />
188
- </BlogLayout>
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
- ```tsx
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
- export default async function PostPage({ params }) {
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
- #### Available Components
139
+ ## Import Paths
243
140
 
244
- | Component | Purpose |
245
- |-----------|---------|
246
- | `BlogList` | Grid of posts with pagination & category filter |
247
- | `BlogPost` | Full post with TOC, author, related posts |
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
- #### Server Functions
146
+ // SEO
147
+ import { getManagedMetadata, ManagedSchema, ManagedFAQ } from '@sonordev/site-kit/seo'
255
148
 
256
- ```tsx
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
- ## Configuration
152
+ // Forms
153
+ import { ManagedForm, useForm, formsApi, field } from '@sonordev/site-kit/forms'
270
154
 
271
- ### Full Provider Options
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
- ```tsx
274
- <SiteKitProvider
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
- ## Environment Variables
162
+ // Booking
163
+ import { BookingWidget } from '@sonordev/site-kit/sync'
310
164
 
311
- ```bash
312
- # Required
313
- SONOR_API_KEY=your_api_key_here
165
+ // Engage
166
+ import { EngageWidget, ChatWidget } from '@sonordev/site-kit/engage'
314
167
 
315
- # Optional (legacy names still supported)
316
- # UPTRADE_API_KEY=your_api_key_here
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
- ## Server-Side Utilities
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
- ```tsx
323
- // app/sitemap.ts
324
- import { generateSitemap } from '@sonordev/site-kit/seo/server'
175
+ // Sitemap
176
+ import { createSitemap } from '@sonordev/site-kit/sitemap'
325
177
 
326
- export default async function sitemap() {
327
- return generateSitemap('your-project-id', 'https://yoursite.com')
328
- }
178
+ // Images
179
+ import { ManagedImage } from '@sonordev/site-kit/images'
329
180
 
330
- // app/robots.ts
331
- import { generateRobots } from '@sonordev/site-kit/seo/server'
181
+ // Reputation
182
+ import { TestimonialSection, fetchReviews } from '@sonordev/site-kit/reputation'
332
183
 
333
- export default async function robots() {
334
- return generateRobots('your-project-id', 'https://yoursite.com')
335
- }
184
+ // Redirects
185
+ import { handleManagedRedirects } from '@sonordev/site-kit/redirects'
336
186
 
337
- // Redirect handling in middleware
338
- import { handleRedirect } from '@sonordev/site-kit/seo/server'
187
+ // Robots
188
+ import { createRobots } from '@sonordev/site-kit/robots'
339
189
 
340
- export async function middleware(request) {
341
- const redirect = await handleRedirect('your-project-id', request.nextUrl.pathname)
342
- if (redirect) {
343
- return NextResponse.redirect(new URL(redirect.to, request.url), redirect.statusCode)
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
- ## Form Routing
210
+ Legacy env vars (`UPTRADE_API_KEY`, `NEXT_PUBLIC_UPTRADE_API_KEY`) are still supported for older sites.
349
211
 
350
- Forms automatically route submissions based on their type:
212
+ ---
351
213
 
352
- | Form Type | Routes To | Use Case |
353
- |-----------|-----------|----------|
354
- | `prospect` | CRM Leads | Sales inquiries, quote requests |
355
- | `support` | Support Tickets | Help requests, bug reports |
356
- | `feedback` | Feedback Entries | User feedback, suggestions |
357
- | `newsletter` | Email Subscribers | Newsletter signups |
358
- | `contact` | Form Submissions | General contact (no routing) |
359
- | `custom` | Form Submissions | Custom handling |
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 modules are fully typed:
240
+ Fully typed. All types are exported from their respective module paths:
364
241
 
365
- ```tsx
366
- import type {
367
- SiteKitConfig,
368
- ManagedMetadata,
369
- AnalyticsEvent,
370
- EngageElement,
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
- ## License
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
- MIT
261
+ For the full platform architecture, see the root `CLAUDE.md`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonordev/site-kit",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Complete client-side integration kit for Sonor - SEO, Analytics, Engage, Forms, Blog",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",