@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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +51 -0
- package/README.md +9 -8
- package/agent-manifest.json +12 -4
- package/dist/{AnalyticsProvider-ZB33W6MC.js → AnalyticsProvider-SLFFBRPE.js} +4 -4
- package/dist/{ArticleViewTracker-GMHYLLLZ.js → ArticleViewTracker-TM5AMGN2.js} +3 -3
- package/dist/{BlocksPopup-EA6LYL25.js → BlocksPopup-YJNJIVPO.js} +5 -5
- package/dist/ChatWidget-XKOSH66S.js +17 -0
- package/dist/{FileField-QKA3JB2Q.js → FileField-CI4UL5SH.js} +3 -3
- package/dist/{FormSpotlight-AQTIA5TP.js → FormSpotlight-F2AAEGBN.js} +20 -6
- package/dist/{FormStage-FDTLTP3B.js → FormStage-BI5R5CNC.js} +27 -7
- package/dist/ManagedForm-G6COGHXL.js +16 -0
- package/dist/{ManagedNewsletterForm-J6QSAGGP.js → ManagedNewsletterForm-BO3WCGXX.js} +7 -5
- package/dist/{SignalCore-JZNXOLNT.js → SignalCore-ZA6WHKBR.js} +3 -3
- package/dist/SiteChat-YJADDTKG.js +5 -0
- package/dist/{SiteDesignReporter-W4EJUSTE.js → SiteDesignReporter-5YAW36AQ.js} +5 -5
- package/dist/SitePopups-ST5PMKZE.js +10 -0
- package/dist/SitemapSync-RJTEBINX.js +8 -0
- package/dist/_client/booking-widget.js +5 -5
- package/dist/affiliates/index.js +3 -3
- package/dist/analytics/index.js +4 -4
- package/dist/analytics/send-gate.d.ts +1 -1
- package/dist/articles/index.js +2 -2
- package/dist/articles/server-ui.js +3 -2
- package/dist/articles/server.js +2 -1
- package/dist/{engage → chat}/ChatWidget.d.ts +7 -6
- package/dist/{engage → chat}/EchoUiActions.d.ts +1 -1
- package/dist/chat/SiteChat.d.ts +25 -0
- package/dist/{engage → chat}/brand-color.d.ts +1 -1
- package/dist/{engage → chat}/chat-messages.d.ts +1 -1
- package/dist/{engage → chat}/echo-config.d.ts +1 -1
- package/dist/chat/index.d.ts +10 -6
- package/dist/chat/index.js +14 -8
- package/dist/{engage → chat}/launcher-placement.d.ts +1 -7
- package/dist/{engage → chat}/socket-loader.d.ts +2 -2
- package/dist/chat/types.d.ts +136 -0
- package/dist/chunk-27FISYK4.js +4 -0
- package/dist/{chunk-LUXVWITO.js → chunk-4NTBQNHA.js} +247 -78
- package/dist/{chunk-V3PUVBME.js → chunk-5V2V5C3Y.js} +2 -4
- package/dist/{chunk-Q4W23JYM.js → chunk-5YCEMV2L.js} +1 -1
- package/dist/{chunk-RHNWA34F.js → chunk-64DKGVEI.js} +41 -7
- package/dist/{chunk-GLHQ3LRO.js → chunk-6U3VLV2C.js} +1 -1
- package/dist/{chunk-WWSHFRDL.js → chunk-BC4Y3S3D.js} +1 -1
- package/dist/{chunk-JSZN6LDZ.js → chunk-BF7TZYC3.js} +38 -31
- package/dist/{chunk-M3YEPKP7.js → chunk-BKY2ZNM7.js} +1 -1
- package/dist/{chunk-AQSNPZY4.js → chunk-C2FZBUSS.js} +80 -1
- package/dist/{chunk-NK4CSJNP.js → chunk-CF5QNFYU.js} +2 -2
- package/dist/{chunk-D5EC5KVO.js → chunk-D5DCBD4Y.js} +2 -2
- package/dist/{chunk-3SDOQUUF.js → chunk-D6ZC7XKZ.js} +2 -2
- package/dist/chunk-EJVQJCG5.js +370 -0
- package/dist/{chunk-AH5Q262S.js → chunk-F24FNCTK.js} +1 -1
- package/dist/{chunk-V6FDQLR7.js → chunk-FR6BYFPC.js} +53 -21
- package/dist/{chunk-UXOGCKL6.js → chunk-GEXT7BTE.js} +1 -1
- package/dist/{chunk-JTU2GOM4.js → chunk-GGORNBSC.js} +18 -6
- package/dist/{chunk-ERXXITG4.js → chunk-HGFAEDG3.js} +1 -1
- package/dist/chunk-HODO5BX5.js +28 -0
- package/dist/chunk-HW7B43E5.js +14 -0
- package/dist/{chunk-CSTQDNGK.js → chunk-IAM3D3RL.js} +43 -24
- package/dist/{chunk-PGMVL4AB.js → chunk-JJ3DELUL.js} +2 -2
- package/dist/{chunk-ILU5I6RW.js → chunk-JMLK6MH7.js} +1 -1
- package/dist/{chunk-YEMPUQVU.js → chunk-JWTQS5F5.js} +2 -2
- package/dist/{chunk-VDF5DFWT.js → chunk-K4ZHAURD.js} +1 -1
- package/dist/chunk-NV72HZ4W.js +45 -0
- package/dist/{chunk-4Y5FWDPM.js → chunk-NZ4RXN3G.js} +55 -7
- package/dist/chunk-OWZ7ATLZ.js +187 -0
- package/dist/{chunk-TXBAOEKG.js → chunk-PKGN32AU.js} +5 -4
- package/dist/chunk-Q6E4OPGG.js +40 -0
- package/dist/{chunk-QEDHRBAT.js → chunk-QL6XNPSD.js} +2 -2
- package/dist/{chunk-JBE46QJZ.js → chunk-R46IQIGK.js} +1 -1
- package/dist/{chunk-6WIQCXOO.js → chunk-RPZ7OQGD.js} +1 -1
- package/dist/{chunk-PHYMNUJU.js → chunk-S54ECQGY.js} +1 -1
- package/dist/{chunk-VGH4MDTU.js → chunk-VHKEF5ZB.js} +1 -1
- package/dist/{chunk-HF57LG73.js → chunk-VO3JYRT5.js} +43 -29
- package/dist/chunk-VWWITFWM.js +300 -0
- package/dist/{chunk-CIG5UL2S.js → chunk-ZTLMPUO6.js} +3 -2
- package/dist/client/index.js +3 -3
- package/dist/commerce/index.js +4 -4
- package/dist/contracts/color.d.ts +1 -1
- package/dist/contracts/entries.d.ts +1 -1
- package/dist/contracts/error-page.d.ts +183 -0
- package/dist/contracts/llms.d.ts +1 -0
- package/dist/contracts/proposal-sitemap.d.ts +130 -0
- package/dist/contracts/schema-placeholders.d.ts +87 -0
- package/dist/engage/EngageWidget.d.ts +13 -12
- package/dist/engage/index.d.ts +10 -9
- package/dist/engage/index.js +31 -35
- package/dist/engage/types.d.ts +19 -244
- package/dist/fleet/FleetHeartbeat.d.ts +1 -1
- package/dist/fleet/index.js +4 -4
- package/dist/forms/FormEnhancer.d.ts +25 -12
- package/dist/forms/ServerForm.d.ts +11 -10
- package/dist/forms/StaticForm.d.ts +3 -1
- package/dist/forms/field-autocomplete.d.ts +28 -0
- package/dist/forms/form-dom-values.d.ts +42 -0
- package/dist/forms/index.js +11 -9
- package/dist/forms/server.js +6 -4
- package/dist/forms/static.js +3 -2
- package/dist/forms/submitForm.d.ts +8 -1
- package/dist/forms/types.d.ts +41 -2
- package/dist/forms/useForm.d.ts +21 -4
- package/dist/forms/webmcp.d.ts +38 -0
- package/dist/images/index.js +4 -4
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -1
- package/dist/layout/SiteKitClientProviders.d.ts +3 -3
- package/dist/layout/SiteKitLayout.d.ts +4 -4
- package/dist/layout/client.d.ts +3 -3
- package/dist/layout/client.js +7 -7
- package/dist/layout/index.js +8 -8
- package/dist/layout/types.d.ts +5 -4
- package/dist/llms/contract.js +1 -1
- package/dist/llms/index.js +6 -6
- package/dist/maps/index.js +3 -3
- package/dist/mcp/index.js +2 -14
- package/dist/mcp/sonor.js +14 -11
- package/dist/revalidate/index.js +2 -2
- package/dist/seo/client.js +4 -4
- package/dist/seo/index.js +15 -8
- package/dist/seo/llms/contract.js +1 -1
- package/dist/seo/llms.js +6 -6
- package/dist/seo/register-sitemap-cli.js +1 -1
- package/dist/seo/sitemap.js +4 -4
- package/dist/server/index.js +2 -2
- package/dist/shared/dialog.d.ts +27 -0
- package/dist/shared/identity.d.ts +1 -1
- package/dist/shared/layers.d.ts +7 -0
- package/dist/shared/mid-form.d.ts +53 -0
- package/dist/shared/reporting-gate.d.ts +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/visual-viewport-gap.d.ts +1 -1
- package/dist/signal/index.js +2 -2
- package/dist/signal/types.d.ts +1 -1
- package/dist/sitemap/index.js +4 -4
- package/dist/{socket-loader-R24ZSRSQ.js → socket-loader-CGIPEG74.js} +1 -1
- package/dist/sync/index.js +5 -5
- package/dist/types.d.ts +3 -1
- package/dist/{web-vitals.attribution-GD6LGLVF.js → web-vitals.attribution-N2PCCF4Q.js} +1 -1
- package/dist/website/BlocksPopup.d.ts +1 -1
- package/dist/website/PopupBlocks.d.ts +5 -2
- package/dist/website/SitePopups.d.ts +25 -0
- package/dist/website/images.js +4 -4
- package/dist/website/index.js +6 -6
- package/dist/website/popup-rules.d.ts +46 -0
- package/dist/website/popup-types.d.ts +113 -0
- package/dist/website/popups.d.ts +2 -6
- package/dist/website/popups.js +6 -14
- package/dist/{writeLLMsTxt-G3JBNBC5.js → writeLLMsTxt-OV24LQVL.js} +3 -3
- package/docs/MIGRATING-TO-7.md +41 -9
- package/docs.json +2 -1
- package/package.json +2 -2
- package/src/analytics/README.md +2 -2
- package/src/articles/README.md +4 -1
- package/src/{engage → chat}/README.md +63 -62
- package/src/forms/README.md +49 -3
- package/src/layout/README.md +8 -5
- package/src/seo/README.md +3 -1
- package/src/sync/README.md +10 -0
- package/src/website/README.md +133 -0
- package/dist/ChatWidget-UCDWMWCW.js +0 -15
- package/dist/EngageWidget-7ZYSK4GE.js +0 -11
- package/dist/ManagedForm-H7BVAH2W.js +0 -14
- package/dist/SitemapSync-XBANGKHZ.js +0 -8
- package/dist/chunk-NDF4A5JM.js +0 -37
- package/dist/chunk-VLARKWZU.js +0 -100
- package/dist/chunk-YCJT4JJG.js +0 -837
- package/dist/engage/DesignRenderer.d.ts +0 -57
- package/dist/engage/element-rules.d.ts +0 -38
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sonordev/contracts/error-page: telling an error page from a page (v1)
|
|
3
|
+
*
|
|
4
|
+
* Three questions every writer of page content and page copy has to answer the
|
|
5
|
+
* same way, so they live here once:
|
|
6
|
+
*
|
|
7
|
+
* 1. Is this stored text a rendered ERROR PAGE rather than the page's own
|
|
8
|
+
* content? (`looksLikeErrorPage`, `usablePageText`)
|
|
9
|
+
* 2. Does this generated COPY say the page itself is missing or unavailable?
|
|
10
|
+
* (`findErrorPageClaim`, `describesErrorPage`)
|
|
11
|
+
* 3. Does any string in a page's managed copy (a title, a keyword list, a
|
|
12
|
+
* JSON-LD block) say so? (`findPageCopyClaim`: question 2 over every
|
|
13
|
+
* string of a record, naming the field that holds it)
|
|
14
|
+
*
|
|
15
|
+
* Why it exists: a page's stored text is the text a visitor's browser (or a
|
|
16
|
+
* build's crawler) rendered. Visit a URL while it's briefly broken and the
|
|
17
|
+
* error page's words are stored as that page's content. Everything generated
|
|
18
|
+
* from that content then describes a working page as broken, and the result
|
|
19
|
+
* is published as the page's title, meta description, social tags, JSON-LD
|
|
20
|
+
* and AI-crawler notes. The outage ends in a deploy; the poisoned copy
|
|
21
|
+
* doesn't, because nothing downstream asks whether its input was ever the
|
|
22
|
+
* page.
|
|
23
|
+
*
|
|
24
|
+
* How the text check works
|
|
25
|
+
*
|
|
26
|
+
* - It reads the START of the text (300 characters), lowercased, with
|
|
27
|
+
* whitespace collapsed, HTML entities decoded and curly quotes made
|
|
28
|
+
* straight. Error copy leads; articles bury it.
|
|
29
|
+
* - The signatures are generic on purpose. Every site words its 404
|
|
30
|
+
* differently and frameworks ship their own ("This page could not be
|
|
31
|
+
* found.", "Application error: a client-side exception has occurred"), so
|
|
32
|
+
* the shape is what's matched, not any one site's copy. They're grouped by
|
|
33
|
+
* family below: not found, server and client errors, unavailable and
|
|
34
|
+
* maintenance, walls.
|
|
35
|
+
* - Stored text is often the page's elements run together with no separator
|
|
36
|
+
* ("404This page could not be found."), so no signature depends on a word
|
|
37
|
+
* boundary around the text it's matching.
|
|
38
|
+
* - A signature that follows a word that DESCRIBES errors rather than
|
|
39
|
+
* showing one ("if something went wrong", "how to fix a 404 error", a
|
|
40
|
+
* quote mark) doesn't count: a help page that names an error isn't one.
|
|
41
|
+
* Navigation is Title Case and prose isn't, so a describing word counts
|
|
42
|
+
* only as it's written in lower case, with only its first letter
|
|
43
|
+
* capitalized ("How to fix..."), or opening the text or a sentence;
|
|
44
|
+
* "Home Blog How To Something went wrong" is still an error page, and the
|
|
45
|
+
* words a menu uses too ("About", "Monitoring", "Alerts for") describe an
|
|
46
|
+
* error only in lower case. A describing word inside the matched phrase
|
|
47
|
+
* counts too, and after a described match the search resumes one character
|
|
48
|
+
* on, so a real error later in the same stretch is still found.
|
|
49
|
+
* - A not-found phrase that sits in a question ("Page not found? Use our site
|
|
50
|
+
* map", "Product not found in your size? Ask us") is a help line, and so is
|
|
51
|
+
* most copy that merely uses the words ("Rare parts can't be found
|
|
52
|
+
* elsewhere": the subject has to be a page, URL, link, file, site or app).
|
|
53
|
+
* - Up to ERROR_PAGE_MAX_WORDS words every check applies, but the loosest
|
|
54
|
+
* ones apply only to the shortest texts: a 404 glued to its copy and
|
|
55
|
+
* "refresh the page and try again" up to 60 words, a dynamic route's own
|
|
56
|
+
* "<name> not found" state up to 40. From there up to 400 words only the
|
|
57
|
+
* prefix-aware check applies: a text written as "<meta description> —
|
|
58
|
+
* <every string on the page>" carries its header and footer strings along,
|
|
59
|
+
* which pushes a rendered error state past the short-text ceiling, so the
|
|
60
|
+
* check looks at how the text opens after the description and a skip link,
|
|
61
|
+
* and an opener followed by "explained" or "how to" is an article. Beyond
|
|
62
|
+
* 400 words the text is prose.
|
|
63
|
+
*
|
|
64
|
+
* `wordCount` semantics: pass the word count of `text` itself. Pass the whole
|
|
65
|
+
* document's count only when `text` is a truncated excerpt of it. A supplied
|
|
66
|
+
* count can only RAISE the count the check uses (an excerpt of a long document
|
|
67
|
+
* is prose however short it is), never lower it: text that is longer than the
|
|
68
|
+
* count you gave is counted by what it holds. A browser's page-wide count
|
|
69
|
+
* (navigation and footer included) is therefore the wrong number for a short
|
|
70
|
+
* error state it scraped: pass the count of the text you pass.
|
|
71
|
+
*
|
|
72
|
+
* How the copy check works
|
|
73
|
+
*
|
|
74
|
+
* `findErrorPageClaim` looks for the copy saying THE PAGE is missing or
|
|
75
|
+
* unavailable ("Page Not Found | Acme", "This URL currently returns a 404",
|
|
76
|
+
* "This listing has been removed", "The page failed to load"). The subject
|
|
77
|
+
* has to be the page, its URL, link or listing, so copy about something
|
|
78
|
+
* unavailable on a working page ("sunset cruises are not available at this
|
|
79
|
+
* time") is left alone, and so is a qualified one ("unavailable in winter").
|
|
80
|
+
* Whitespace is collapsed and each string is read up to 5,000 characters, so
|
|
81
|
+
* hostile whitespace costs nothing. The page's own text exempts a phrase the
|
|
82
|
+
* page really says; a page that is itself an error page exempts nothing.
|
|
83
|
+
*
|
|
84
|
+
* `findPageCopyClaim(fields, pageText?)` is the same check over a record:
|
|
85
|
+
* each value of `fields` is checked if it's a string, and walked for every
|
|
86
|
+
* string it holds if it's an array or an object (8 levels deep, 2,000
|
|
87
|
+
* leaves at most; numbers, booleans and null are ignored). It returns the
|
|
88
|
+
* first claim as `{ field, phrase }`, `field` being the TOP-LEVEL key (the
|
|
89
|
+
* column or property that holds the copy), or null. Use it for a page row's
|
|
90
|
+
* managed title, description, keywords and JSON-LD in one call.
|
|
91
|
+
*
|
|
92
|
+
* Known limits (a text-only check can't close these)
|
|
93
|
+
*
|
|
94
|
+
* - Any error phrase in the first 300 characters of a text of 120 words or
|
|
95
|
+
* fewer reads as an error page, wherever in those 300 characters it sits, unless
|
|
96
|
+
* something describes it (see above): a short page ABOUT errors ("Page not
|
|
97
|
+
* found: how to design a helpful 404 page. Five examples.", or a web
|
|
98
|
+
* agency's line about its "custom 404 error page") can be misread. Texts of
|
|
99
|
+
* 121 to 400 words are only read for how they open, and longer ones are
|
|
100
|
+
* prose. The loosest wordings (a 404 glued to its copy, "refresh the page
|
|
101
|
+
* and try again", "<name> not found") apply only up to 60, 60 and 40 words.
|
|
102
|
+
* - A business whose name is an error phrase ("404 Brewing Co.", "Page Not
|
|
103
|
+
* Found Records") reads as an error page when its name leads the text.
|
|
104
|
+
* - Chrome in front hides the error state from the wordings anchored to the
|
|
105
|
+
* start of the text ("Not Found", "Be right back.", "Error: ...", "Oops!
|
|
106
|
+
* This page...", a permission wall, a dynamic route's "<name> not found").
|
|
107
|
+
* The wordings that hold anywhere in the first 300 characters (a status with
|
|
108
|
+
* its reason, "could not be found", "something went wrong") are what's
|
|
109
|
+
* left. Prefer the page's main content.
|
|
110
|
+
* - The text alone never proves a page failed. Where a call site observed
|
|
111
|
+
* the HTTP status (a browser reports it), require a status of 400 or more
|
|
112
|
+
* as well, and treat this check as the fallback for the sites and the
|
|
113
|
+
* paths that report none.
|
|
114
|
+
* - It reads English. A 404 in another language isn't matched.
|
|
115
|
+
* - Walls and pages served by the host in front of the site (a CDN block, a
|
|
116
|
+
* bot challenge, a parked domain, a default server page, a browser
|
|
117
|
+
* interstitial) never reach the writers this guards, so they aren't
|
|
118
|
+
* matched here.
|
|
119
|
+
* - Copy that claims the page is missing in words outside the closed list
|
|
120
|
+
* below isn't caught; the real protection is that error text never reaches
|
|
121
|
+
* the generator (`usablePageText`).
|
|
122
|
+
*
|
|
123
|
+
* Pure string logic: no I/O, no Node built-ins, no lookbehind (this ships to
|
|
124
|
+
* browsers, and Safari before 16.4 throws on a lookbehind at parse time).
|
|
125
|
+
*/
|
|
126
|
+
/** Bump on breaking changes to what counts as an error page or an error claim. */
|
|
127
|
+
export declare const ERROR_PAGE_CONTRACT_VERSION: 1;
|
|
128
|
+
/**
|
|
129
|
+
* Above this many words, text is real prose even if it mentions a 404: only
|
|
130
|
+
* the prefix-aware check (up to 400 words, see the header) still looks at it.
|
|
131
|
+
*
|
|
132
|
+
* The gate is what makes the check safe: an article about broken links ("How
|
|
133
|
+
* to fix 404 errors on your site") legitimately holds every phrase below and
|
|
134
|
+
* must not be discarded. A rendered error page is short, and this ceiling
|
|
135
|
+
* still separates the two by an order of magnitude.
|
|
136
|
+
*/
|
|
137
|
+
export declare const ERROR_PAGE_MAX_WORDS = 120;
|
|
138
|
+
/**
|
|
139
|
+
* True when this text is a rendered error page rather than page content.
|
|
140
|
+
*
|
|
141
|
+
* @param text the scraped or extracted body text
|
|
142
|
+
* @param wordCount the word count of `text` itself, or the whole document's
|
|
143
|
+
* when `text` is a truncated excerpt of it. It can only raise
|
|
144
|
+
* the count the check uses, never lower it; derived from
|
|
145
|
+
* `text` when absent.
|
|
146
|
+
*/
|
|
147
|
+
export declare function looksLikeErrorPage(text?: string | null, wordCount?: number | null): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* The page's stored text, or null when it's an error page. What a model may
|
|
150
|
+
* be shown as "the page's content".
|
|
151
|
+
*/
|
|
152
|
+
export declare function usablePageText(text?: string | null, wordCount?: number | null): string | null;
|
|
153
|
+
/**
|
|
154
|
+
* The phrase in `copy` that says the page itself is missing or unavailable, or
|
|
155
|
+
* null when the copy is fine.
|
|
156
|
+
*
|
|
157
|
+
* @param pageText the page's own text. A phrase the page really says (an
|
|
158
|
+
* article about fixing "page not found" errors) is allowed;
|
|
159
|
+
* only copy that claims what the page doesn't is a finding.
|
|
160
|
+
* A pageText that is itself an error page allows nothing: its
|
|
161
|
+
* words are the poison, so the copy that repeats them would
|
|
162
|
+
* otherwise vouch for itself.
|
|
163
|
+
*/
|
|
164
|
+
export declare function findErrorPageClaim(copy?: string | null, pageText?: string | null): string | null;
|
|
165
|
+
/** True when `copy` says the page itself is missing or unavailable (see `findErrorPageClaim`). */
|
|
166
|
+
export declare function describesErrorPage(copy?: string | null, pageText?: string | null): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* The first string anywhere in a record of managed copy that says the page
|
|
169
|
+
* itself is missing or unavailable, or null. `findErrorPageClaim` over every
|
|
170
|
+
* string of `fields`, and the record's own field name with the phrase.
|
|
171
|
+
*
|
|
172
|
+
* Each value is checked if it's a string, and walked for the strings it holds
|
|
173
|
+
* if it's an array or an object (a JSON-LD block, a keyword list), up to 8
|
|
174
|
+
* levels deep, 2,000 strings and 100,000 characters in all; numbers, booleans and
|
|
175
|
+
* null are ignored (and don't count toward those limits).
|
|
176
|
+
* `field` is the TOP-LEVEL key, the column or property that holds the copy.
|
|
177
|
+
* `pageText` exempts what the page really says, as in `findErrorPageClaim`; the
|
|
178
|
+
* page's own text is read once for the whole record.
|
|
179
|
+
*/
|
|
180
|
+
export declare function findPageCopyClaim(fields: object, pageText?: string | null): {
|
|
181
|
+
field: string;
|
|
182
|
+
phrase: string;
|
|
183
|
+
} | null;
|
package/dist/contracts/llms.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface LLMsPayloadMeta {
|
|
|
28
28
|
/** Optional single-line, public-safe note in the llms.txt blockquote (e.g. scope / not legal advice). */
|
|
29
29
|
llms_disclaimer?: string | null;
|
|
30
30
|
}
|
|
31
|
+
export declare function clipAtWordBoundary(text: string, max: number): string;
|
|
31
32
|
/**
|
|
32
33
|
* Public-safe summary for llms.txt `[title](url): notes` — never CRM-only or internal copy.
|
|
33
34
|
*/
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sonordev/contracts/proposal-sitemap — how a proposal's site plan is counted (v1)
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the numbers a website proposal's site plan
|
|
5
|
+
* (the `SitemapPlan` block) states: how many pages the build has, when the
|
|
6
|
+
* "before → after" comparison may show, which custom labels the block may
|
|
7
|
+
* carry, and whether page counts written elsewhere in the proposal agree with
|
|
8
|
+
* the plan. The proposal generator stamps them, the Sonor API checks them and
|
|
9
|
+
* the dashboard renders them, so the headline, the plan and the price can't
|
|
10
|
+
* each state a different number.
|
|
11
|
+
*
|
|
12
|
+
* Why it exists: the numbers in a plan are what a buyer reads first, and a
|
|
13
|
+
* count written by a language model can't be trusted to add up. A rebuild
|
|
14
|
+
* that keeps every page must never read as a loss ("24 → 0 pages"), one
|
|
15
|
+
* address listed twice must not count twice, and the headline must not state
|
|
16
|
+
* a number the plan doesn't. Counts are computed here, never taken from prose.
|
|
17
|
+
*/
|
|
18
|
+
/** Bump on breaking changes to how a plan is counted. */
|
|
19
|
+
export declare const PROPOSAL_SITEMAP_CONTRACT_VERSION: 1;
|
|
20
|
+
/**
|
|
21
|
+
* A count the plan states: a non-negative whole number, as a number or a
|
|
22
|
+
* numeric string. Anything else is no count at all (undefined), never 0.
|
|
23
|
+
*/
|
|
24
|
+
export declare function sitemapCount(v: unknown): number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The address a plan row stands for, in one shape: lowercase, no scheme or
|
|
27
|
+
* host, no query or fragment, a leading slash and no trailing one ("/" for the
|
|
28
|
+
* home page). Undefined for a row without a slug; such a row always counts,
|
|
29
|
+
* since nothing proves it repeats another.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sitemapPageKey(slug: unknown): string | undefined;
|
|
32
|
+
export interface SitemapPlanCounts {
|
|
33
|
+
/** Core pages (Home, About, Contact and the like), each address once. */
|
|
34
|
+
core: number;
|
|
35
|
+
/**
|
|
36
|
+
* The plan's own pages: top-level pages plus the pages under them, minus
|
|
37
|
+
* any address already counted as a core page.
|
|
38
|
+
*/
|
|
39
|
+
architecture: number;
|
|
40
|
+
/** Existing articles re-published with the build (the plan's `blog.count`). */
|
|
41
|
+
articles: number;
|
|
42
|
+
/** Everything the client gets: core + architecture + articles. */
|
|
43
|
+
full: number;
|
|
44
|
+
/**
|
|
45
|
+
* Pages that exist today and carry over (status rebuild, optimize or
|
|
46
|
+
* migrate, plus the re-published articles).
|
|
47
|
+
*/
|
|
48
|
+
existing: number;
|
|
49
|
+
/** Pages the build adds (status new, or no status). */
|
|
50
|
+
added: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Count a site plan's pages, each address once. Core pages are counted
|
|
54
|
+
* first, then each top-level page and the pages under it; a row whose
|
|
55
|
+
* address was already counted is skipped. Rows without a slug always count.
|
|
56
|
+
*/
|
|
57
|
+
export declare function countSitemapPlan(props: unknown): SitemapPlanCounts;
|
|
58
|
+
export interface SitemapTransformationCounts {
|
|
59
|
+
/** Today's count, as the plan states it. */
|
|
60
|
+
before: number;
|
|
61
|
+
/** The build's count. */
|
|
62
|
+
after: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The before → after comparison a plan may show, or null when it must not
|
|
66
|
+
* show. It shows only when the plan states today's count and the build is
|
|
67
|
+
* bigger. A missing after-count falls back to the plan's own pages (then its
|
|
68
|
+
* `totalPages`); an after-count of zero, or one no bigger than today's, hides
|
|
69
|
+
* the comparison. A rebuild that keeps the same pages isn't a before and after.
|
|
70
|
+
*/
|
|
71
|
+
export declare function sitemapTransformation(props: unknown): SitemapTransformationCounts | null;
|
|
72
|
+
/**
|
|
73
|
+
* Words a plan uses for its own pages, for a business whose pages aren't
|
|
74
|
+
* services sold to industries (communities and towns, practice areas,
|
|
75
|
+
* locations). Each replaces one default: `architecture` the heading
|
|
76
|
+
* ("Service Architecture"), `pillar` a top-level page ("Service pillar"),
|
|
77
|
+
* `child` a page under it ("Industry page"). Singular, title case.
|
|
78
|
+
*/
|
|
79
|
+
export interface SitemapPlanLabels {
|
|
80
|
+
architecture?: string;
|
|
81
|
+
pillar?: string;
|
|
82
|
+
child?: string;
|
|
83
|
+
}
|
|
84
|
+
/** The longest a label may be. */
|
|
85
|
+
export declare const SITEMAP_LABEL_MAX = 40;
|
|
86
|
+
/**
|
|
87
|
+
* Keep only usable labels: strings of 1 to {@link SITEMAP_LABEL_MAX}
|
|
88
|
+
* characters on one line, whitespace collapsed, no angle brackets. Undefined
|
|
89
|
+
* when none survive, so a plan without labels reads the defaults.
|
|
90
|
+
*/
|
|
91
|
+
export declare function normalizeSitemapLabels(raw: unknown): SitemapPlanLabels | undefined;
|
|
92
|
+
export type PageCountUnit = 'page' | 'url';
|
|
93
|
+
export interface PageCountClaim {
|
|
94
|
+
/** The number stated. */
|
|
95
|
+
value: number;
|
|
96
|
+
/** Whether it counts pages or URLs. */
|
|
97
|
+
unit: PageCountUnit;
|
|
98
|
+
/** The words that make the claim, e.g. "27 existing URLs". */
|
|
99
|
+
text: string;
|
|
100
|
+
/** Where the claim starts in the text. */
|
|
101
|
+
index: number;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Every page or URL count stated in a piece of text. A number that's part of
|
|
105
|
+
* a name ("Troy 7 Apartments get pages of their own") isn't a count.
|
|
106
|
+
*/
|
|
107
|
+
export declare function findPageCountClaims(text: unknown): PageCountClaim[];
|
|
108
|
+
export interface PageCountIssue {
|
|
109
|
+
/** The block the claim is in, e.g. "GlassHero". */
|
|
110
|
+
section: string;
|
|
111
|
+
/** Where in the block, e.g. "stats[0]" or "tiers[0].features[1]". */
|
|
112
|
+
field: string;
|
|
113
|
+
/** The number stated. */
|
|
114
|
+
claimed: number;
|
|
115
|
+
unit: PageCountUnit;
|
|
116
|
+
/** The words that make the claim. */
|
|
117
|
+
text: string;
|
|
118
|
+
/** The page count the plan adds up to. */
|
|
119
|
+
planned: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Page and URL counts a proposal states that match nothing its site plan or
|
|
123
|
+
* its measured evidence adds up to. A stated count passes when it equals any
|
|
124
|
+
* of: the plan's full, core, own, article, carried-over or added page count;
|
|
125
|
+
* the plan's stated before-count; or the measured URL and page counts the
|
|
126
|
+
* evidence blocks carry. Empty when the proposal has no site plan.
|
|
127
|
+
*/
|
|
128
|
+
export declare function proposalPageCountIssues(sections: unknown): PageCountIssue[];
|
|
129
|
+
/** One plain sentence for a person reviewing the proposal before it's sent. */
|
|
130
|
+
export declare function describePageCountIssue(issue: PageCountIssue): string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-LD template placeholders: found once, dropped wherever sonor-api serves
|
|
3
|
+
* stored schema to a site, and never stored as implemented by register-schema.
|
|
4
|
+
*
|
|
5
|
+
* Stored JSON-LD (seo_schema_markup.schema_json, seo_pages.managed_schema) can
|
|
6
|
+
* carry a template's unfilled slots. Schema extracted from a site's source,
|
|
7
|
+
* where the base URL was a variable, comes back as https://example.com; an AI
|
|
8
|
+
* filling a template leaves "Example", "+1-000-000-0000", "[Resident Name]",
|
|
9
|
+
* "{plan.name}", or an object that is only a "note" saying what goes there.
|
|
10
|
+
* site-kit renders all of it on the live page, where search engines and AI
|
|
11
|
+
* crawlers take it as the business's identity. Observed 2026-10-01: a live
|
|
12
|
+
* site served an Organization named "Example", phone +1-000-000-0000, on its
|
|
13
|
+
* home page, and dozens more of its implemented rows carried example.com URLs
|
|
14
|
+
* and unfilled template slots.
|
|
15
|
+
*
|
|
16
|
+
* Conservative on purpose, because a false positive removes real schema from a
|
|
17
|
+
* live site:
|
|
18
|
+
* - Domains: only the reserved example domains (example.com, example.net,
|
|
19
|
+
* example.org, their subdomains, and the .example TLD), and only when the
|
|
20
|
+
* whole value is a URL, host or email address. A real domain with
|
|
21
|
+
* "example" in its path, or prose that mentions example.com, is fine.
|
|
22
|
+
* - Names: whole-value matches only (trimmed, case-insensitive) on name-like
|
|
23
|
+
* properties. "Example Plumbing Co" is a real name.
|
|
24
|
+
* - Phones (telephone, faxNumber, tel: links): all zeros, 123-456-7890,
|
|
25
|
+
* XXX-XXX-XXXX, and 555-0100 through 555-0199, the North American range
|
|
26
|
+
* reserved for fiction. 555-0200 and 555-1212 are real numbers.
|
|
27
|
+
* - Template slots: a value that is wholly a bracketed slot ("[YYYY-MM-DD]")
|
|
28
|
+
* or a JSX expression ("{plan.name}"), a {{mustache}} slot, a
|
|
29
|
+
* REPLACE_WITH_ marker, a title-case field name in brackets inside text
|
|
30
|
+
* ("Call [Phone Number]"), or a JSX member expression inside a URL. A URI
|
|
31
|
+
* template's {search_term_string} is how schema.org spells a SearchAction
|
|
32
|
+
* and is never a finding.
|
|
33
|
+
* - Annotations: an object that holds nothing but an AI note. A note key on
|
|
34
|
+
* a real node is stripped (it isn't schema.org vocabulary) and the node
|
|
35
|
+
* stays.
|
|
36
|
+
*
|
|
37
|
+
* The unit is the NODE: the innermost object with @type or @id (or a top-level
|
|
38
|
+
* or @graph member) whose own values hold the placeholder. A placeholder node
|
|
39
|
+
* is dropped whole, since its other values came from the same unfilled
|
|
40
|
+
* template. Its real siblings, and a real parent it hangs off, stay: an
|
|
41
|
+
* FAQPage keeps its questions when only its publisher was a placeholder. A
|
|
42
|
+
* node left with nothing but JSON-LD keywords once its placeholder children
|
|
43
|
+
* are gone (a BreadcrumbList whose every item was a placeholder) goes too, so
|
|
44
|
+
* the site falls back to what it generates itself.
|
|
45
|
+
*
|
|
46
|
+
* Single source of truth for "is this stored schema a placeholder", published
|
|
47
|
+
* as `@sonordev/contracts/schema-placeholders`: sonor-api's public schema reads
|
|
48
|
+
* and register-schema, and site-kit's ManagedSchema and LLMSchema (so a site
|
|
49
|
+
* drops a placeholder whichever Sonor source served it) all call it. Add new
|
|
50
|
+
* shapes here, with a test, never in a caller.
|
|
51
|
+
*/
|
|
52
|
+
export type SchemaPlaceholderReason = 'example_domain' | 'placeholder_phone' | 'placeholder_name' | 'template_token' | 'annotation' | 'emptied';
|
|
53
|
+
export interface SchemaPlaceholderEvidence {
|
|
54
|
+
reason: SchemaPlaceholderReason;
|
|
55
|
+
/** The property that held the value. */
|
|
56
|
+
key: string;
|
|
57
|
+
/** The value, cut short for logs. */
|
|
58
|
+
value: string;
|
|
59
|
+
}
|
|
60
|
+
export interface SchemaPlaceholderNode {
|
|
61
|
+
/** Where the node sits: '$' for the root, then .key and [index] steps. */
|
|
62
|
+
path: string;
|
|
63
|
+
/** The node's @type ('A,B' when it has several), or null. */
|
|
64
|
+
type: string | null;
|
|
65
|
+
reasons: SchemaPlaceholderReason[];
|
|
66
|
+
evidence: SchemaPlaceholderEvidence[];
|
|
67
|
+
}
|
|
68
|
+
export interface SchemaPlaceholderScan<T> {
|
|
69
|
+
/** The value without its placeholder nodes: the same reference when nothing was found, null when nothing real is left. */
|
|
70
|
+
value: T | null;
|
|
71
|
+
/** Every node removed, outermost only (a dropped node's children go with it). */
|
|
72
|
+
dropped: SchemaPlaceholderNode[];
|
|
73
|
+
/** AI note keys stripped from nodes that were kept. */
|
|
74
|
+
notes: Array<{
|
|
75
|
+
path: string;
|
|
76
|
+
key: string;
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The value with its placeholder nodes removed, and what was removed. Pure; the
|
|
81
|
+
* input is never mutated.
|
|
82
|
+
*/
|
|
83
|
+
export declare function withoutSchemaPlaceholders<T>(value: T): SchemaPlaceholderScan<T>;
|
|
84
|
+
/** Which nodes of a JSON-LD value are placeholders, and why. */
|
|
85
|
+
export declare function findSchemaPlaceholders(value: unknown): SchemaPlaceholderNode[];
|
|
86
|
+
/** One line a person can act on: 'Organization at $: name "Example" is a placeholder name'. */
|
|
87
|
+
export declare function describeSchemaPlaceholder(node: SchemaPlaceholderNode): string;
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @sonordev/site-kit/engage -
|
|
2
|
+
* @sonordev/site-kit/engage - EngageWidget: deprecated, kept through 7.x.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Engage was retired in Sonor. SiteKitLayout mounts website chat and popups
|
|
5
|
+
* for you (its `chat` and `popups` props). To place them yourself, use
|
|
6
|
+
* `SiteChat` from `@sonordev/site-kit/chat` and `SitePopups` from
|
|
7
|
+
* `@sonordev/site-kit/website/popups`. This draws exactly those two, so a
|
|
8
|
+
* site that still renders <EngageWidget /> keeps working. Removed in 8.0.
|
|
6
9
|
*/
|
|
7
10
|
import React from 'react';
|
|
8
|
-
import type
|
|
9
|
-
|
|
10
|
-
interface EngageWidgetProps extends
|
|
11
|
-
|
|
12
|
-
apiKey?: string;
|
|
13
|
-
projectId?: string;
|
|
11
|
+
import { type SiteChatProps } from '../chat/SiteChat';
|
|
12
|
+
/** @deprecated Use SiteChat and SitePopups, or SiteKitLayout's `chat` and `popups`. */
|
|
13
|
+
export interface EngageWidgetProps extends SiteChatProps {
|
|
14
|
+
/** Show the chat launcher. Default true. */
|
|
14
15
|
chatEnabled?: boolean;
|
|
15
|
-
/**
|
|
16
|
+
/** Show the site's popups, banners and toasts. Default true. */
|
|
16
17
|
popupsEnabled?: boolean;
|
|
17
18
|
debug?: boolean;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
export {};
|
|
20
|
+
/** @deprecated Use SiteChat and SitePopups, or SiteKitLayout's `chat` and `popups`. */
|
|
21
|
+
export declare function EngageWidget({ chatEnabled, popupsEnabled, debug, ...props }: EngageWidgetProps): React.JSX.Element;
|
package/dist/engage/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @sonordev/site-kit/engage
|
|
2
|
+
* @sonordev/site-kit/engage — deprecated, kept through 7.x so old imports
|
|
3
|
+
* keep building. Removed in 8.0.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Engage was retired in Sonor. Website chat is `@sonordev/site-kit/chat`
|
|
6
|
+
* (Sonor → Messages) and popups are `@sonordev/site-kit/website/popups`
|
|
7
|
+
* (Sonor → Website). Nothing lives here: this re-exports them under the old
|
|
8
|
+
* names, and EngageWidget draws SiteChat and SitePopups.
|
|
6
9
|
*/
|
|
7
|
-
export { EngageWidget } from './EngageWidget';
|
|
8
|
-
export { ChatWidget } from '
|
|
9
|
-
export {
|
|
10
|
-
export type {
|
|
10
|
+
export { EngageWidget, type EngageWidgetProps } from './EngageWidget';
|
|
11
|
+
export { ChatWidget } from '../chat/ChatWidget';
|
|
12
|
+
export { SONOR_PUBLIC_ECHO_DEFAULTS, getEchoWidgetProps } from '../chat/echo-config';
|
|
13
|
+
export type { EchoPublicConfig } from '../chat/echo-config';
|
|
11
14
|
export * from './types';
|
|
12
|
-
export { SONOR_PUBLIC_ECHO_DEFAULTS, getEchoWidgetProps } from './echo-config';
|
|
13
|
-
export type { EchoPublicConfig } from './echo-config';
|
package/dist/engage/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import '../chunk-NDF4A5JM.js';
|
|
2
|
+
export { SONOR_PUBLIC_ECHO_DEFAULTS, getEchoWidgetProps } from '../chunk-HODO5BX5.js';
|
|
3
|
+
export { ChatWidget } from '../chunk-64DKGVEI.js';
|
|
4
|
+
import { SiteChat } from '../chunk-NV72HZ4W.js';
|
|
6
5
|
import '../chunk-6T6CQVNL.js';
|
|
7
|
-
import '../chunk-
|
|
8
|
-
import '../chunk-
|
|
6
|
+
import '../chunk-JE4A5S4F.js';
|
|
7
|
+
import { SitePopups } from '../chunk-EJVQJCG5.js';
|
|
8
|
+
import '../chunk-27FISYK4.js';
|
|
9
|
+
import '../chunk-FR6BYFPC.js';
|
|
10
|
+
import '../chunk-Q6E4OPGG.js';
|
|
11
|
+
import '../chunk-C2FZBUSS.js';
|
|
9
12
|
import '../chunk-5SQK3D53.js';
|
|
13
|
+
import '../chunk-HW7B43E5.js';
|
|
10
14
|
import '../chunk-NJCTQH2P.js';
|
|
11
15
|
import '../chunk-S22FSH7C.js';
|
|
12
16
|
import '../chunk-24QZEO3Q.js';
|
|
@@ -14,36 +18,28 @@ import '../chunk-L2V5PUFN.js';
|
|
|
14
18
|
import '../chunk-GJWI74ZZ.js';
|
|
15
19
|
import '../chunk-43OCZ3JA.js';
|
|
16
20
|
import '../chunk-EKBEOXTH.js';
|
|
17
|
-
import '../chunk-
|
|
18
|
-
import '../chunk-
|
|
19
|
-
import '../chunk-
|
|
21
|
+
import '../chunk-D6ZC7XKZ.js';
|
|
22
|
+
import '../chunk-K4ZHAURD.js';
|
|
23
|
+
import '../chunk-F24FNCTK.js';
|
|
20
24
|
import '../chunk-PKBMQBKP.js';
|
|
25
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
signal_enabled: true,
|
|
40
|
-
signal_url: config.signalUrl || SONOR_PUBLIC_ECHO_DEFAULTS.signalUrl,
|
|
41
|
-
welcome_message: config.welcomeMessage || SONOR_PUBLIC_ECHO_DEFAULTS.welcomeMessage,
|
|
42
|
-
suggested_questions: config.suggestedQuestions || SONOR_PUBLIC_ECHO_DEFAULTS.suggestedQuestions,
|
|
43
|
-
brand_color: config.brandColor || SONOR_PUBLIC_ECHO_DEFAULTS.brandColor,
|
|
44
|
-
assistant_name: config.assistantName || SONOR_PUBLIC_ECHO_DEFAULTS.assistantName,
|
|
45
|
-
assistant_avatar: config.assistantAvatar
|
|
46
|
-
};
|
|
27
|
+
function EngageWidget({ chatEnabled = true, popupsEnabled = true, debug, ...props }) {
|
|
28
|
+
const { apiUrl, apiKey, zIndex, allowInFrame, allowLocalhost } = props;
|
|
29
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
|
+
popupsEnabled && /* @__PURE__ */ jsx(
|
|
31
|
+
SitePopups,
|
|
32
|
+
{
|
|
33
|
+
apiUrl,
|
|
34
|
+
apiKey,
|
|
35
|
+
zIndex,
|
|
36
|
+
allowInFrame,
|
|
37
|
+
allowLocalhost,
|
|
38
|
+
debug
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
chatEnabled && /* @__PURE__ */ jsx(SiteChat, { ...props })
|
|
42
|
+
] });
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
export {
|
|
45
|
+
export { EngageWidget };
|