@sonordev/site-kit 7.1.2 → 7.2.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.
Files changed (167) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +51 -0
  3. package/README.md +9 -8
  4. package/agent-manifest.json +12 -4
  5. package/dist/{AnalyticsProvider-ZB33W6MC.js → AnalyticsProvider-SLFFBRPE.js} +4 -4
  6. package/dist/{ArticleViewTracker-GMHYLLLZ.js → ArticleViewTracker-TM5AMGN2.js} +3 -3
  7. package/dist/{BlocksPopup-EA6LYL25.js → BlocksPopup-YJNJIVPO.js} +5 -5
  8. package/dist/ChatWidget-XKOSH66S.js +17 -0
  9. package/dist/{FileField-QKA3JB2Q.js → FileField-CI4UL5SH.js} +3 -3
  10. package/dist/{FormSpotlight-AQTIA5TP.js → FormSpotlight-F2AAEGBN.js} +20 -6
  11. package/dist/{FormStage-FDTLTP3B.js → FormStage-BI5R5CNC.js} +27 -7
  12. package/dist/ManagedForm-G6COGHXL.js +16 -0
  13. package/dist/{ManagedNewsletterForm-J6QSAGGP.js → ManagedNewsletterForm-BO3WCGXX.js} +7 -5
  14. package/dist/{SignalCore-JZNXOLNT.js → SignalCore-ZA6WHKBR.js} +3 -3
  15. package/dist/SiteChat-YJADDTKG.js +5 -0
  16. package/dist/{SiteDesignReporter-W4EJUSTE.js → SiteDesignReporter-5YAW36AQ.js} +5 -5
  17. package/dist/SitePopups-ST5PMKZE.js +10 -0
  18. package/dist/SitemapSync-RJTEBINX.js +8 -0
  19. package/dist/_client/booking-widget.js +5 -5
  20. package/dist/affiliates/index.js +3 -3
  21. package/dist/analytics/index.js +4 -4
  22. package/dist/analytics/send-gate.d.ts +1 -1
  23. package/dist/articles/index.js +2 -2
  24. package/dist/articles/server-ui.js +3 -2
  25. package/dist/articles/server.js +2 -1
  26. package/dist/{engage → chat}/ChatWidget.d.ts +7 -6
  27. package/dist/{engage → chat}/EchoUiActions.d.ts +1 -1
  28. package/dist/chat/SiteChat.d.ts +25 -0
  29. package/dist/{engage → chat}/brand-color.d.ts +1 -1
  30. package/dist/{engage → chat}/chat-messages.d.ts +1 -1
  31. package/dist/{engage → chat}/echo-config.d.ts +1 -1
  32. package/dist/chat/index.d.ts +10 -6
  33. package/dist/chat/index.js +14 -8
  34. package/dist/{engage → chat}/launcher-placement.d.ts +1 -7
  35. package/dist/{engage → chat}/socket-loader.d.ts +2 -2
  36. package/dist/chat/types.d.ts +136 -0
  37. package/dist/chunk-27FISYK4.js +4 -0
  38. package/dist/{chunk-LUXVWITO.js → chunk-4NTBQNHA.js} +247 -78
  39. package/dist/{chunk-V3PUVBME.js → chunk-5V2V5C3Y.js} +2 -4
  40. package/dist/{chunk-Q4W23JYM.js → chunk-5YCEMV2L.js} +1 -1
  41. package/dist/{chunk-RHNWA34F.js → chunk-64DKGVEI.js} +41 -7
  42. package/dist/{chunk-GLHQ3LRO.js → chunk-6U3VLV2C.js} +1 -1
  43. package/dist/{chunk-WWSHFRDL.js → chunk-BC4Y3S3D.js} +1 -1
  44. package/dist/{chunk-JSZN6LDZ.js → chunk-BF7TZYC3.js} +38 -31
  45. package/dist/{chunk-M3YEPKP7.js → chunk-BKY2ZNM7.js} +1 -1
  46. package/dist/{chunk-AQSNPZY4.js → chunk-C2FZBUSS.js} +80 -1
  47. package/dist/{chunk-NK4CSJNP.js → chunk-CF5QNFYU.js} +2 -2
  48. package/dist/{chunk-D5EC5KVO.js → chunk-D5DCBD4Y.js} +2 -2
  49. package/dist/{chunk-3SDOQUUF.js → chunk-D6ZC7XKZ.js} +2 -2
  50. package/dist/chunk-EJVQJCG5.js +370 -0
  51. package/dist/{chunk-AH5Q262S.js → chunk-F24FNCTK.js} +1 -1
  52. package/dist/{chunk-V6FDQLR7.js → chunk-FR6BYFPC.js} +53 -21
  53. package/dist/{chunk-UXOGCKL6.js → chunk-GEXT7BTE.js} +1 -1
  54. package/dist/{chunk-JTU2GOM4.js → chunk-GGORNBSC.js} +18 -6
  55. package/dist/{chunk-ERXXITG4.js → chunk-HGFAEDG3.js} +1 -1
  56. package/dist/chunk-HODO5BX5.js +28 -0
  57. package/dist/chunk-HW7B43E5.js +14 -0
  58. package/dist/{chunk-CSTQDNGK.js → chunk-IAM3D3RL.js} +43 -24
  59. package/dist/{chunk-PGMVL4AB.js → chunk-JJ3DELUL.js} +2 -2
  60. package/dist/{chunk-ILU5I6RW.js → chunk-JMLK6MH7.js} +1 -1
  61. package/dist/{chunk-YEMPUQVU.js → chunk-JWTQS5F5.js} +2 -2
  62. package/dist/{chunk-VDF5DFWT.js → chunk-K4ZHAURD.js} +1 -1
  63. package/dist/chunk-NV72HZ4W.js +45 -0
  64. package/dist/{chunk-4Y5FWDPM.js → chunk-NZ4RXN3G.js} +55 -7
  65. package/dist/chunk-OWZ7ATLZ.js +187 -0
  66. package/dist/{chunk-TXBAOEKG.js → chunk-PKGN32AU.js} +5 -4
  67. package/dist/chunk-Q6E4OPGG.js +40 -0
  68. package/dist/{chunk-QEDHRBAT.js → chunk-QL6XNPSD.js} +2 -2
  69. package/dist/{chunk-JBE46QJZ.js → chunk-R46IQIGK.js} +1 -1
  70. package/dist/{chunk-6WIQCXOO.js → chunk-RPZ7OQGD.js} +1 -1
  71. package/dist/{chunk-PHYMNUJU.js → chunk-S54ECQGY.js} +1 -1
  72. package/dist/{chunk-VGH4MDTU.js → chunk-VHKEF5ZB.js} +1 -1
  73. package/dist/{chunk-HF57LG73.js → chunk-VO3JYRT5.js} +43 -29
  74. package/dist/chunk-VWWITFWM.js +300 -0
  75. package/dist/{chunk-CIG5UL2S.js → chunk-ZTLMPUO6.js} +3 -2
  76. package/dist/client/index.js +3 -3
  77. package/dist/commerce/index.js +4 -4
  78. package/dist/contracts/color.d.ts +1 -1
  79. package/dist/contracts/entries.d.ts +1 -1
  80. package/dist/contracts/error-page.d.ts +183 -0
  81. package/dist/contracts/llms.d.ts +1 -0
  82. package/dist/contracts/proposal-sitemap.d.ts +130 -0
  83. package/dist/contracts/schema-placeholders.d.ts +87 -0
  84. package/dist/engage/EngageWidget.d.ts +13 -12
  85. package/dist/engage/index.d.ts +10 -9
  86. package/dist/engage/index.js +31 -35
  87. package/dist/engage/types.d.ts +19 -244
  88. package/dist/fleet/FleetHeartbeat.d.ts +1 -1
  89. package/dist/fleet/index.js +4 -4
  90. package/dist/forms/FormEnhancer.d.ts +25 -12
  91. package/dist/forms/ServerForm.d.ts +11 -10
  92. package/dist/forms/StaticForm.d.ts +3 -1
  93. package/dist/forms/field-autocomplete.d.ts +28 -0
  94. package/dist/forms/form-dom-values.d.ts +42 -0
  95. package/dist/forms/index.js +11 -9
  96. package/dist/forms/server.js +6 -4
  97. package/dist/forms/static.js +3 -2
  98. package/dist/forms/submitForm.d.ts +8 -1
  99. package/dist/forms/types.d.ts +41 -2
  100. package/dist/forms/useForm.d.ts +21 -4
  101. package/dist/forms/webmcp.d.ts +38 -0
  102. package/dist/images/index.js +4 -4
  103. package/dist/index.d.ts +4 -1
  104. package/dist/index.js +1 -1
  105. package/dist/layout/SiteKitClientProviders.d.ts +3 -3
  106. package/dist/layout/SiteKitLayout.d.ts +4 -4
  107. package/dist/layout/client.d.ts +3 -3
  108. package/dist/layout/client.js +7 -7
  109. package/dist/layout/index.js +8 -8
  110. package/dist/layout/types.d.ts +5 -4
  111. package/dist/llms/contract.js +1 -1
  112. package/dist/llms/index.js +6 -6
  113. package/dist/maps/index.js +3 -3
  114. package/dist/mcp/index.js +2 -14
  115. package/dist/mcp/sonor.js +14 -11
  116. package/dist/revalidate/index.js +2 -2
  117. package/dist/seo/client.js +4 -4
  118. package/dist/seo/index.js +15 -8
  119. package/dist/seo/llms/contract.js +1 -1
  120. package/dist/seo/llms.js +6 -6
  121. package/dist/seo/register-sitemap-cli.js +1 -1
  122. package/dist/seo/sitemap.js +4 -4
  123. package/dist/server/index.js +2 -2
  124. package/dist/shared/dialog.d.ts +27 -0
  125. package/dist/shared/identity.d.ts +1 -1
  126. package/dist/shared/layers.d.ts +7 -0
  127. package/dist/shared/mid-form.d.ts +53 -0
  128. package/dist/shared/reporting-gate.d.ts +1 -1
  129. package/dist/shared/version.d.ts +1 -1
  130. package/dist/shared/visual-viewport-gap.d.ts +1 -1
  131. package/dist/signal/index.js +2 -2
  132. package/dist/signal/types.d.ts +1 -1
  133. package/dist/sitemap/index.js +4 -4
  134. package/dist/{socket-loader-R24ZSRSQ.js → socket-loader-CGIPEG74.js} +1 -1
  135. package/dist/sync/index.js +5 -5
  136. package/dist/types.d.ts +3 -1
  137. package/dist/{web-vitals.attribution-GD6LGLVF.js → web-vitals.attribution-N2PCCF4Q.js} +1 -1
  138. package/dist/website/BlocksPopup.d.ts +1 -1
  139. package/dist/website/PopupBlocks.d.ts +5 -2
  140. package/dist/website/SitePopups.d.ts +25 -0
  141. package/dist/website/images.js +4 -4
  142. package/dist/website/index.js +6 -6
  143. package/dist/website/popup-rules.d.ts +46 -0
  144. package/dist/website/popup-types.d.ts +113 -0
  145. package/dist/website/popups.d.ts +2 -6
  146. package/dist/website/popups.js +6 -14
  147. package/dist/{writeLLMsTxt-G3JBNBC5.js → writeLLMsTxt-OV24LQVL.js} +3 -3
  148. package/docs/MIGRATING-TO-7.md +41 -9
  149. package/docs.json +2 -1
  150. package/package.json +2 -2
  151. package/src/analytics/README.md +2 -2
  152. package/src/articles/README.md +4 -1
  153. package/src/{engage → chat}/README.md +63 -62
  154. package/src/forms/README.md +49 -3
  155. package/src/layout/README.md +8 -5
  156. package/src/seo/README.md +3 -1
  157. package/src/sync/README.md +10 -0
  158. package/src/website/README.md +133 -0
  159. package/dist/ChatWidget-UCDWMWCW.js +0 -15
  160. package/dist/EngageWidget-7ZYSK4GE.js +0 -11
  161. package/dist/ManagedForm-H7BVAH2W.js +0 -14
  162. package/dist/SitemapSync-XBANGKHZ.js +0 -8
  163. package/dist/chunk-NDF4A5JM.js +0 -37
  164. package/dist/chunk-VLARKWZU.js +0 -100
  165. package/dist/chunk-YCJT4JJG.js +0 -837
  166. package/dist/engage/DesignRenderer.d.ts +0 -57
  167. package/dist/engage/element-rules.d.ts +0 -38
