@sonordev/site-kit 7.0.1 → 7.0.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +3539 -0
  2. package/README.md +12 -13
  3. package/agent-manifest.json +1 -1
  4. package/dist/{AnalyticsProvider-EMM2TKRE.js → AnalyticsProvider-XXWTFKJH.js} +4 -4
  5. package/dist/{ArticleViewTracker-RA64BGL6.js → ArticleViewTracker-V4KZB6QN.js} +3 -3
  6. package/dist/{BlocksPopup-D25RFNOV.js → BlocksPopup-JHGHB6XW.js} +4 -4
  7. package/dist/{ChatWidget-RYI7BMJJ.js → ChatWidget-CG32POI3.js} +5 -5
  8. package/dist/{EngageWidget-UKFCN33M.js → EngageWidget-LQMR4LEX.js} +4 -4
  9. package/dist/{FileField-MUHA7LZR.js → FileField-KUG3CKXG.js} +3 -3
  10. package/dist/{FormSpotlight-TCLPWPLL.js → FormSpotlight-FVNPOCU3.js} +1 -1
  11. package/dist/{FormStage-CNYLP6I6.js → FormStage-C7VKRURJ.js} +1 -1
  12. package/dist/{ManagedForm-7ZL5SKTO.js → ManagedForm-VLNJKV65.js} +6 -6
  13. package/dist/{ManagedNewsletterForm-33B4JLX7.js → ManagedNewsletterForm-KJEU23BV.js} +4 -4
  14. package/dist/{SignalCore-L5FVDHFE.js → SignalCore-K2O46QG7.js} +3 -3
  15. package/dist/{SiteDesignReporter-4JOFL4FP.js → SiteDesignReporter-D7MD66GI.js} +5 -5
  16. package/dist/SitemapSync-NMXGMPCQ.js +8 -0
  17. package/dist/_client/booking-widget.js +5 -5
  18. package/dist/affiliates/index.js +3 -3
  19. package/dist/analytics/index.js +4 -4
  20. package/dist/articles/index.js +1 -1
  21. package/dist/articles/server-ui.js +1 -1
  22. package/dist/chat/index.js +5 -5
  23. package/dist/{chunk-QANVUXKH.js → chunk-42OXY4JV.js} +1 -1
  24. package/dist/{chunk-GYESATRY.js → chunk-56JNI463.js} +1 -1
  25. package/dist/{chunk-MV2MBTC3.js → chunk-5FBY2ZIH.js} +1 -1
  26. package/dist/{chunk-BMO3VGMR.js → chunk-7JIKGKWD.js} +7 -7
  27. package/dist/{chunk-QGHSMJKW.js → chunk-B6RZ2NRH.js} +1 -1
  28. package/dist/{chunk-OFOAHPUV.js → chunk-BEL7YFMC.js} +1 -1
  29. package/dist/{chunk-WATH55UY.js → chunk-BS7FWUOY.js} +1 -1
  30. package/dist/{chunk-FL4EPUWA.js → chunk-DKTSGYLM.js} +2 -2
  31. package/dist/{chunk-HGCK465A.js → chunk-GGD4P7UW.js} +1 -1
  32. package/dist/{chunk-FYBZ5SNP.js → chunk-GYY6ETGB.js} +1 -1
  33. package/dist/{chunk-CVTVNC2U.js → chunk-K5WZX776.js} +2 -2
  34. package/dist/{chunk-4IQ52CXL.js → chunk-LJZ3SUET.js} +2 -2
  35. package/dist/{chunk-P5J7VMQ3.js → chunk-O52CH273.js} +1 -1
  36. package/dist/{chunk-3KUUH2YP.js → chunk-OIETJKIL.js} +1 -1
  37. package/dist/{chunk-V6LSQRTH.js → chunk-P2GIIQH5.js} +1 -1
  38. package/dist/{chunk-4RMVXRBO.js → chunk-P72ZJRSX.js} +3 -3
  39. package/dist/{chunk-EGOD74PP.js → chunk-RU2RMTGT.js} +2 -2
  40. package/dist/{chunk-QZZIKMAT.js → chunk-SAUTJMK6.js} +1 -1
  41. package/dist/{chunk-T3MC4HOD.js → chunk-SWP36NCB.js} +1 -1
  42. package/dist/{chunk-5SEM2V4A.js → chunk-T4SY3FMN.js} +3 -3
  43. package/dist/{chunk-P4GRY6QP.js → chunk-ZRE4ZYEG.js} +1 -1
  44. package/dist/{chunk-UZN4ZYR2.js → chunk-ZSLRAMCK.js} +1 -1
  45. package/dist/client/index.js +3 -3
  46. package/dist/commerce/index.js +4 -4
  47. package/dist/engage/index.js +6 -6
  48. package/dist/fleet/index.js +4 -4
  49. package/dist/forms/index.js +7 -7
  50. package/dist/forms/server.js +2 -2
  51. package/dist/forms/types.d.ts +3 -1
  52. package/dist/images/index.js +4 -4
  53. package/dist/index.js +1 -1
  54. package/dist/layout/client.js +7 -7
  55. package/dist/layout/index.js +8 -8
  56. package/dist/maps/index.js +3 -3
  57. package/dist/mcp/sonor.js +6 -6
  58. package/dist/seo/client.js +4 -4
  59. package/dist/seo/index.js +4 -4
  60. package/dist/server/index.js +2 -2
  61. package/dist/shared/version.d.ts +1 -1
  62. package/dist/signal/index.js +2 -2
  63. package/dist/sync/index.js +5 -5
  64. package/dist/website/images.js +4 -4
  65. package/dist/website/index.js +5 -5
  66. package/dist/website/popups.js +4 -4
  67. package/docs/MIGRATING-TO-7.md +146 -0
  68. package/docs.json +67 -0
  69. package/package.json +9 -4
  70. package/src/admin-auth/README.md +88 -0
  71. package/src/analytics/README.md +264 -0
  72. package/src/articles/README.md +325 -0
  73. package/src/commerce/README.md +109 -0
  74. package/src/cta-bar/README.md +154 -0
  75. package/src/engage/README.md +241 -0
  76. package/src/forms/README.md +219 -0
  77. package/src/images/README.md +74 -0
  78. package/src/layout/README.md +66 -0
  79. package/src/llms/README.md +723 -0
  80. package/src/mcp/README.md +376 -0
  81. package/src/motion/README.md +372 -0
  82. package/src/og/README.md +304 -0
  83. package/src/proxy/README.md +152 -0
  84. package/src/redirects/README.md +74 -0
  85. package/src/reputation/README.md +64 -0
  86. package/src/seo/README.md +359 -0
  87. package/src/signal/README.md +115 -0
  88. package/src/sitemap/README.md +127 -0
  89. package/src/sync/README.md +115 -0
  90. package/dist/SitemapSync-7WKY4HXI.js +0 -8
