@sonordev/site-kit 7.1.1 → 7.1.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/AGENTS.md +2 -2
- package/CHANGELOG.md +17 -3426
- package/agent-manifest.json +1 -1
- package/dist/{AnalyticsProvider-3MI5RCTL.js → AnalyticsProvider-ZB33W6MC.js} +4 -4
- package/dist/{ArticleViewTracker-CMBSJR3N.js → ArticleViewTracker-GMHYLLLZ.js} +3 -3
- package/dist/{BlocksPopup-X3OAAK2L.js → BlocksPopup-EA6LYL25.js} +4 -4
- package/dist/{ChatWidget-JMFBXJ75.js → ChatWidget-UCDWMWCW.js} +5 -5
- package/dist/{EngageWidget-A3QKZ3SU.js → EngageWidget-7ZYSK4GE.js} +4 -4
- package/dist/{FileField-QRSEGO6D.js → FileField-QKA3JB2Q.js} +3 -3
- package/dist/{FormSpotlight-XKV7VTN4.js → FormSpotlight-AQTIA5TP.js} +2 -2
- package/dist/{FormStage-C3YGYAUZ.js → FormStage-FDTLTP3B.js} +2 -2
- package/dist/ManagedForm-H7BVAH2W.js +14 -0
- package/dist/{ManagedNewsletterForm-LPIKGJF4.js → ManagedNewsletterForm-J6QSAGGP.js} +4 -4
- package/dist/{SignalCore-ICXXZBYI.js → SignalCore-JZNXOLNT.js} +3 -3
- package/dist/{SiteDesignReporter-A7MS2CIP.js → SiteDesignReporter-W4EJUSTE.js} +5 -5
- package/dist/SitemapSync-XBANGKHZ.js +8 -0
- package/dist/SitemapSync.d.ts +1 -1
- package/dist/_client/booking-widget.js +5 -5
- package/dist/_client/testimonial-section.d.ts +1 -1
- package/dist/affiliates/api.d.ts +1 -1
- package/dist/affiliates/index.js +3 -3
- package/dist/analytics/AnalyticsProvider.d.ts +1 -1
- package/dist/analytics/index.js +4 -4
- package/dist/analytics/send-gate.d.ts +12 -25
- package/dist/articles/Article.d.ts +1 -1
- package/dist/articles/ArticleList.d.ts +1 -1
- package/dist/articles/ClusterLandingPage.d.ts +2 -2
- package/dist/articles/PublicationLayout.d.ts +1 -1
- package/dist/articles/PublicationSidebar.d.ts +1 -1
- package/dist/articles/RelatedPosts.d.ts +1 -1
- package/dist/articles/author-schema.d.ts +1 -1
- package/dist/articles/excerpt.d.ts +3 -4
- package/dist/articles/index.d.ts +2 -2
- package/dist/articles/index.js +1 -1
- package/dist/articles/news-sitemap.d.ts +1 -1
- package/dist/articles/processArticleHtml.d.ts +1 -1
- package/dist/articles/server-core.d.ts +4 -4
- package/dist/articles/server-ui.js +1 -1
- package/dist/articles/server.d.ts +4 -4
- package/dist/articles/types.d.ts +1 -1
- package/dist/chat/index.js +5 -5
- package/dist/{chunk-PXZ7LAOY.js → chunk-3SDOQUUF.js} +2 -2
- package/dist/{chunk-R3VKF43H.js → chunk-6WIQCXOO.js} +1 -1
- package/dist/{chunk-6OL23QYD.js → chunk-AH5Q262S.js} +1 -1
- package/dist/{chunk-OPRBF7VE.js → chunk-CIG5UL2S.js} +1 -1
- package/dist/{chunk-EQBZ24VH.js → chunk-CSTQDNGK.js} +4 -4
- package/dist/{chunk-JC7KXNNM.js → chunk-D5EC5KVO.js} +1 -1
- package/dist/{chunk-DUQMSNF2.js → chunk-ERXXITG4.js} +1 -1
- package/dist/{chunk-ILAW3XKD.js → chunk-HF57LG73.js} +7 -7
- package/dist/{chunk-542MIHNP.js → chunk-ILU5I6RW.js} +1 -1
- package/dist/{chunk-HAG4YIZY.js → chunk-JBE46QJZ.js} +6 -6
- package/dist/{chunk-ZAWANWUS.js → chunk-JTU2GOM4.js} +1 -1
- package/dist/{chunk-QPEEKFCA.js → chunk-LUXVWITO.js} +2 -2
- package/dist/{chunk-EFPS56JA.js → chunk-M3YEPKP7.js} +1 -1
- package/dist/{chunk-H42PZKVC.js → chunk-NK4CSJNP.js} +2 -2
- package/dist/{chunk-ODADQM6Z.js → chunk-PGMVL4AB.js} +2 -2
- package/dist/{chunk-Q3S26EQA.js → chunk-PHYMNUJU.js} +1 -1
- package/dist/{chunk-ICFRH2ED.js → chunk-Q4W23JYM.js} +1 -1
- package/dist/{chunk-J4D6ZXRW.js → chunk-QEDHRBAT.js} +1 -1
- package/dist/{chunk-V4HUSHZO.js → chunk-RHNWA34F.js} +2 -2
- package/dist/{chunk-KOPVJQ2P.js → chunk-UXOGCKL6.js} +1 -1
- package/dist/{chunk-RK7GF7PQ.js → chunk-V3PUVBME.js} +2 -2
- package/dist/{chunk-NEKEH4CK.js → chunk-V6FDQLR7.js} +1 -1
- package/dist/{chunk-OUNRFXDC.js → chunk-VDF5DFWT.js} +1 -1
- package/dist/{chunk-JIL5VY2R.js → chunk-VGH4MDTU.js} +1 -1
- package/dist/{chunk-RTMMHTEQ.js → chunk-VLARKWZU.js} +1 -1
- package/dist/{chunk-ZETJTCMV.js → chunk-WWSHFRDL.js} +1 -1
- package/dist/{chunk-B73QPZPH.js → chunk-YCJT4JJG.js} +3 -3
- package/dist/{chunk-7FWA3I6A.js → chunk-YEMPUQVU.js} +2 -2
- package/dist/client/index.js +3 -3
- package/dist/cms/CmsPreview.d.ts +2 -2
- package/dist/cms/index.d.ts +3 -3
- package/dist/cms/server-api.d.ts +1 -1
- package/dist/cms/types.d.ts +1 -1
- package/dist/commerce/EventCheckout.d.ts +1 -1
- package/dist/commerce/EventsAgenda.d.ts +3 -3
- package/dist/commerce/api.d.ts +1 -1
- package/dist/commerce/index.js +4 -4
- package/dist/commerce/server.d.ts +3 -4
- package/dist/contracts/entries.d.ts +1 -1
- package/dist/contracts/fleet.d.ts +9 -9
- package/dist/contracts/forms.d.ts +4 -10
- package/dist/contracts/llms.d.ts +1 -1
- package/dist/contracts/portfolio.d.ts +11 -15
- package/dist/contracts/seo-meta.d.ts +11 -12
- package/dist/contracts/seo-pages.d.ts +12 -18
- package/dist/contracts/site-cache.d.ts +4 -4
- package/dist/contracts/sites-normalize.d.ts +2 -4
- package/dist/contracts/sites.d.ts +3 -5
- package/dist/contracts/slot-content.d.ts +2 -2
- package/dist/contracts/slots.d.ts +6 -6
- package/dist/contracts/voice.d.ts +56 -0
- package/dist/contracts/website.d.ts +2 -2
- package/dist/engage/ChatWidget.d.ts +2 -2
- package/dist/engage/EchoUiActions.d.ts +3 -3
- package/dist/engage/EngageWidget.d.ts +1 -1
- package/dist/engage/brand-color.d.ts +1 -1
- package/dist/engage/element-rules.d.ts +2 -2
- package/dist/engage/index.js +6 -6
- package/dist/engage/types.d.ts +3 -3
- package/dist/fleet/FleetHeartbeat.d.ts +2 -2
- package/dist/fleet/index.js +4 -4
- package/dist/forms/FormSpotlight.d.ts +2 -2
- package/dist/forms/ManagedForm.d.ts +1 -1
- package/dist/forms/StaticForm.d.ts +1 -1
- package/dist/forms/field-interactions.d.ts +2 -2
- package/dist/forms/form-loaded-at.d.ts +2 -2
- package/dist/forms/formsApi.d.ts +2 -2
- package/dist/forms/index.d.ts +1 -1
- package/dist/forms/index.js +8 -8
- package/dist/forms/server.d.ts +1 -1
- package/dist/forms/server.js +4 -4
- package/dist/forms/static.js +2 -2
- package/dist/forms/submitForm.d.ts +1 -1
- package/dist/forms/types.d.ts +3 -3
- package/dist/images/ManagedFavicon.d.ts +1 -1
- package/dist/images/ManagedImage.d.ts +4 -4
- package/dist/images/api.d.ts +1 -1
- package/dist/images/index.d.ts +2 -2
- package/dist/images/index.js +4 -4
- package/dist/index.js +1 -1
- package/dist/landing/contract.d.ts +3 -4
- package/dist/layout/SiteKitClientProviders.d.ts +1 -1
- package/dist/layout/SiteKitLayout.d.ts +7 -7
- package/dist/layout/client.d.ts +1 -1
- package/dist/layout/client.js +7 -7
- package/dist/layout/index.js +8 -8
- package/dist/layout/types.d.ts +2 -2
- package/dist/llms/SpeakableSchema.d.ts +2 -2
- package/dist/llms/agent-access.d.ts +2 -2
- package/dist/llms/aiRobots.d.ts +4 -4
- package/dist/llms/api.d.ts +2 -2
- package/dist/llms/generateLLMsTxt.d.ts +1 -1
- package/dist/llms/index.js +4 -4
- package/dist/llms/links.d.ts +1 -1
- package/dist/llms/revalidate.d.ts +1 -1
- package/dist/llms/seo-revalidate.d.ts +2 -3
- package/dist/llms/types.d.ts +6 -6
- package/dist/llms/writeLLMsTxt.d.ts +10 -10
- package/dist/manifest/index.d.ts +8 -8
- package/dist/maps/index.js +3 -3
- package/dist/mcp/WebMcpTools.d.ts +1 -1
- package/dist/mcp/report.d.ts +2 -2
- package/dist/mcp/serverCard.d.ts +1 -1
- package/dist/mcp/sonor.d.ts +5 -6
- package/dist/mcp/sonor.js +8 -8
- package/dist/mcp/transport.d.ts +1 -2
- package/dist/mcp/types.d.ts +4 -5
- package/dist/motion/engine.d.ts +2 -3
- package/dist/motion/failsafe.d.ts +1 -2
- package/dist/motion/gsap.d.ts +2 -2
- package/dist/motion/three.d.ts +1 -1
- package/dist/og/config.d.ts +4 -4
- package/dist/og/contrast.d.ts +2 -2
- package/dist/og/pages.d.ts +5 -5
- package/dist/og/route-fit.d.ts +2 -2
- package/dist/og/route.d.ts +1 -2
- package/dist/og/template.d.ts +1 -1
- package/dist/proxy/index.d.ts +2 -2
- package/dist/proxy/securityHeaders.d.ts +10 -12
- package/dist/redirects/index.d.ts +7 -7
- package/dist/reputation/TestimonialSection.d.ts +1 -1
- package/dist/reputation/api.d.ts +1 -1
- package/dist/revalidate/index.js +2 -2
- package/dist/robots/index.d.ts +5 -5
- package/dist/robots/indexnow.d.ts +2 -3
- package/dist/seo/ManagedScripts.d.ts +2 -2
- package/dist/seo/client.js +4 -4
- package/dist/seo/getManagedMetadata.d.ts +1 -1
- package/dist/seo/groundingSchema.d.ts +2 -2
- package/dist/seo/index.js +4 -4
- package/dist/seo/llms.js +4 -4
- package/dist/seo/register-sitemap-cli-impl.d.ts +1 -1
- package/dist/seo/register-sitemap-cli.d.ts +1 -1
- package/dist/seo/register-sitemap-cli.js +2 -2
- package/dist/seo/schema-filter.d.ts +5 -6
- package/dist/seo/server-api.d.ts +1 -1
- package/dist/seo/sitemap.js +2 -2
- package/dist/seo/types.d.ts +4 -4
- package/dist/seo/withManagedMetadata.d.ts +4 -4
- package/dist/server/index.js +2 -2
- package/dist/server/mint-site-token.d.ts +1 -2
- package/dist/server/server-fetch.d.ts +5 -5
- package/dist/shared/build-entries.d.ts +4 -4
- package/dist/shared/frame.d.ts +8 -9
- package/dist/shared/fresh-fetch.d.ts +1 -2
- package/dist/shared/next-files.d.ts +1 -1
- package/dist/shared/publishCredential.d.ts +1 -1
- package/dist/shared/uuid.d.ts +1 -2
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/visual-viewport-gap.d.ts +2 -2
- package/dist/signal/index.js +2 -2
- package/dist/site-config/index.d.ts +2 -2
- package/dist/sitemap/index.d.ts +13 -13
- package/dist/sitemap/index.js +2 -2
- package/dist/sitemap/shared.d.ts +3 -3
- package/dist/sites/site-param.d.ts +2 -2
- package/dist/slots/ManagedRichText.d.ts +1 -1
- package/dist/slots/ManagedSlot.d.ts +1 -1
- package/dist/slots/index.d.ts +1 -3
- package/dist/sync/index.js +5 -5
- package/dist/types.d.ts +1 -1
- package/dist/{web-vitals.attribution-N2PCCF4Q.js → web-vitals.attribution-GD6LGLVF.js} +1 -1
- package/dist/website/PopupBlocks.d.ts +3 -4
- package/dist/website/images.js +4 -4
- package/dist/website/index.js +5 -5
- package/dist/website/popups.js +4 -4
- package/dist/{writeLLMsTxt-QR23OQUE.js → writeLLMsTxt-G3JBNBC5.js} +1 -1
- package/docs/MIGRATING-TO-7.md +9 -13
- package/package.json +1 -1
- package/src/admin-auth/README.md +3 -3
- package/src/analytics/README.md +7 -9
- package/src/articles/README.md +6 -6
- package/src/cta-bar/README.md +11 -11
- package/src/forms/README.md +10 -8
- package/src/llms/README.md +3 -3
- package/src/mcp/README.md +7 -7
- package/src/motion/README.md +1 -1
- package/src/og/README.md +26 -27
- package/src/proxy/README.md +14 -14
- package/src/seo/README.md +1 -1
- package/src/slots/README.md +1 -1
- package/dist/ManagedForm-2GSSDJPP.js +0 -14
- package/dist/SitemapSync-4U654SEA.js +0 -8
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
* @sonordev/contracts/portfolio — portfolio proof display policy (v1)
|
|
3
3
|
*
|
|
4
4
|
* Single source of truth for the Lighthouse-KPI collapse policy shared by the
|
|
5
|
-
* content side (
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* from `src/portfolio/curate-proof.ts`, which keeps the render-only
|
|
9
|
-
* primitives that bind to its section types.
|
|
10
|
-
* - signal-api imports it through
|
|
11
|
-
* `src/modules/skills/portfolio/portfolio-kpi-policy.util.ts`.
|
|
5
|
+
* content side (Signal's case-study generation) and the render side
|
|
6
|
+
* (agency-site-kit, which imports it as `@sonordev/site-kit/portfolio/contract`
|
|
7
|
+
* and keeps the render-only primitives that bind to its section types).
|
|
12
8
|
*
|
|
13
9
|
* Why it exists: generated case studies arrived with four "100/100 Lighthouse X"
|
|
14
10
|
* KPIs repeated across hero, results cards, and a Performance section. One
|
|
@@ -17,8 +13,8 @@
|
|
|
17
13
|
*
|
|
18
14
|
* It is also the single source of truth for METRIC PROVENANCE (below): the
|
|
19
15
|
* three sources a portfolio number can have, what a client-reported number
|
|
20
|
-
* must carry, and which numbers may headline.
|
|
21
|
-
*
|
|
16
|
+
* must carry, and which numbers may headline. The Sonor API applies the same
|
|
17
|
+
* rules.
|
|
22
18
|
*/
|
|
23
19
|
/** The message a cross-origin-framed site-kit page posts to its embedder once it has painted. */
|
|
24
20
|
export declare const FRAME_READY_MESSAGE: 'sonor:frame-ready';
|
|
@@ -109,11 +105,11 @@ export declare const METRIC_SOURCES: readonly ['measured', 'reported', 'estimate
|
|
|
109
105
|
export type MetricSource = (typeof METRIC_SOURCES)[number];
|
|
110
106
|
/** Who stands behind a client-reported number. */
|
|
111
107
|
export interface MetricAttribution {
|
|
112
|
-
/** The person, e.g. "
|
|
108
|
+
/** The person, e.g. "Dana Reyes". */
|
|
113
109
|
name: string;
|
|
114
|
-
/** Their title, e.g. "Director of
|
|
110
|
+
/** Their title, e.g. "Director of Operations". */
|
|
115
111
|
role?: string;
|
|
116
|
-
/** Where they work: almost always the client, e.g. "
|
|
112
|
+
/** Where they work: almost always the client, e.g. "Northwind Engineering". */
|
|
117
113
|
organization: string;
|
|
118
114
|
/** The day they reported it, YYYY-MM-DD. */
|
|
119
115
|
date: string;
|
|
@@ -140,12 +136,12 @@ export declare function isHeadlineMetric(metric: SourcedMetric): boolean;
|
|
|
140
136
|
/** The badge a display shows, or null for a missing or unknown source (show nothing rather than guess). */
|
|
141
137
|
export declare function metricSourceLabel(source: unknown): string | null;
|
|
142
138
|
/**
|
|
143
|
-
* The full credit: "Reported by
|
|
144
|
-
*
|
|
139
|
+
* The full credit: "Reported by Dana Reyes, Director of Operations, Northwind
|
|
140
|
+
* Engineering, September 25, 2026". The date is read as a plain day,
|
|
145
141
|
* never through a time zone, so it can't slip to the day before.
|
|
146
142
|
*/
|
|
147
143
|
export declare function formatAttribution(by: MetricAttribution): string;
|
|
148
|
-
/** The credit for a tight space (a card, a carousel tile): "per
|
|
144
|
+
/** The credit for a tight space (a card, a carousel tile): "per Northwind Engineering". */
|
|
149
145
|
export declare function shortAttribution(by: Pick<MetricAttribution, 'organization'>): string;
|
|
150
146
|
/**
|
|
151
147
|
* Carry a person's reported numbers through a regeneration. The generator can
|
|
@@ -4,17 +4,16 @@
|
|
|
4
4
|
* Single source of truth for "is this machine-generated seo_pages.managed_title
|
|
5
5
|
* / managed_meta_description fit to ship?". site-kit applies it at render time
|
|
6
6
|
* (getManagedMetadata falls back to the page's own title/description), and
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Signal applies it to every generated title before it's written, so the two
|
|
8
|
+
* can't disagree.
|
|
9
9
|
*
|
|
10
|
-
* Why it exists
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* rendered them verbatim.
|
|
10
|
+
* Why it exists: a meta generator that uses the raw domain as the brand when it
|
|
11
|
+
* can't resolve one ships titles like "Apartments for Rent |
|
|
12
|
+
* maplecourtapts.com", hard-cuts them at 60 chars ("… |
|
|
13
|
+
* brightsideheatingandcooli"), loses the suffix to a word-boundary cut
|
|
14
|
+
* ("Contact Brightside Heating and Cooling |"), or rebuilds a brand out of the
|
|
15
|
+
* domain ("About BBE Llis Apartments | BBE Llis" for bbellisapts.com, when the
|
|
16
|
+
* brand is BB Ellis Apartments). Sites render them verbatim.
|
|
18
17
|
*
|
|
19
18
|
* Every rule derives from the site's own identity (its hosts and brand), so
|
|
20
19
|
* there is no list of bad strings to maintain. Pure string logic — no I/O, no
|
|
@@ -24,7 +23,7 @@
|
|
|
24
23
|
export declare const SEO_META_CONTRACT_VERSION: 1;
|
|
25
24
|
/** Who the site is. Hosts come from the project/site config, the brand from the resolved business name — never from the domain. */
|
|
26
25
|
export interface MetaIdentity {
|
|
27
|
-
/** The site's brand, e.g. "
|
|
26
|
+
/** The site's brand, e.g. "BB Ellis Apartments". */
|
|
28
27
|
brand?: string | null;
|
|
29
28
|
/** Other correct spellings of the brand. */
|
|
30
29
|
brandAliases?: Array<string | null | undefined>;
|
|
@@ -34,7 +33,7 @@ export interface MetaIdentity {
|
|
|
34
33
|
export type TitleProblem = 'empty' | 'placeholder' | 'dangling-separator' | 'hostname' | 'mangled-brand';
|
|
35
34
|
export type DescriptionProblem = 'empty' | 'hostname' | 'mangled-brand' | 'truncated';
|
|
36
35
|
export type SuffixProblem = 'hostname' | 'mangled-brand';
|
|
37
|
-
/** "https://www.
|
|
36
|
+
/** "https://www.MapleCourtApts.com:443/x" → "maplecourtapts.com". Null for anything that isn't a host (no dot, or words). */
|
|
38
37
|
export declare function metaHost(value: string | null | undefined): string | null;
|
|
39
38
|
/** True when the text names the brand (an alias, or either one's core) word for word. */
|
|
40
39
|
export declare function namesBrand(text: string, id: MetaIdentity): boolean;
|
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Single source of truth for choosing ONE seo_pages row when a (project_id,
|
|
5
5
|
* path/url) lookup matches several, and for scoping a project's rows to one
|
|
6
|
-
* host. site-kit (`@sonordev/site-kit/seo/pages/contract`)
|
|
7
|
-
*
|
|
8
|
-
* (`src/shared/utils/seo-page-pick.util.ts`) all import this; none keeps a
|
|
9
|
-
* copy.
|
|
6
|
+
* host. site-kit (`@sonordev/site-kit/seo/pages/contract`) and both Sonor
|
|
7
|
+
* APIs import this; none keeps a copy.
|
|
10
8
|
*
|
|
11
|
-
* seo_pages identity
|
|
12
|
-
* 20260702150000_seo_pages_per_site_identity.sql: a multi-site project (one
|
|
9
|
+
* seo_pages identity is (project_id, site, url): a multi-site project (one
|
|
13
10
|
* Sonor project hosting many domains) keeps one row PER SUB-SITE HOST for
|
|
14
11
|
* shared paths like `/` or `/contact`. Any path/url lookup can therefore match
|
|
15
12
|
* several rows — `.single()`/`.maybeSingle()` error on them (PGRST116) — so
|
|
@@ -26,11 +23,9 @@ export declare const SEO_PAGES_CONTRACT_VERSION: 1;
|
|
|
26
23
|
*
|
|
27
24
|
* Retired pages are kept as tombstones (status='removed') so their history and
|
|
28
25
|
* redirects survive, which means every public read has to opt out of them
|
|
29
|
-
* explicitly.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* was 32% dead URLs, advertising service areas in Atlanta, Detroit and Tampa
|
|
33
|
-
* that the business has never served.
|
|
26
|
+
* explicitly. A read that forgets serves dead URLs: an llms.txt, the file
|
|
27
|
+
* handed straight to AI crawlers, ends up advertising pages the business
|
|
28
|
+
* retired long ago.
|
|
34
29
|
*
|
|
35
30
|
* Null-safe by design: `status` is nullable with an 'active' default, so a row
|
|
36
31
|
* whose status was explicitly nulled stays live rather than vanishing from the
|
|
@@ -80,13 +75,12 @@ export declare function matchSitemapEntryRow<T extends SeoPageRowLike>(candidate
|
|
|
80
75
|
* that belongs to `site`, keeping at most one row per path.
|
|
81
76
|
*
|
|
82
77
|
* Why this exists: a multi-site project (one Sonor project hosting many
|
|
83
|
-
* domains
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* among same-path rows is broken by whatever order Postgres returns.
|
|
78
|
+
* domains, e.g. example.com plus dozens of city microsites) holds one row PER
|
|
79
|
+
* HOST for shared paths like `/` and `/terms`. Any project-wide read that
|
|
80
|
+
* dedupes by path alone therefore keeps an ARBITRARY host's row: example.com's
|
|
81
|
+
* llms.txt can describe `/` with a sibling's homepage copy, and which sibling
|
|
82
|
+
* wins changes every time a neighbour rebuilds, because the tie among
|
|
83
|
+
* same-path rows is broken by whatever order Postgres returns.
|
|
90
84
|
*
|
|
91
85
|
* Rules, per path, via `matchSitemapEntryRow`:
|
|
92
86
|
* - this host's row wins outright
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* How Sonor refreshes a site's cached pages when content changes.
|
|
3
3
|
*
|
|
4
4
|
* A site serves cached pages and caches every fetch from Sonor. When someone
|
|
5
|
-
* edits content in Sonor,
|
|
5
|
+
* edits content in Sonor, Sonor POSTs the paths and cache tags that
|
|
6
6
|
* changed to the site's revalidation route, authenticated with the project's
|
|
7
7
|
* API key, and the site regenerates just those. site-kit tags its fetches with
|
|
8
8
|
* these names and serves the route (`@sonordev/site-kit/revalidate`);
|
|
9
|
-
*
|
|
9
|
+
* Sonor sends them. Single source of truth for the names on both sides:
|
|
10
10
|
* never spell a tag as a string literal in either.
|
|
11
11
|
*/
|
|
12
12
|
/** Where the revalidation route lives on every site. */
|
|
@@ -29,7 +29,7 @@ export declare const SITE_CACHE_TAGS: {
|
|
|
29
29
|
readonly portfolio: 'portfolio';
|
|
30
30
|
};
|
|
31
31
|
export type SiteCacheTag = (typeof SITE_CACHE_TAGS)[keyof typeof SITE_CACHE_TAGS];
|
|
32
|
-
/** The body
|
|
32
|
+
/** The body Sonor POSTs to the revalidation route. */
|
|
33
33
|
export interface SiteRevalidatePayload {
|
|
34
34
|
/** Local paths to regenerate, e.g. `['/services/roofing']`. */
|
|
35
35
|
paths?: string[];
|
|
@@ -39,7 +39,7 @@ export interface SiteRevalidatePayload {
|
|
|
39
39
|
revalidateAll?: boolean;
|
|
40
40
|
/**
|
|
41
41
|
* Only check that the route is installed and the key matches; nothing is
|
|
42
|
-
* regenerated.
|
|
42
|
+
* regenerated. Sonor pings after it registers a site's route, so it
|
|
43
43
|
* knows whether an edit goes live in seconds or waits for the cache to age out.
|
|
44
44
|
*/
|
|
45
45
|
ping?: boolean;
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
* pushed those entries over their bundle budgets.
|
|
15
15
|
*
|
|
16
16
|
* `./sites` re-exports this, so `@sonordev/contracts/sites` (and site-kit's
|
|
17
|
-
* `sites/contract`) carry it too. The
|
|
18
|
-
* (sonor-api `src/common/utils/site-host.ts`, signal-api
|
|
19
|
-
* `src/shared/utils/seo-page-pick.util.ts`).
|
|
17
|
+
* `sites/contract`) carry it too. The Sonor APIs import it from there.
|
|
20
18
|
*
|
|
21
19
|
* Pure string logic only (no Node built-ins) so it is safe in the browser.
|
|
22
20
|
*/
|
|
@@ -24,7 +22,7 @@
|
|
|
24
22
|
* normalizeSiteHost — canonical normalizer for a sub-site host value before it
|
|
25
23
|
* is persisted or used as a filter.
|
|
26
24
|
*
|
|
27
|
-
* `https://
|
|
25
|
+
* `https://Ohio.Example.com:3000/path` → `ohio.example.com`
|
|
28
26
|
*
|
|
29
27
|
* Lowercases, strips protocol, keeps host only (drops path + query), strips
|
|
30
28
|
* port (rollups span dev/prod) and the FQDN root dot. Returns null for
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
* @sonordev/contracts/sites — Multi-site "site" dimension host contract (v1)
|
|
3
3
|
*
|
|
4
4
|
* Single source of truth for normalizing and comparing sub-site host values
|
|
5
|
-
* (the multi-site `site` dimension
|
|
6
|
-
*
|
|
7
|
-
* (`src/shared/utils/seo-page-pick.util.ts`) all import it, so there's no
|
|
8
|
-
* copy to drift.
|
|
5
|
+
* (the multi-site `site` dimension). site-kit and both Sonor APIs import it,
|
|
6
|
+
* so there's no copy to drift.
|
|
9
7
|
*
|
|
10
8
|
* Pure string logic only (no Node built-ins) so it is safe in the browser:
|
|
11
9
|
* site-kit itself normalizes `__SITE_KIT_SITE__` with `normalizeSiteHost`.
|
|
@@ -28,7 +26,7 @@ export declare function stripWww(host: string): string;
|
|
|
28
26
|
*/
|
|
29
27
|
export declare function sameSite(a: string | null | undefined, b: string | null | undefined): boolean;
|
|
30
28
|
/**
|
|
31
|
-
* Host of a full URL ("https://
|
|
29
|
+
* Host of a full URL ("https://ohio.example.com/x" → "ohio.example.com"),
|
|
32
30
|
* null for site-relative paths ("/services") and unparseable input.
|
|
33
31
|
*/
|
|
34
32
|
export declare function hostFromUrl(value: string | null | undefined): string | null;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The pure half of the slots contract: content types, value validation
|
|
5
5
|
* (normalizeSlotValue), safe links, and the rich-text subset parser. No
|
|
6
6
|
* crypto, so the dashboard can import it in the browser to validate and
|
|
7
|
-
* preview exactly what
|
|
7
|
+
* preview exactly what the Sonor API will accept and site-kit will render.
|
|
8
8
|
* `@sonordev/contracts/slots` re-exports all of it next to the signing.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
@@ -66,7 +66,7 @@ export type SlotValueResult = {
|
|
|
66
66
|
/**
|
|
67
67
|
* Validate and normalize what an editor (or Signal) submits for a slot of
|
|
68
68
|
* `type`, returning the canonical `content` + `value` to store and sign.
|
|
69
|
-
* The single rule for "what a valid slot of this type is":
|
|
69
|
+
* The single rule for "what a valid slot of this type is": the Sonor API runs it
|
|
70
70
|
* on every write and verifySlotContent re-runs it on every read.
|
|
71
71
|
*/
|
|
72
72
|
export declare function normalizeSlotValue(type: SlotContentType, input: {
|
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Single source of truth for the slot payload shape, value validation, the
|
|
5
5
|
* rich-text subset, and content signing, shared by site-kit (verify + render),
|
|
6
|
-
*
|
|
6
|
+
* the Sonor API (validate + sign) and the dashboard (preview). Node only: it signs
|
|
7
7
|
* with `crypto`.
|
|
8
8
|
*
|
|
9
9
|
* Security model: every SlotContent is HMAC-signed with the project API key.
|
|
10
10
|
* site-kit verifies before rendering and falls back to the developer's static
|
|
11
11
|
* content on any mismatch, so platform-originated content carries provenance
|
|
12
|
-
* end to end.
|
|
12
|
+
* end to end.
|
|
13
13
|
*
|
|
14
14
|
* v1 (site-kit ≤ 7.0) is plain text only. v2 adds rich text, links and lists,
|
|
15
|
-
* and signs the structured value too.
|
|
15
|
+
* and signs the structured value too. Sonor serves both: a v1 request gets
|
|
16
16
|
* text slots signed the v1 way and null for everything else.
|
|
17
17
|
*/
|
|
18
18
|
import { type SlotContentType, type SlotValue } from './slot-content';
|
|
19
19
|
export * from './slot-content';
|
|
20
20
|
/** The version this package speaks. Bump on breaking payload changes. */
|
|
21
21
|
export declare const SLOTS_CONTRACT_VERSION = 2;
|
|
22
|
-
/** Versions
|
|
22
|
+
/** Versions Sonor still serves. */
|
|
23
23
|
export declare const SUPPORTED_SLOTS_CONTRACT_VERSIONS: readonly number[];
|
|
24
24
|
export interface SlotContent {
|
|
25
25
|
slot_id: string;
|
|
@@ -63,7 +63,7 @@ export interface SlotResolveRequestEntry {
|
|
|
63
63
|
export interface SlotsResolveRequest {
|
|
64
64
|
contract_version: number;
|
|
65
65
|
slots: SlotResolveRequestEntry[];
|
|
66
|
-
/** Multi-site sub-dimension: normalized lowercase host
|
|
66
|
+
/** Multi-site sub-dimension: normalized lowercase host. */
|
|
67
67
|
site?: string;
|
|
68
68
|
}
|
|
69
69
|
export interface SlotsResolveResponse {
|
|
@@ -83,7 +83,7 @@ type Signable = Pick<SlotContent, 'slot_id' | 'type' | 'content'> & {
|
|
|
83
83
|
*/
|
|
84
84
|
export declare function slotSigningPayload(content: Signable, version?: number): string;
|
|
85
85
|
/**
|
|
86
|
-
* Sign slot content with the project API key (
|
|
86
|
+
* Sign slot content with the project API key (Sonor's side).
|
|
87
87
|
* Returns the hex HMAC-SHA256 signature.
|
|
88
88
|
*/
|
|
89
89
|
export declare function signSlotContent(content: Signable, apiKey: string, version?: number): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stands in for every character that isn't prose (code, quotations, link
|
|
3
|
+
* destinations). It's neither whitespace nor a word character, so a masked
|
|
4
|
+
* span still separates words and no pattern can match it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const VOICE_MASK = "\u2800";
|
|
7
|
+
export interface VoiceHit {
|
|
8
|
+
/** UTF-16 offset into the whole string you passed in, never line-relative. */
|
|
9
|
+
readonly index: number;
|
|
10
|
+
/** Length of the span to replace. May include a run of spaces or tabs. */
|
|
11
|
+
readonly length: number;
|
|
12
|
+
/** The text at [index, index + length) of the string you passed in. */
|
|
13
|
+
readonly match: string;
|
|
14
|
+
/** What to put there, first-letter case preserved. Always a straight apostrophe. */
|
|
15
|
+
readonly suggestion: string;
|
|
16
|
+
}
|
|
17
|
+
export interface VoiceOptions {
|
|
18
|
+
/**
|
|
19
|
+
* The text is a heading or title, so one that opens with "What Is" keeps it.
|
|
20
|
+
* Applies to every line. Pass the heading text, not the Markdown line: `#`
|
|
21
|
+
* and list markers must already be blanked.
|
|
22
|
+
*/
|
|
23
|
+
readonly titleLike?: boolean | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Half-open [start, end) UTF-16 ranges to leave alone, computed from the
|
|
26
|
+
* exact string passed in. Any order, overlaps and touching ranges allowed.
|
|
27
|
+
* Start is floored and clamped to 0 (so -Infinity means "from the start"),
|
|
28
|
+
* end is ceiled and clamped to the text length (so Infinity means "to the
|
|
29
|
+
* end"), and an empty or out-of-text range is ignored. Every protected
|
|
30
|
+
* character is masked except "\n" and "\r", which stay as they are (a
|
|
31
|
+
* protected line break still ends its line). A malformed range (NaN, a
|
|
32
|
+
* non-number, or start after end) or a truthy value that isn't an array
|
|
33
|
+
* protects the whole text: protection never fails open. A falsy value means
|
|
34
|
+
* no ranges.
|
|
35
|
+
*/
|
|
36
|
+
readonly protectedRanges?: ReadonlyArray<readonly [number, number]> | undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Full forms the house voice contracts ("do not" -> "don't"). Read one line at
|
|
40
|
+
* a time: split on "\n"; a "\r" right before it, or at the very end of the
|
|
41
|
+
* text, ends the line with it; any other "\r" is text.
|
|
42
|
+
* Sorted by index, never overlapping, [] for ''.
|
|
43
|
+
*/
|
|
44
|
+
export declare function findUncontracted(text: string, options?: VoiceOptions): VoiceHit[];
|
|
45
|
+
/**
|
|
46
|
+
* `text` with every findUncontracted hit applied. Returns `text` itself when
|
|
47
|
+
* nothing applies, and for anything that isn't a string. Protected characters
|
|
48
|
+
* are never rewritten.
|
|
49
|
+
*/
|
|
50
|
+
export declare function contractText(text: string, options?: VoiceOptions): string;
|
|
51
|
+
/**
|
|
52
|
+
* Contractions a question-form heading or FAQ question must expand
|
|
53
|
+
* ("What's" -> "What is"). Sorted by index, never overlapping. `titleLike` is
|
|
54
|
+
* not part of this direction.
|
|
55
|
+
*/
|
|
56
|
+
export declare function findContracted(text: string, options?: Pick<VoiceOptions, 'protectedRanges'>): VoiceHit[];
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* SINGLE SOURCE OF TRUTH shared by every side of the Website module's popups:
|
|
6
6
|
* - the site (site-kit measures its own rendered design and reports it,
|
|
7
7
|
* publishes it as `--sk-site-*` CSS variables, and renders popup blocks),
|
|
8
|
-
* -
|
|
8
|
+
* - the Sonor API (validates the design report and every popup write with these
|
|
9
9
|
* normalizers, so a popup can't be saved in a shape the site can't draw),
|
|
10
10
|
* - the dashboard (the builder's preview reads the same tokens through the
|
|
11
11
|
* same CSS-variable mapping, so it looks like the site),
|
|
12
12
|
* - Signal's agents (the block shapes an MCP `propose_site_popup` sends).
|
|
13
13
|
*
|
|
14
14
|
* All four import it: site-kit as `@sonordev/site-kit/website/contract`,
|
|
15
|
-
*
|
|
15
|
+
* the API and the dashboard as `@sonordev/contracts/website`.
|
|
16
16
|
*
|
|
17
17
|
* Why popups carry blocks and the site carries tokens: a popup should look
|
|
18
18
|
* like the site it's on. It says WHAT (a heading, some formatted text, an
|
|
@@ -17,7 +17,7 @@ interface ChatWidgetProps {
|
|
|
17
17
|
projectId?: string;
|
|
18
18
|
config?: Partial<ChatConfig>;
|
|
19
19
|
apiUrl?: string;
|
|
20
|
-
/** Signal API base URL for direct AI chat (no
|
|
20
|
+
/** Signal API base URL for direct AI chat (no Sonor passthrough) */
|
|
21
21
|
signalUrl?: string;
|
|
22
22
|
/** Clickable question chips shown in the welcome screen before the first message */
|
|
23
23
|
suggested_questions?: string[];
|
|
@@ -65,7 +65,7 @@ interface WidgetConfigFromApi {
|
|
|
65
65
|
* Sonor decides this and sends it as `is_enabled` on `GET /engage/widget/config`:
|
|
66
66
|
* the Engage "Enable Chat Widget" switch (a project that never saved chat
|
|
67
67
|
* settings counts as on) and the project's Engage module, resolved once
|
|
68
|
-
* server-side
|
|
68
|
+
* server-side. Only an explicit `false` hides
|
|
69
69
|
* Echo, which also keeps an API that predates the field showing it. `null`
|
|
70
70
|
* means the config hasn't arrived or couldn't be fetched, and the chat can't
|
|
71
71
|
* run without it either.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @sonordev/site-kit/engage - Echo UI Actions
|
|
3
3
|
*
|
|
4
4
|
* Renders the optional `uiActions` the visitor Echo concierge returns alongside
|
|
5
|
-
* a chat reply
|
|
5
|
+
* a chat reply. Rendered beneath the assistant
|
|
6
6
|
* message in ChatWidget, on-brand with the widget's primary-color tokens and
|
|
7
7
|
* the brand foreground colours from brand-color.ts.
|
|
8
8
|
*
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* forms `useForm` fetch/validate/submit path) with optional prefill.
|
|
11
11
|
* - suggest_action → clickable chip; sends the message as the next turn, or
|
|
12
12
|
* navigates when the action is a path/URL.
|
|
13
|
-
* - show_portfolio_item → intentionally ignored
|
|
14
|
-
*
|
|
13
|
+
* - show_portfolio_item → intentionally ignored: the generic widget has no
|
|
14
|
+
* portfolio view; a site with its own chat UI handles it.
|
|
15
15
|
*/
|
|
16
16
|
import React from 'react';
|
|
17
17
|
import type { UiAction } from './types';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @sonordev/site-kit/engage - Engage Widget
|
|
3
3
|
*
|
|
4
|
-
* Loads and renders engagement widgets (popups, nudges, bars, chat) via
|
|
4
|
+
* Loads and renders engagement widgets (popups, nudges, bars, chat) via Sonor API
|
|
5
5
|
* Supports both legacy config-based rendering and new design_json from Engage Studio
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react';
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* when it already reads, otherwise the brand pulled toward black (light
|
|
15
15
|
* panel) or white (dark panel) just far enough to clear WCAG AA.
|
|
16
16
|
*
|
|
17
|
-
* Why: a light brand (
|
|
17
|
+
* Why: a light brand (a #d4af37 gold, any yellow) drawn as text
|
|
18
18
|
* on the ~86% white panel sat at about 2:1.
|
|
19
19
|
*/
|
|
20
20
|
/** Light enough that text and icons on a fill of it should be dark. Unreadable colours count as dark. */
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @sonordev/site-kit/engage - the rules EngageWidget applies to popups,
|
|
3
3
|
* banners and toasts, as pure functions (the package's tests have no DOM).
|
|
4
4
|
*
|
|
5
|
-
* Sonor validates every popup before it's published
|
|
6
|
-
*
|
|
5
|
+
* Sonor validates every popup before it's published, so these are the
|
|
6
|
+
* second check: a link the kit
|
|
7
7
|
* would follow or an image it would load must be safe here too, whatever the
|
|
8
8
|
* API sent.
|
|
9
9
|
*/
|
package/dist/engage/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
export { ChatWidget } from '../chunk-
|
|
2
|
+
export { ChatWidget } from '../chunk-RHNWA34F.js';
|
|
3
3
|
import '../chunk-JE4A5S4F.js';
|
|
4
|
-
export { DesignRenderer, EngageWidget } from '../chunk-
|
|
4
|
+
export { DesignRenderer, EngageWidget } from '../chunk-YCJT4JJG.js';
|
|
5
5
|
import '../chunk-NDF4A5JM.js';
|
|
6
6
|
import '../chunk-6T6CQVNL.js';
|
|
7
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-V6FDQLR7.js';
|
|
8
8
|
import '../chunk-AQSNPZY4.js';
|
|
9
9
|
import '../chunk-5SQK3D53.js';
|
|
10
10
|
import '../chunk-NJCTQH2P.js';
|
|
@@ -14,9 +14,9 @@ import '../chunk-L2V5PUFN.js';
|
|
|
14
14
|
import '../chunk-GJWI74ZZ.js';
|
|
15
15
|
import '../chunk-43OCZ3JA.js';
|
|
16
16
|
import '../chunk-EKBEOXTH.js';
|
|
17
|
-
import '../chunk-
|
|
18
|
-
import '../chunk-
|
|
19
|
-
import '../chunk-
|
|
17
|
+
import '../chunk-3SDOQUUF.js';
|
|
18
|
+
import '../chunk-VDF5DFWT.js';
|
|
19
|
+
import '../chunk-AH5Q262S.js';
|
|
20
20
|
import '../chunk-PKBMQBKP.js';
|
|
21
21
|
|
|
22
22
|
// src/engage/echo-config.ts
|
package/dist/engage/types.d.ts
CHANGED
|
@@ -197,12 +197,12 @@ export interface ChatConfig extends ChatLauncherPlacement {
|
|
|
197
197
|
* the JSON response alongside `content` / `conversationId`. The chat widget
|
|
198
198
|
* renders each as an inline card or chip beneath the assistant message.
|
|
199
199
|
*
|
|
200
|
-
* This mirrors the shape
|
|
201
|
-
*
|
|
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
202
|
*
|
|
203
203
|
* - `show_form` → data.formSlug (+ optional data.prefill) — offer the managed form inline.
|
|
204
204
|
* - `suggest_action` → data.action + data.message — clickable chip (send message or navigate).
|
|
205
|
-
* - `show_portfolio_item` → data.slug —
|
|
205
|
+
* - `show_portfolio_item` → data.slug — the generic widget ignores it (a site's own chat UI handles it).
|
|
206
206
|
*/
|
|
207
207
|
export type UiActionType = 'show_form' | 'suggest_action' | 'show_portfolio_item';
|
|
208
208
|
export interface UiAction {
|
|
@@ -9,8 +9,8 @@
|
|
|
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 (
|
|
13
|
-
*
|
|
12
|
+
* Analytics and Engage, so it never enters the critical hydration path (the
|
|
13
|
+
* page must server-render). It renders null and carries no
|
|
14
14
|
* visitor data.
|
|
15
15
|
*/
|
|
16
16
|
import { type AnalyticsGateOptions } from '../shared/reporting-gate';
|
package/dist/fleet/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
export { FLEET_HEARTBEAT_PATH, FleetHeartbeat, detectNextVersion, sendFleetHeartbeat } from '../chunk-
|
|
2
|
+
export { FLEET_HEARTBEAT_PATH, FleetHeartbeat, detectNextVersion, sendFleetHeartbeat } from '../chunk-NK4CSJNP.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-
|
|
8
|
-
import '../chunk-
|
|
9
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-3SDOQUUF.js';
|
|
8
|
+
import '../chunk-VDF5DFWT.js';
|
|
9
|
+
import '../chunk-AH5Q262S.js';
|
|
10
10
|
import '../chunk-PKBMQBKP.js';
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* - a completed field earns its animated checkmark (the existing lock-in
|
|
11
11
|
* tick + ring)
|
|
12
12
|
* - when a whole row completes and you move on, the fields COLLAPSE INTO A
|
|
13
|
-
* SENTENCE the form says back — "Nice to meet you,
|
|
14
|
-
* "We'll follow up at
|
|
13
|
+
* SENTENCE the form says back — "Nice to meet you, Jordan." /
|
|
14
|
+
* "We'll follow up at jordan@… or 555‑0100." — with a drawn check and a
|
|
15
15
|
* little edit button to reopen the row
|
|
16
16
|
* - when the last requirement clears, the submit wakes (existing
|
|
17
17
|
* [data-sk-ready] pop + breathe)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @sonordev/site-kit/forms - Managed Form Component
|
|
3
3
|
*
|
|
4
|
-
* Fetches form config from
|
|
4
|
+
* Fetches form config from Sonor API and renders fields with validation
|
|
5
5
|
*/
|
|
6
6
|
import type { ManagedFormProps } from './types';
|
|
7
7
|
export declare function ManagedForm({ formId, projectId, className, onSuccess, onError, initialConfig, experience, defaultValues, children, }: ManagedFormProps): import("react").JSX.Element;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* whole forms runtime — validation, spotlight, celebration, momentum — as an
|
|
19
19
|
* INITIAL script, on the LCP-critical path.
|
|
20
20
|
*
|
|
21
|
-
* Measured on
|
|
21
|
+
* Measured on a real homepage: 48 KB of form code in the initial chunk
|
|
22
22
|
* set cost 6 Lighthouse points (88 -> 82, LCP 3.8s -> 4.7s). Deferring the
|
|
23
23
|
* client component recovered the score but deleted the form from the HTML,
|
|
24
24
|
* taking the crawlable fields with it. Neither half was acceptable; a
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* drop-off. Lives on the shared tracking session (tracking-session.ts), one per
|
|
7
7
|
* form_analytics row, never one per tracker.
|
|
8
8
|
*
|
|
9
|
-
* Wire shape, per field slug (the API caps these
|
|
10
|
-
*
|
|
9
|
+
* Wire shape, per field slug (the API caps these; keep them in sync with the
|
|
10
|
+
* API's limits):
|
|
11
11
|
* { completed: boolean, focusCount: number, timeMs: number }
|
|
12
12
|
*
|
|
13
13
|
* `completed` isn't stored here. The caller evaluates it at report time against
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `_formLoadedAt` render stamp: when this form appeared in the browser.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* It's part of the evidence Sonor needs that a browser rendered the page
|
|
4
|
+
* before it accepts a submission.
|
|
5
5
|
*
|
|
6
6
|
* Single source of truth for the stamp, used by useForm and FormClient
|
|
7
7
|
* (ManagedForm). It's set on mount, not during render: a Cache Components
|
package/dist/forms/formsApi.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @sonordev/site-kit/forms - Forms API Client
|
|
3
3
|
*
|
|
4
4
|
* Full API client for form management. Allows developers to create, update,
|
|
5
|
-
* and delete forms programmatically. Changes sync to
|
|
5
|
+
* and delete forms programmatically. Changes sync to Sonor automatically.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```tsx
|
|
@@ -97,7 +97,7 @@ export interface CreateFormInput {
|
|
|
97
97
|
slug: string;
|
|
98
98
|
name: string;
|
|
99
99
|
/**
|
|
100
|
-
* Sub-site host this form belongs to (e.g. `
|
|
100
|
+
* Sub-site host this form belongs to (e.g. `ohio.example.com`). Lets
|
|
101
101
|
* one Sonor project host one form per microsite — the dashboard's site
|
|
102
102
|
* picker scopes the Forms list by this value. Optional for project-wide
|
|
103
103
|
* forms. CLI scripts (migrate-contact-form) should derive this from
|
package/dist/forms/index.d.ts
CHANGED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
* Every managed form renders the SPOTLIGHT experience by default: the
|
|
59
59
|
* familiar layout, alive. A glowing ring visits the field you are in, and
|
|
60
60
|
* each completed row collapses into a sentence the form says back —
|
|
61
|
-
* "Nice to meet you,
|
|
61
|
+
* "Nice to meet you, Jordan." — with an edit control to reopen it.
|
|
62
62
|
*
|
|
63
63
|
* Nothing to install, nothing to wire: same fields, same validation, same
|
|
64
64
|
* spam defense. SSR and no-JS render the plain form, so the lead is always
|