@@ -1,254 +1,29 @@
1
1
  /**
2
- * @sonordev/site-kit/engage - Type definitions
2
+ * @sonordev/site-kit/engage types: deprecated, kept through 7.x so old
3
+ * imports compile. Engage was retired in Sonor. Chat types live in
4
+ * `@sonordev/site-kit/chat`, popup types in `@sonordev/site-kit/website/popups`.
5
+ * Nothing here has runtime code. Removed in 8.0.
3
6
  */
4
- import type { DesignDocument } from './DesignRenderer';
5
- import type { PopupBlock } from '../website/contract';
6
- export type WidgetType = 'popup' | 'nudge' | 'bar' | 'chat' | 'slide-in';
7
- export interface EngageElement {
8
- id: string;
9
- project_id: string;
10
- name: string;
11
- type: WidgetType;
12
- config: WidgetConfig;
13
- targeting: TargetingRules;
14
- trigger: TriggerConfig;
15
- is_active: boolean;
16
- priority: number;
17
- created_at: string;
18
- updated_at: string;
19
- /**
20
- * Website → Popups & Banners: the popup as blocks, drawn in the site's own
21
- * design by PopupBlocks. Sent alongside design_json, which older site-kits
22
- * still draw; a site-kit that knows blocks prefers them.
23
- */
24
- blocks?: PopupBlock[];
25
- /**
26
- * Where a blocks popup sits. `type`/`config` describe it for older kits
27
- * (which can't pin a bar to the bottom); this says what it actually is.
28
- */
29
- layout?: {
30
- placement: 'popup' | 'bar' | 'slide-in';
31
- edge?: 'top' | 'bottom';
32
- corner?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
33
- /** The popup's own accent, over the site's. */
34
- accent?: string;
35
- };
36
- design_json?: DesignDocument;
37
- compiled_bundle_url?: string;
38
- status?: 'draft' | 'published' | 'archived' | 'paused';
39
- }
40
- export interface WidgetConfig {
41
- /** Visual style */
42
- theme?: 'light' | 'dark' | 'custom';
43
- position?: 'center' | 'top' | 'bottom' | 'left' | 'right' | 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
44
- /** Content */
45
- title?: string;
46
- message?: string;
47
- imageUrl?: string;
48
- /** CTA */
49
- ctaText?: string;
50
- ctaUrl?: string;
51
- ctaAction?: 'link' | 'close' | 'form' | 'chat';
52
- /** Form (if ctaAction is 'form') */
53
- formId?: string;
54
- /** Styling */
55
- backgroundColor?: string;
56
- textColor?: string;
57
- ctaBackgroundColor?: string;
58
- ctaTextColor?: string;
59
- borderRadius?: number;
60
- /** Behavior */
61
- closable?: boolean;
62
- showOverlay?: boolean;
63
- overlayOpacity?: number;
64
- }
65
- export interface TargetingRules {
66
- /** Page targeting */
67
- pages?: {
68
- include?: string[];
69
- exclude?: string[];
70
- };
71
- /** Device targeting */
72
- devices?: ('desktop' | 'mobile' | 'tablet')[];
73
- /** Visitor targeting */
74
- visitor?: {
75
- type?: 'new' | 'returning' | 'all';
76
- minSessions?: number;
77
- maxSessions?: number;
78
- };
79
- /** Source targeting */
80
- source?: {
81
- utm_source?: string[];
82
- utm_medium?: string[];
83
- utm_campaign?: string[];
84
- referrer?: string[];
85
- };
86
- /** Time targeting */
87
- schedule?: {
88
- startDate?: string;
89
- endDate?: string;
90
- daysOfWeek?: number[];
91
- startTime?: string;
92
- endTime?: string;
93
- timezone?: string;
94
- };
95
- }
96
- export interface TriggerConfig {
97
- type: 'immediate' | 'delay' | 'scroll' | 'exit-intent' | 'click' | 'custom';
98
- /** Delay in seconds (for 'delay' type) */
99
- delay?: number;
100
- /** Scroll percentage (for 'scroll' type) */
101
- scrollPercentage?: number;
102
- /** CSS selector (for 'click' type) */
103
- clickSelector?: string;
104
- /** Custom event name (for 'custom' type) */
105
- customEvent?: string;
106
- /** Frequency capping */
107
- frequency?: {
108
- type: 'always' | 'once' | 'once-per-session' | 'every-n-days';
109
- days?: number;
110
- };
111
- }
112
- /** The corner the Echo chat launcher sits in. */
113
- export type ChatLauncherPosition = 'bottom-right' | 'bottom-left';
114
- /**
115
- * Where the Echo chat launcher sits, including the layer it stacks on. The
116
- * single definition of launcher placement: EngageConfig (here and in
117
- * `@sonordev/site-kit/layout`), EngageWidget's props and ChatConfig all
118
- * extend it, so a new placement option is added once. ChatWidget turns it
119
- * into styles through `chatLauncherStyles` (./launcher-placement).
120
- */
121
- export interface ChatLauncherPlacement {
122
- /** Corner. Default: 'bottom-right' */
123
- position?: ChatLauncherPosition;
124
- /**
125
- * Distance from the bottom of the viewport to the launcher, as any CSS
126
- * length (`'88px'`, `'5.5rem'`, `'calc(4rem + 8px)'`). A number is pixels.
127
- * Default: `'20px'`.
128
- *
129
- * The device's safe-area inset is added on top, so pass the clearance you
130
- * want above your own UI, not the inset. The chat popup opens above the
131
- * launcher wherever this puts it.
132
- *
133
- * A `--sk-echo-offset-bottom` custom property set anywhere above the
134
- * launcher (`:root`, `body`, a media query, a `:has()` rule) wins over this
135
- * value. Use that when the offset depends on the page or the breakpoint,
136
- * such as clearing a sticky bar that only some pages render.
137
- */
138
- offsetBottom?: string | number;
139
- /**
140
- * Stacking layer. The launcher sits on it and the chat popup one layer
141
- * beneath. Default: 9999.
142
- *
143
- * On EngageConfig and EngageWidget the same number also stacks popups,
144
- * nudges and bars, so everything Engage renders shares one layer. Lower it
145
- * when the site's own fixed UI (a mobile menu, a cookie banner) has to
146
- * cover the chat.
147
- */
148
- zIndex?: number;
149
- }
150
- export interface ChatConfig extends ChatLauncherPlacement {
151
- /** Widget position */
152
- position: ChatLauncherPosition;
153
- /** Widget appearance */
154
- buttonIcon?: 'chat' | 'help' | 'custom';
155
- buttonColor?: string;
156
- buttonText?: string;
157
- /** Brand primary color */
158
- brandPrimary?: string;
159
- /** Welcome message */
160
- welcomeMessage?: string;
161
- /** Operating hours */
162
- operatingHours?: {
163
- enabled: boolean;
164
- timezone: string;
165
- hours: {
166
- [day: number]: {
167
- start: string;
168
- end: string;
169
- } | null;
170
- };
171
- };
172
- /** AI vs Live Chat */
173
- mode: 'ai' | 'live' | 'hybrid';
174
- /** AI settings (if mode is 'ai' or 'hybrid') */
175
- aiSettings?: {
176
- skillId?: string;
177
- handoffToLive?: boolean;
178
- handoffKeywords?: string[];
179
- /** When AI should suggest handoff based on sentiment or keywords */
180
- handoffTriggers?: {
181
- negativesentiment?: boolean;
182
- keywords?: string[];
183
- afterMessages?: number;
184
- };
185
- };
186
- /** Offline mode settings (when no agents available) */
187
- offlineMode?: 'form' | 'ai' | 'message';
188
- /** Form slug to show when offline (defaults to 'contact') */
189
- offlineFormSlug?: string;
190
- /** AI fallback when no agents online (hybrid mode) */
191
- aiFallbackEnabled?: boolean;
192
- /** Custom offline message */
193
- offlineMessage?: string;
194
- }
195
- /**
196
- * UI actions the visitor Echo endpoint (POST /echo/public/chat) may return on
197
- * the JSON response alongside `content` / `conversationId`. The chat widget
198
- * renders each as an inline card or chip beneath the assistant message.
199
- *
200
- * This mirrors the shape Echo's endpoint sends. It is a LOCAL contract on
201
- * purpose: the kit never imports from the API.
202
- *
203
- * - `show_form` → data.formSlug (+ optional data.prefill) — offer the managed form inline.
204
- * - `suggest_action` → data.action + data.message — clickable chip (send message or navigate).
205
- * - `show_portfolio_item` → data.slug — the generic widget ignores it (a site's own chat UI handles it).
206
- */
207
- export type UiActionType = 'show_form' | 'suggest_action' | 'show_portfolio_item';
208
- export interface UiAction {
209
- type: UiActionType | string;
210
- data: Record<string, unknown>;
211
- }
212
- export interface ChatMessage {
213
- id: string;
214
- conversation_id: string;
215
- role: 'user' | 'assistant' | 'agent' | 'system';
216
- content: string;
217
- metadata?: Record<string, unknown>;
218
- created_at: string;
219
- /** Name of the agent (for agent messages) */
220
- sender_name?: string;
221
- /** Avatar URL of the agent */
222
- sender_avatar?: string;
223
- }
224
- export interface ChatConversation {
225
- id: string;
226
- project_id: string;
227
- visitor_id: string;
228
- status: 'active' | 'ai' | 'pending_handoff' | 'human' | 'closed';
229
- assigned_to?: string;
230
- last_message_at: string;
231
- created_at: string;
232
- /** AI summary of the conversation */
233
- ai_summary?: string;
234
- /** Visitor info */
235
- visitor_name?: string;
236
- visitor_email?: string;
237
- }
238
- /** Availability status returned by the widget API */
239
- export interface ChatAvailability {
240
- available: boolean;
241
- mode: 'live' | 'ai' | 'offline';
242
- agentsOnline: number;
243
- operatingHoursActive: boolean;
244
- }
7
+ import type { ChatLauncherPlacement } from '../chat/types';
8
+ export type { ChatAvailability, ChatConfig, ChatConversation, ChatLauncherPlacement, ChatLauncherPosition, ChatMessage, UiAction, UiActionType, } from '../chat/types';
9
+ import type { SitePopup, SitePopupConfig, SitePopupTargeting, SitePopupTrigger, SitePopupType } from '../website/popup-types';
10
+ /** @deprecated Use `SitePopup` from `@sonordev/site-kit/website/popups`. */
11
+ export type EngageElement = SitePopup;
12
+ /** @deprecated Use `SitePopupType` from `@sonordev/site-kit/website/popups`. */
13
+ export type WidgetType = SitePopupType;
14
+ /** @deprecated Use `SitePopupConfig` from `@sonordev/site-kit/website/popups`. */
15
+ export type WidgetConfig = SitePopupConfig;
16
+ /** @deprecated Use `SitePopupTargeting` from `@sonordev/site-kit/website/popups`. */
17
+ export type TargetingRules = SitePopupTargeting;
18
+ /** @deprecated Use `SitePopupTrigger` from `@sonordev/site-kit/website/popups`. */
19
+ export type TriggerConfig = SitePopupTrigger;
20
+ /** @deprecated Use SiteKitLayout's `chat` and `popups` props. */
245
21
  export interface EngageConfig extends ChatLauncherPlacement {
246
22
  projectId: string;
247
- /** Enable chat */
248
23
  chatEnabled?: boolean;
249
- /** Debug mode */
250
24
  debug?: boolean;
251
25
  }