@@ -0,0 +1,115 @@
1
+ # Signal — `@sonordev/site-kit/signal`
2
+
3
+ Real-time A/B experiments, behavior tracking, and dynamic configuration from Signal AI. Requires `full_signal` plan.
4
+
5
+ ## Usage
6
+
7
+ Auto-included by `SiteKitLayout` when `signal` prop is enabled:
8
+
9
+ ```tsx
10
+ <SiteKitLayout signal>{children}</SiteKitLayout>
11
+ ```
12
+
13
+ For standalone use:
14
+
15
+ ```tsx
16
+ 'use client'
17
+ import { SignalBridge } from '@sonordev/site-kit/signal'
18
+
19
+ export default function Providers({ children }) {
20
+ return <SignalBridge>{children}</SignalBridge>
21
+ }
22
+ ```
23
+
24
+ ## A/B Experiments
25
+
26
+ ### Declarative
27
+
28
+ ```tsx
29
+ import { SignalExperiment } from '@sonordev/site-kit/signal'
30
+
31
+ <SignalExperiment
32
+ experimentId="hero-cta"
33
+ variants={{
34
+ control: <Button>Get Started</Button>,
35
+ variant_a: <Button>Start Free Trial</Button>,
36
+ }}
37
+ trackImpression
38
+ fallback={<Button>Default</Button>}
39
+ />
40
+ ```
41
+
42
+ ### Hook-Based
43
+
44
+ ```tsx
45
+ import { useSignalExperiment } from '@sonordev/site-kit/signal'
46
+
47
+ function HeroCTA() {
48
+ const { variant, isControl } = useSignalExperiment('hero-cta')
49
+ return isControl ? <Button>Get Started</Button> : <Button>Start Free Trial</Button>
50
+ }
51
+ ```
52
+
53
+ ### Conversion Tracking
54
+
55
+ ```tsx
56
+ import { ExperimentConversion } from '@sonordev/site-kit/signal'
57
+
58
+ <ExperimentConversion experimentId="hero-cta" conversionType="click">
59
+ <Button>Sign Up</Button>
60
+ </ExperimentConversion>
61
+ ```
62
+
63
+ ## Hooks
64
+
65
+ ```ts
66
+ useSignal() // Full context: config, loading, trackEvent, trackOutcome
67
+ useSignalConfig() // Just the config object
68
+ useSignalEvent() // Returns trackEvent function
69
+ useSignalOutcome() // Returns trackOutcome function
70
+ useSignalExperiment(id) // Returns { assignment, variant, isControl }
71
+ ```
72
+
73
+ ## SignalBridge Props
74
+
75
+ ```ts
76
+ interface SignalBridgeProps {
77
+ enabled?: boolean // Default: true
78
+ realtime?: boolean // SSE real-time updates (default: true)
79
+ experiments?: boolean // Participate in A/B tests (default: true)
80
+ behaviorTracking?: boolean // Scroll, clicks, time-on-page (default: true)
81
+ children: React.ReactNode
82
+ }
83
+ ```
84
+
85
+ ## What It Does
86
+
87
+ 1. Fetches config from `GET /api/public/signal/config`
88
+ 2. Opens SSE stream for real-time `config_update` and `experiment_update` events
89
+ 3. Assigns experiment variants per visitor (cached)
90
+ 4. Batches behavioral events (scroll depth, click count, time-on-page) and flushes on debounce
91
+ 5. Tracks outcomes/conversions via POST
92
+
93
+ ## Key Types
94
+
95
+ ```ts
96
+ interface ExperimentConfig {
97
+ id: string; name: string;
98
+ status: 'draft' | 'running' | 'paused' | 'completed';
99
+ variants: ExperimentVariant[];
100
+ traffic_allocation: number; // 0-1
101
+ goal: string;
102
+ winner?: string;
103
+ }
104
+
105
+ interface ExperimentVariant {
106
+ key: string; name: string; weight: number; description?: string;
107
+ }
108
+
109
+ interface SignalEvent {
110
+ event_type: string; event_name: string; event_data?: object;
111
+ page_url: string; page_title: string;
112
+ engagement: { time_on_page: number; scroll_depth: number; click_count: number };
113
+ experiments: Array<{ id: string; variant: string }>;
114
+ }
115
+ ```
@@ -0,0 +1,127 @@
1
+ # Sitemap — `@sonordev/site-kit/sitemap`
2
+
3
+ Auto-generates `sitemap.xml` from your Next.js app directory structure. Discovers pages, resolves dynamic routes, syncs to Sonor, and optionally writes build-time `llms.txt`.
4
+
5
+ ## Usage
6
+
7
+ ```ts
8
+ // app/sitemap.ts
9
+ import { createSitemap } from '@sonordev/site-kit/sitemap'
10
+
11
+ export default createSitemap({
12
+ baseUrl: 'https://example.com',
13
+ })
14
+ ```
15
+
16
+ ## Config
17
+
18
+ ```ts
19
+ interface SitemapConfig {
20
+ baseUrl?: string // Resolved from Sonor API if not set
21
+ trailingSlash?: boolean // Default: next.config's trailingSlash (see below)
22
+ exclude?: string[] // Glob patterns: ['/admin/*', '/api/*']
23
+ defaultPriority?: number // Default: 0.5
24
+ defaultChangeFrequency?: 'weekly' | 'monthly' | 'yearly' | 'never'
25
+
26
+ // Dynamic route resolution
27
+ dynamicRoutes?: Record<string, string[]> // Manual: { '[slug]': ['seo', 'analytics'] }
28
+ resolveGenerateStaticParams?: boolean // Auto-import generateStaticParams (default: true)
29
+
30
+ // Priority overrides by path pattern
31
+ priorities?: Record<string, number> // { '/services/*': 0.8, '/article/*': 0.6 }
32
+
33
+ // Additional paths not in app directory
34
+ additionalPaths?: () => Promise<{ path: string; priority?: number }[]>
35
+
36
+ // Sonor sync
37
+ apiUrl?: string // Sonor API URL
38
+ apiKey?: string // Sonor API key
39
+ disableSync?: boolean // Skip Sonor sync (default: false)
40
+ awaitMetaOptimization?: boolean // Wait for Signal meta optimization
41
+
42
+ // GEO / llms.txt integration
43
+ optimizedLLMsTxt?: boolean // Write AI-optimized llms.txt at build (default: true)
44
+ optimizedLLMsFullTxt?: boolean // Also write llms-full.txt
45
+ includeLlmsTxtInSitemap?: boolean // Add /llms.txt to sitemap (default: false; leave it off)
46
+ includeLlmsFullTxtInSitemap?: boolean // Add /llms-full.txt (default: false; leave it off)
47
+
48
+ // Intelligent priority (requires Signal)
49
+ intelligentPriority?: boolean // Use visibility scores + depth heuristics
50
+
51
+ // Local data for llms.txt fallback
52
+ getLocalData?: () => Promise<LLMsDataResponse | null>
53
+ llmsPublicSummaryOnly?: boolean
54
+ }
55
+ ```
56
+
57
+ ## How Page Discovery Works
58
+
59
+ 1. Scans `app/` directory recursively for `page.tsx`/`page.jsx` files
60
+ 2. Detects dynamic segments (`[slug]`, `[...catchAll]`) and resolves them via:
61
+ - `dynamicRoutes` config (highest priority)
62
+ - Auto-import of `generateStaticParams()` from the page file (5s timeout)
63
+ 3. Fetches portfolio paths via `getPortfolioPaths()` if portfolio module is used
64
+ 4. Deduplicates and filters exclusion patterns
65
+ 5. Infers `pageType` for each page (homepage, service, article, faq, etc.)
66
+
67
+ ## Trailing Slashes
68
+
69
+ Every `<loc>` is the URL the site actually serves. With `trailingSlash: true` in
70
+ next.config, Next 308-redirects `/about` to `/about/`, so createSitemap emits
71
+ `/about/`. The exceptions follow Next's own redirects: `/` itself, file-like
72
+ paths (a `.` in the last segment, like `/llms.txt` or `/feed.xml`) and
73
+ `/.well-known/*`.
74
+
75
+ You don't need to set anything. The option defaults to the site's next.config
76
+ value, which Next inlines into the bundle. Set `trailingSlash` only if the kit
77
+ is loaded outside Next's bundler (`serverExternalPackages`).
78
+
79
+ Only the emitted URL changes. Dedupe, `exclude`, `priorities` and the Sonor
80
+ sync all use the unslashed path, which is how `seo_pages` stores it. The
81
+ build-time llms.txt links follow the same setting. `npx sonor-setup doctor`
82
+ warns (`sitemap.trailing-slash`) when a built sitemap's URLs don't match
83
+ next.config.
84
+
85
+ ## Portfolio Paths
86
+
87
+ ```ts
88
+ import { getPortfolioPaths } from '@sonordev/site-kit/sitemap'
89
+
90
+ createSitemap({
91
+ additionalPaths: () => getPortfolioPaths({ basePath: '/work', priority: 0.7 }),
92
+ })
93
+ ```
94
+
95
+ ## Sonor Sync
96
+
97
+ During `next build` only (not ISR), the sitemap entries are POSTed to Sonor via `POST /api/public/seo/register-sitemap` with `full-replace` mode. This keeps `seo_pages` in sync with the actual site structure.
98
+
99
+ ## Build-Time llms.txt
100
+
101
+ When `optimizedLLMsTxt` is true (default), the sitemap build also writes `public/llms.txt` (and optionally `public/llms-full.txt`) using `writeLLMsTxtToPublic()`. This is served as a static file by the llms route handler.
102
+
103
+ If the Sonor API is unreachable at build time and local data can't produce real content, the write is **skipped** (with a warning) rather than replaced with an "Information not available." stub — any existing `public/llms.txt` / `public/llms-full.txt` from a previous successful build keeps serving.
104
+
105
+ ### It often won't fit in the route — write it from your postbuild
106
+
107
+ Sonor generates llms.txt with an LLM call that can take a minute, and Next
108
+ gives a prerendered route 60s before it retries and fails the build. So the
109
+ in-route write gets only what's left of a 50s share of that budget: it refreshes
110
+ the file when Sonor is quick, and otherwise leaves the existing file serving
111
+ (the warning says so). It never fails the build.
112
+
113
+ For a refresh on every build, move the write to your postbuild, where nothing
114
+ imposes a ceiling and the sync it reads has already run:
115
+
116
+ ```jsonc
117
+ // package.json
118
+ "scripts": { "postbuild": "sonor-register-sitemap --write-llms" }
119
+ ```
120
+
121
+ ```ts
122
+ // app/sitemap.ts — one writer owns the file
123
+ export default createSitemap({ baseUrl, optimizedLLMsTxt: false })
124
+ ```
125
+
126
+ Add `--write-llms-full` for `public/llms-full.txt`. The flag works even when the
127
+ CLI skips its own sync because a sitemap route owns it.
@@ -0,0 +1,115 @@
1
+ # Sync (Booking) — `@sonordev/site-kit/sync`
2
+
3
+ Embeddable booking/scheduling widget — like Calendly, built into Sonor. Appointments, consultations, classes.
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ 'use client'
9
+ import { BookingWidget } from '@sonordev/site-kit/sync'
10
+
11
+ export default function BookPage() {
12
+ return (
13
+ <BookingWidget
14
+ onBookingComplete={(result) => {
15
+ console.log('Booked:', result.booking.confirmationCode)
16
+ }}
17
+ />
18
+ )
19
+ }
20
+ ```
21
+
22
+ ## Props
23
+
24
+ ```ts
25
+ interface BookingWidgetProps {
26
+ orgSlug?: string // Organization slug (for public mode)
27
+ apiKey?: string // API key (auto-resolved from env)
28
+ apiUrl?: string // Default: https://api.sonor.io
29
+ bookingTypeSlug?: string // Show specific type only
30
+ timezone?: string // Guest timezone (auto-detected)
31
+ className?: string
32
+ daysToShow?: number // Days of availability to display
33
+ onBookingComplete?: (result: BookingResult) => void
34
+ onError?: (error: Error) => void
35
+ hideTypeSelector?: boolean // Hide type selector for single-type embed
36
+
37
+ styles?: { // Custom theme
38
+ primaryColor?: string
39
+ borderRadius?: string
40
+ fontFamily?: string
41
+ backgroundColor?: string
42
+ textPrimary?: string
43
+ borderColor?: string
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## UI Flow
49
+
50
+ 1. **Type selector:** choose a booking type, unless a specific `bookingTypeSlug` is provided.
51
+ 2. **Meeting format:** when the booking type enables meeting options, choose virtual, office, or a visit to the guest's location. Travel meetings validate the address before showing availability.
52
+ 3. **Calendar and times:** choose an available time and reserve it while completing contact details.
53
+ 4. **Guest info:** name, email, phone, and notes. Travel meetings require a phone number.
54
+ 5. **Result:** a confirmed booking shows calendar links when Sonor sends the guest's invitation. When the host's Google Calendar sends it, the widget says who the invite is coming from instead (see [Calendar invitations](#calendar-invitations)). Meetings that require review show a pending request without calendar links.
55
+
56
+ Changing a meeting format restores the selected format, address, and access notes. Guests can return to the service selector when it's available. Contact details survive changes to the format or reserved time.
57
+
58
+ Expired meeting details and time reservations show a recovery action. Guests can recheck their meeting details or choose another time without re-entering their contact information. An active hold remains usable if its prepared meeting token expires, until the hold's own deadline.
59
+
60
+ Navigation is disabled while a reservation or booking request is running. Stale responses cannot reopen a previous step, and holds created after the widget is removed are released.
61
+
62
+ ## API Functions
63
+
64
+ ```ts
65
+ import {
66
+ fetchBookingTypes, fetchBookingTypeDetails,
67
+ fetchAvailability, fetchAvailableDates,
68
+ createSlotHold, releaseSlotHold, createBooking,
69
+ prepareMeeting,
70
+ detectTimezone, formatTime, formatDate, formatDuration,
71
+ } from '@sonordev/site-kit/sync'
72
+ ```
73
+
74
+ ## Types
75
+
76
+ ```ts
77
+ interface BookingType {
78
+ slug: string; name: string; description?: string;
79
+ duration_minutes: number; color?: string;
80
+ location_type: 'virtual' | 'phone' | 'in_person' | 'custom';
81
+ price_cents?: number; currency?: string; is_active: boolean;
82
+ }
83
+
84
+ interface BookingResult {
85
+ success: boolean
86
+ booking: {
87
+ id: string; confirmationCode: string; scheduledAt: string;
88
+ durationMinutes: number; hostName?: string; timezone: string;
89
+ }
90
+ cancelUrl: string; rescheduleUrl: string;
91
+ invitation?: 'google' | 'sonor' // who sends the guest's calendar invite
92
+ calendarLinks: { google: string; outlook: string; ics: string }
93
+ }
94
+ ```
95
+
96
+ ## Calendar invitations
97
+
98
+ Each booking gives the guest one calendar invitation, and `invitation` says who sends it:
99
+
100
+ - **`'google'`**: the host has Google Calendar connected, so Google invites the guest to the host's event. Sonor's emails don't attach a second invite, and the success screen says who the invite is coming from instead of showing calendar links. The Google and Outlook links build a separate event with no tie to that invitation, so a guest who clicked one would have two entries for the same meeting.
101
+ - **`'sonor'`**: there's no Google event for the booking, usually because the host hasn't connected a calendar. Sonor's confirmation email carries the invite, and the success screen shows the calendar links.
102
+
103
+ APIs older than the field don't send it; treat a missing `invitation` like `'sonor'`. `calendarLinks` is still sent for every booking so older widgets keep working. If you build your own success screen with `createBooking`, check `invitation` before you render `calendarLinks`.
104
+
105
+ ## Features
106
+
107
+ - Automatic timezone detection
108
+ - Slot hold with expiry (prevents double-booking)
109
+ - Business hours support
110
+ - Multiple hosts per booking type
111
+ - Custom meeting URL / location
112
+
113
+ ## Booking regression checks
114
+
115
+ Run `pnpm exec vitest run src/sync/booking-flow.test.ts` for expiry and server-error recovery checks. Start `pnpm exec vite --host 127.0.0.1 --port 5189`, then run `node test/meeting-flow/run.mjs` and `node test/meeting-flow/regression.mjs` for the real widget flows. The browser harness intercepts requests to a reserved test domain, so it creates no live bookings.
@@ -1,8 +0,0 @@
1
- export { SitemapSync } from './chunk-WATH55UY.js';
2
- import './chunk-YVKRYQRG.js';
3
- import './chunk-43OCZ3JA.js';
4
- import './chunk-EKBEOXTH.js';
5
- import './chunk-EGOD74PP.js';
6
- import './chunk-3KUUH2YP.js';
7
- import './chunk-QGHSMJKW.js';
8
- import './chunk-PKBMQBKP.js';