26
+ /** @deprecated Unused since Engage was retired. */
252
27
  export interface EngageAnalytics {
253
28
  element_id: string;
254
29
  event_type: 'impression' | 'click' | 'close' | 'submit' | 'dismiss';
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * It is mounted as a deferred, CHILDLESS SIBLING — never a wrapper — by
11
11
  * SiteKitClientProviders, behind the same idle/first-interaction gate as
12
- * Analytics and Engage, so it never enters the critical hydration path (the
12
+ * Analytics and chat, so it never enters the critical hydration path (the
13
13
  * page must server-render). It renders null and carries no
14
14
  * visitor data.
15
15
  */
@@ -1,10 +1,10 @@
1
1
  'use client';
2
- export { FLEET_HEARTBEAT_PATH, FleetHeartbeat, detectNextVersion, sendFleetHeartbeat } from '../chunk-NK4CSJNP.js';
2
+ export { FLEET_HEARTBEAT_PATH, FleetHeartbeat, detectNextVersion, sendFleetHeartbeat } from '../chunk-CF5QNFYU.js';
3
3
  export { FLEET_CONTRACT_VERSION, FLEET_MODULES, FLEET_MODULES_MAX, FLEET_SITE_MAX_LENGTH, FLEET_VERSION_MAX_LENGTH, FLEET_VERSION_PATTERN, buildFleetHeartbeatPayload, isFleetModule, normalizeFleetModules, normalizeFleetSite, sanitizeFleetVersion } from '../chunk-ZTZQPYP6.js';
4
4
  import '../chunk-YC7ELZS3.js';
5
5
  import '../chunk-43OCZ3JA.js';
6
6
  import '../chunk-EKBEOXTH.js';
7
- import '../chunk-3SDOQUUF.js';
8
- import '../chunk-VDF5DFWT.js';
9
- import '../chunk-AH5Q262S.js';
7
+ import '../chunk-D6ZC7XKZ.js';
8
+ import '../chunk-K4ZHAURD.js';
9
+ import '../chunk-F24FNCTK.js';
10
10
  import '../chunk-PKBMQBKP.js';
@@ -2,23 +2,36 @@
2
2
  * FormEnhancer — the only client code a managed form needs up front.
3
3
  *
4
4
  * Renders the server-rendered shell (<StaticForm>, passed as `children`) until
5
- * an idle/first-interaction gate fires, then lazy-imports the interactive form
6
- * and swaps it in. Everything expensive — validation, conditional logic,
5
+ * an idle/first-interaction gate fires, then imports the interactive form and
6
+ * swaps it in. Everything expensive — validation, conditional logic,
7
7
  * spotlight/stage, celebration, momentum — lives behind that dynamic import,
8
8
  * so a page ships this boundary and nothing else in its initial chunk set.
9
9
  *
10
- * The shell is a REAL form the whole time (native action + signed token), so
11
- * the progression is:
10
+ * Since 6.3.2 there is no no-JavaScript submit (the native POST door was a
11
+ * bot magnet and took no real leads), so the shell is a real form a visitor
12
+ * can read and type into, but only the enhancement sends it. The progression:
12
13
  *
13
- * no JS at all -> shell, submits natively. Works.
14
- * JS, pre-idle -> shell, submits natively. Works.
15
- * JS, post-idle -> enhanced form, submits over JSON. Works, with delight.
16
- * chunk 404s -> shell stays. Works. (Never left blank.)
14
+ * no JS at all -> shell; its Send button stays disabled. Nothing is
15
+ * lost to a POST that goes nowhere.
16
+ * JS, pre-swap -> shell. A submit is caught here and replayed by the
17
+ * interactive form the moment it mounts.
18
+ * JS, post-swap -> enhanced form, submits over JSON, with delight.
19
+ * chunk 404s -> shell stays. (Never left blank.)
17
20
  *
18
- * That last line is the invariant to protect: the shell is only removed once
19
- * the enhancement has actually rendered. Do not "hide the shell then load" —
20
- * a CDN hiccup would leave a visitor staring at an empty box where the form
21
- * used to be.
21
+ * Two rules keep the swap from losing anything (7.2.0):
22
+ *
23
+ * - The shell stays mounted, untouched, until the interactive form's code
24
+ * has loaded. The old version re-mounted the shell inside a Suspense
25
+ * fallback the moment the gate fired, which reset every control and
26
+ * dropped focus mid-click.
27
+ * - What's in the shell's controls at the moment of the swap is carried
28
+ * into the interactive form (form-dom-values.ts), including a date typed
29
+ * into the plain date input. Typing into the shell, or a browser agent
30
+ * filling it, also starts the swap at once instead of waiting for idle.
31
+ *
32
+ * The invariant to protect: the shell is only removed once the enhancement
33
+ * has actually rendered. Do not "hide the shell then load" — a CDN hiccup
34
+ * would leave a visitor staring at an empty box where the form used to be.
22
35
  *
23
36
  * ── Entrance animation ──────────────────────────────────────────────────
24
37
  *
@@ -17,13 +17,14 @@
17
17
  * What each layer is for:
18
18
  *
19
19
  * getFormConfig — server fetch, ISR-cached. No client round trip.
20
- * StaticForm — real HTML form: fields, honeypot, signed native token.
21
- * Submits without JavaScript.
22
- * FormEnhancer — ~1 KB client boundary. Lazy-loads the interactive form
23
- * at idle and swaps it in over the shell.
20
+ * StaticForm — real HTML form: every field and the honeypot, readable
21
+ * by people and crawlers before any script runs.
22
+ * FormEnhancer — ~1 KB client boundary. Loads the interactive form at idle
23
+ * (or as soon as someone types into the shell) and swaps it
24
+ * in, carrying over anything already typed.
24
25
  *
25
- * Set `enhance={false}` for a form that should never load JavaScript at all —
26
- * the native POST handles it, and the page ships zero form script.
26
+ * Since 6.3.2 only the enhancement can send a managed form (the no-JavaScript
27
+ * POST was removed), so every ServerForm enhances.
27
28
  *
28
29
  * The client `ManagedForm` from `@sonordev/site-kit/forms` still exists and
29
30
  * still works; reach for it only when the form must live inside an existing
@@ -44,9 +45,9 @@ export interface ServerFormProps {
44
45
  /** Entrance animation for the idle upgrade. See FormEnhancer. */
45
46
  enter?: FormEnterAnimation;
46
47
  /**
47
- * false = ship no form JavaScript at all. The static form still submits via
48
- * the native endpoint; you lose inline validation, the experiences, and the
49
- * celebration. Good for a footer newsletter field, wrong for a long form.
48
+ * @deprecated Ignored since 7.2.0. There is no no-JavaScript submit (6.3.2
49
+ * removed it), so a form that never loads its script can't send, and
50
+ * `false` only ever produced a form that silently went nowhere.
50
51
  */
51
52
  enhance?: boolean;
52
53
  /**
@@ -65,4 +66,4 @@ export interface ServerFormProps {
65
66
  /** Passed through to getFormConfig (api key / url / revalidate). */
66
67
  configOptions?: GetFormConfigOptions;
67
68
  }
68
- export declare function ServerForm({ formId, className, experience, enter, enhance, defer, initialConfig, configOptions, defaultValues, }: ServerFormProps): Promise<React.JSX.Element>;
69
+ export declare function ServerForm({ formId, className, experience, enter, defer, initialConfig, configOptions, defaultValues, }: ServerFormProps): Promise<React.JSX.Element>;
@@ -8,7 +8,9 @@
8
8
  * (/api/public/forms/submit-native, a signed token in the page HTML) took
9
9
  * 10,500 bot submissions across the fleet and not one real lead, because a bot
10
10
  * only had to download the page to borrow the token. `method="post"` stays so
11
- * a click before hydration never puts the visitor's details in a URL.
11
+ * nothing typed can end up in a URL, and since 7.2.0 the Send button renders
12
+ * disabled until FormEnhancer is ready to catch the submit, so a click before
13
+ * hydration no longer posts the page to itself and throws the input away.
12
14
  *
13
15
  * ── Why this exists ─────────────────────────────────────────────────────
14
16
  *
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @sonordev/site-kit/forms - autocomplete tokens for managed form fields
3
+ *
4
+ * Browser autofill, password managers and AI agents filling a form for
5
+ * someone all read a field's `autocomplete` token to know what it wants.
6
+ * Without one they guess from the label, and a guess is how a phone number
7
+ * ends up in the company field. Managed forms shipped no tokens at all before
8
+ * 7.2.0.
9
+ *
10
+ * Single source of truth for which token a managed field gets. FormField is
11
+ * the only caller; every experience (classic, spotlight, stage, the static
12
+ * shell) renders its controls through FormField.
13
+ *
14
+ * Precedence: the field's CRM destination (`destination_field`, set in
15
+ * Sonor) > its input type > its slug > its label. When nothing matches the
16
+ * attribute is omitted. A wrong token is worse than none, so matching is
17
+ * deliberately narrow: exact keys only, plus Sonor's random slug suffixes
18
+ * (`email_k3x9pq2m`).
19
+ */
20
+ import type { FormField } from './types';
21
+ /**
22
+ * The `autocomplete` token for one managed field, or undefined to omit it.
23
+ * Only text-like inputs and selects get one; a message box, a date or a
24
+ * checkbox never does.
25
+ */
26
+ export declare function autocompleteFor(field: Pick<FormField, 'field_type' | 'slug' | 'label'> & {
27
+ destination_field?: string;
28
+ }): string | undefined;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @sonordev/site-kit/forms - reading a managed form's values out of the DOM
3
+ *
4
+ * Two moments need what is actually in the controls rather than what React
5
+ * last heard about:
6
+ *
7
+ * 1. The enhancement swap. The server-rendered shell is plain HTML, so
8
+ * whatever a visitor (or an agent) typed before the interactive form
9
+ * loaded lives only in the DOM. FormEnhancer reads it here and seeds the
10
+ * interactive form with it. Before 7.2.0 the swap threw it away.
11
+ * 2. A submit. A browser agent can set a control's value without the input
12
+ * event React listens for. FormClient compares the DOM with its state
13
+ * at submit time and adopts anything it missed, instead of rejecting a
14
+ * filled form as empty.
15
+ *
16
+ * Single source of truth for turning form controls back into field values,
17
+ * typed the way FormClient stores them (checkbox groups and multi-selects as
18
+ * arrays, a lone checkbox as a boolean, ratings and sliders as numbers).
19
+ */
20
+ import type { FormField } from './types';
21
+ /**
22
+ * Nothing worth seeding or adopting: an empty value (field-rules.ts), an
23
+ * unticked lone checkbox, or whitespace.
24
+ */
25
+ export declare function isBlank(value: unknown): boolean;
26
+ /**
27
+ * The value of every answerable field, read from the form's controls.
28
+ * Fields with no control in the DOM (another step, conditionally hidden) and
29
+ * blank fields are left out, as is the honeypot.
30
+ */
31
+ export declare function readFormControlValues(form: HTMLFormElement | null | undefined, fields: FormField[] | undefined, honeypotName: string): Record<string, unknown>;
32
+ /**
33
+ * What the shell holds beyond each field's configured default: the part of
34
+ * the shell worth carrying into the interactive form.
35
+ */
36
+ export declare function shellValuesToCarry(form: HTMLFormElement | null | undefined, fields: FormField[] | undefined, honeypotName: string): Record<string, unknown>;
37
+ /**
38
+ * Values present in the DOM that React state doesn't have: a control set
39
+ * without an input event. Only fills a blank state value; never overrides
40
+ * something the visitor typed through React.
41
+ */
42
+ export declare function unseenControlValues(form: HTMLFormElement | null | undefined, fields: FormField[] | undefined, honeypotName: string, state: Record<string, unknown>): Record<string, unknown>;
@@ -1,21 +1,23 @@
1
1
  'use client';
2
2
  export { FormCelebration } from '../chunk-YGGDU6C3.js';
3
- export { FormClient, ManagedForm } from '../chunk-CSTQDNGK.js';
4
- import '../chunk-D5EC5KVO.js';
3
+ export { FormClient, ManagedForm } from '../chunk-IAM3D3RL.js';
4
+ import '../chunk-D5DCBD4Y.js';
5
5
  export { DatePicker } from '../chunk-SOD2F6C2.js';
6
- export { FormEnhancer } from '../chunk-VLARKWZU.js';
6
+ export { FormEnhancer } from '../chunk-OWZ7ATLZ.js';
7
7
  import '../chunk-YC7ELZS3.js';
8
8
  import '../chunk-DXO6SBMD.js';
9
- export { FormField as FormFieldComponent } from '../chunk-LUXVWITO.js';
10
- export { useForm, useFormTracking } from '../chunk-V6FDQLR7.js';
11
- import '../chunk-AQSNPZY4.js';
9
+ export { FormField as FormFieldComponent } from '../chunk-4NTBQNHA.js';
10
+ export { useForm, useFormTracking } from '../chunk-FR6BYFPC.js';
11
+ import '../chunk-Q6E4OPGG.js';
12
+ import '../chunk-C2FZBUSS.js';
12
13
  export { DEFAULT_FORM_SUCCESS_MESSAGE } from '../chunk-5SQK3D53.js';
14
+ import '../chunk-HW7B43E5.js';
13
15
  import '../chunk-NJCTQH2P.js';
14
16
  import '../chunk-L2V5PUFN.js';
15
17
  import '../chunk-GJWI74ZZ.js';
16
- import { sonorFetch } from '../chunk-3SDOQUUF.js';
17
- import '../chunk-VDF5DFWT.js';
18
- import '../chunk-AH5Q262S.js';
18
+ import { sonorFetch } from '../chunk-D6ZC7XKZ.js';
19
+ import '../chunk-K4ZHAURD.js';
20
+ import '../chunk-F24FNCTK.js';
19
21
  import '../chunk-PKBMQBKP.js';
20
22
 
21
23
  // src/forms/formsApi.ts
@@ -1,11 +1,13 @@
1
- export { ServerForm, getFormConfig } from '../chunk-V3PUVBME.js';
2
- import '../chunk-VLARKWZU.js';
1
+ export { ServerForm, getFormConfig } from '../chunk-5V2V5C3Y.js';
2
+ import '../chunk-OWZ7ATLZ.js';
3
3
  import '../chunk-YC7ELZS3.js';
4
- export { StaticForm } from '../chunk-CIG5UL2S.js';
4
+ export { StaticForm } from '../chunk-ZTLMPUO6.js';
5
5
  import '../chunk-DXO6SBMD.js';
6
- import '../chunk-LUXVWITO.js';
6
+ import '../chunk-4NTBQNHA.js';
7
7
  import '../chunk-X6F6SO4S.js';
8
8
  import '../chunk-MH2DSGC6.js';
9
+ import '../chunk-C2FZBUSS.js';
9
10
  import '../chunk-5SQK3D53.js';
11
+ import '../chunk-HW7B43E5.js';
10
12
  import '../chunk-NJCTQH2P.js';
11
13
  import '../chunk-PKBMQBKP.js';
@@ -1,6 +1,7 @@
1
- export { StaticForm } from '../chunk-CIG5UL2S.js';
1
+ export { StaticForm } from '../chunk-ZTLMPUO6.js';
2
2
  import '../chunk-DXO6SBMD.js';
3
- import '../chunk-LUXVWITO.js';
3
+ import '../chunk-4NTBQNHA.js';
4
4
  import '../chunk-5SQK3D53.js';
5
+ import '../chunk-HW7B43E5.js';
5
6
  import '../chunk-NJCTQH2P.js';
6
7
  import '../chunk-PKBMQBKP.js';
@@ -24,13 +24,20 @@ export interface SubmitFormArgs {
24
24
  honeypotValue: string;
25
25
  /** Timestamp (ms) the form was first rendered — a weak anti-bot timing signal. */
26
26
  formLoadedAt: number;
27
+ /**
28
+ * The browser reported that an AI agent pressed submit (WebMCP's
29
+ * `SubmitEvent.agentInvoked`). Sent so Sonor can tag the lead for the
30
+ * business; it is the browser's word, so it never changes how the
31
+ * submission is checked.
32
+ */
33
+ agentInvoked?: boolean;
27
34
  }
28
35
  /**
29
36
  * Submit a managed form. Throws on failure (network, timeout, or a non-2xx
30
37
  * response) with a human-readable message; resolves with the parsed API
31
38
  * response on success.
32
39
  */
33
- export declare function submitForm({ config, values, honeypotValue, formLoadedAt, }: SubmitFormArgs): Promise<FormSubmitResult>;
40
+ export declare function submitForm({ config, values, honeypotValue, formLoadedAt, agentInvoked, }: SubmitFormArgs): Promise<FormSubmitResult>;
34
41
  /**
35
42
  * The receipt Sonor returns plus the values that were sent. `onSuccess`
36
43
  * callers need the values (free-audit reads the website and email back), and
@@ -127,6 +127,45 @@ export interface ConditionalLogic {
127
127
  * on purpose (a bot must not learn it was caught), so nothing here says
128
128
  * whether the lead was accepted, routed or scored.
129
129
  */
130
+ /**
131
+ * Options for a managed form's `submit()`. All optional; `submit()` with no
132
+ * argument behaves exactly as it always has.
133
+ */
134
+ export interface FormSubmitOptions {
135
+ /**
136
+ * The browser reported that an AI agent pressed submit (WebMCP's
137
+ * `SubmitEvent.agentInvoked`). Sonor tags the lead so the business can see
138
+ * how agent-sent leads compare; it changes nothing else about the submit.
139
+ */
140
+ agentInvoked?: boolean;
141
+ /**
142
+ * Values found in the form's controls that React state never received (a
143
+ * control set without an input event). Merged over blank state values
144
+ * before validating. See form-dom-values.ts.
145
+ */
146
+ domFills?: Record<string, unknown>;
147
+ }
148
+ /**
149
+ * What `submit()` resolves with. Callers that ignore it keep working; an
150
+ * agent that submitted through WebMCP is handed this as its result.
151
+ */
152
+ export type FormSubmitOutcome = {
153
+ status: 'sent';
154
+ message: string;
155
+ submissionId?: string;
156
+ } | {
157
+ status: 'invalid';
158
+ errors: Record<string, string>;
159
+ } | {
160
+ status: 'failed';
161
+ message: string;
162
+ } | {
163
+ status: 'busy';
164
+ } | {
165
+ status: 'next_step';
166
+ step: number;
167
+ totalSteps: number;
168
+ };
130
169
  export interface FormSubmitResult {
131
170
  success: boolean;
132
171
  /** The form_submissions row id (same as submissionId). */
@@ -197,7 +236,7 @@ export interface ExperienceRenderProps {
197
236
  isFieldVisible: (field: FormField) => boolean;
198
237
  fieldIsComplete: (field: FormField) => boolean;
199
238
  applyFieldError: (slug: string, error: string) => void;
200
- submit: () => Promise<void>;
239
+ submit: (options?: FormSubmitOptions) => Promise<FormSubmitOutcome>;
201
240
  trackStepChange: (step: number) => void;
202
241
  /** Pass to each FormField's onFocus/onBlur (field_interactions). */
203
242
  trackFieldFocus: (slug: string) => void;
@@ -269,7 +308,7 @@ export interface FormRenderProps {
269
308
  /** Go to specific step */
270
309
  goToStep: (step: number) => void;
271
310
  /** Submit the form */
272
- submit: () => Promise<void>;
311
+ submit: (options?: FormSubmitOptions) => Promise<FormSubmitOutcome>;
273
312
  /** Set field value */
274
313
  setFieldValue: (key: string, value: unknown) => void;
275
314
  /** Check if field should be visible (conditional logic) */