@sonordev/site-kit 8.6.0 → 8.8.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/CHANGELOG.md +17 -0
- package/agent-manifest.json +1 -1
- package/dist/{AnalyticsProvider-GVJYL6XY.js → AnalyticsProvider-7SBJ27SI.js} +6 -6
- package/dist/{ArticleViewTracker-AOJXEBYM.js → ArticleViewTracker-PV2RD3P6.js} +5 -5
- package/dist/{BlocksPopup-AJ3CKVQG.js → BlocksPopup-NEEFV2L3.js} +9 -8
- package/dist/{ChatWidget-EYBVB5SZ.js → ChatWidget-V46UBDOM.js} +5 -5
- package/dist/{FileField-UEQYIFRO.js → FileField-MJ3RCNFJ.js} +3 -3
- package/dist/{FormSpotlight-H73ZZ66T.js → FormSpotlight-NQPQFZS7.js} +1 -1
- package/dist/{FormStage-2PLWWYBW.js → FormStage-4T5PJR5K.js} +1 -1
- package/dist/{ManagedForm-ZQOFXEWS.js → ManagedForm-DDDVMKLJ.js} +6 -6
- package/dist/{ManagedNewsletterForm-5TE47YZD.js → ManagedNewsletterForm-TUYGN6O3.js} +9 -9
- package/dist/{SignalCore-FEFNMNYN.js → SignalCore-OW3YIH6M.js} +5 -5
- package/dist/SiteChat-XMKHBSEK.js +6 -0
- package/dist/SiteDesignReporter-BRH7ORQS.js +13 -0
- package/dist/{SitePopups-C44YTQ3Q.js → SitePopups-VU2C3Z6B.js} +6 -6
- package/dist/SitemapSync-C4H3HF3L.js +9 -0
- package/dist/_client/booking-widget.js +5 -5
- package/dist/admin-auth/index.d.ts +2 -2
- package/dist/admin-auth/session.d.ts +2 -2
- package/dist/affiliates/index.js +3 -3
- package/dist/analytics/content-text.d.ts +17 -0
- package/dist/analytics/index.js +6 -6
- package/dist/{article-styles-VASFBYA3.js → article-styles-D7VCDXMG.js} +2 -1
- package/dist/article-tables-W45BE353.js +3 -0
- package/dist/articles/index.js +7 -20
- package/dist/articles/server-ui.js +29 -55
- package/dist/articles/server.js +6 -5
- package/dist/chat/index.js +8 -8
- package/dist/{chunk-3RDJKIYO.js → chunk-2WDWQI7K.js} +1 -1
- package/dist/{chunk-KTMHRXDT.js → chunk-2WM4TNDF.js} +1 -1
- package/dist/chunk-3MJNBOUO.js +8 -0
- package/dist/{chunk-BF7TZYC3.js → chunk-52KO4LKD.js} +1 -1
- package/dist/chunk-64XODP7M.js +7 -0
- package/dist/{chunk-DSD3XZQQ.js → chunk-6BFQI3ZE.js} +3 -3
- package/dist/{chunk-QNOJSSQT.js → chunk-6DXQHNZK.js} +1 -1
- package/dist/{chunk-5D7ZISUP.js → chunk-7DYMM6SS.js} +11 -6
- package/dist/{chunk-QQT2T6JQ.js → chunk-7HW4TZB5.js} +1 -1
- package/dist/{chunk-E4PTMORJ.js → chunk-AL5UCWLI.js} +23 -8
- package/dist/chunk-AYQEIMGS.js +390 -0
- package/dist/{chunk-L6QTERSH.js → chunk-DYROJGI2.js} +1 -1
- package/dist/{chunk-XA7Q2VQA.js → chunk-EK7P62DT.js} +1 -1
- package/dist/{chunk-BUE3Z32O.js → chunk-F6WLHVMQ.js} +1 -1
- package/dist/{chunk-IYCSDFPN.js → chunk-FLOJUMFR.js} +2 -2
- package/dist/{chunk-7R2STOVD.js → chunk-G7BRCSV6.js} +3 -3
- package/dist/{chunk-NI6ZTP3D.js → chunk-GQN4BGK6.js} +1 -1
- package/dist/{chunk-GFY3E7EQ.js → chunk-GXWM5KNS.js} +15 -7
- package/dist/{chunk-U34O6I7W.js → chunk-HN7GVGWC.js} +3 -3
- package/dist/{chunk-6KU7STM3.js → chunk-HY7ZEU2C.js} +3 -1
- package/dist/chunk-IBHBEUZV.js +27 -0
- package/dist/{chunk-ZZSAWXWD.js → chunk-IDC5CVBR.js} +1 -1
- package/dist/{chunk-HMTPRBYJ.js → chunk-IWNJCRXW.js} +1 -1
- package/dist/chunk-JESLPEST.js +14 -0
- package/dist/{chunk-BC7JMY4Z.js → chunk-JG53YJLM.js} +58 -1
- package/dist/{chunk-RI2N2EBI.js → chunk-JYITKUUB.js} +13 -5
- package/dist/{chunk-V67OD7RW.js → chunk-KG5IEV7I.js} +113 -93
- package/dist/chunk-KIYACQW3.js +1 -0
- package/dist/{chunk-JSW42BHL.js → chunk-M5JIHQ42.js} +1 -1
- package/dist/{chunk-YJH6WE76.js → chunk-NFGAXWCN.js} +9 -9
- package/dist/{chunk-Y35BWW2C.js → chunk-ODOV76VW.js} +1 -1
- package/dist/{chunk-VA6IJ55K.js → chunk-OPUY4RS6.js} +1 -1
- package/dist/{chunk-6NXFA7HE.js → chunk-OWZWRP7Y.js} +2 -2
- package/dist/{chunk-JJ4JS3LD.js → chunk-PAYCK7FC.js} +1 -1
- package/dist/{chunk-72F44HKL.js → chunk-PSS6M4CG.js} +1 -1
- package/dist/{chunk-O3Z5HHFP.js → chunk-SBPUFTMP.js} +2 -2
- package/dist/{chunk-OFSV5TZY.js → chunk-SHAUX2MQ.js} +1 -1
- package/dist/{chunk-OWO3RFEX.js → chunk-SLFYTELD.js} +1 -1
- package/dist/{chunk-CMUD37WV.js → chunk-SVHPILS3.js} +1 -1
- package/dist/{chunk-YNBRVVTX.js → chunk-THXELZTQ.js} +1 -1
- package/dist/{chunk-IGAIQ2B3.js → chunk-TP32MRXV.js} +2 -2
- package/dist/{chunk-NUXXFCIT.js → chunk-URLTBVRX.js} +4 -4
- package/dist/chunk-XAZC2TUD.js +1119 -0
- package/dist/{chunk-URJN75ZG.js → chunk-XIDWNXZN.js} +3 -3
- package/dist/chunk-XLAHRN4B.js +1 -0
- package/dist/chunk-XS2ORMUG.js +32 -0
- package/dist/{chunk-6AWIUO2Z.js → chunk-Y6SHFILE.js} +1 -1
- package/dist/{chunk-FTSEP7Q3.js → chunk-YEX5RAEU.js} +3 -3
- package/dist/{chunk-AJG74ZZV.js → chunk-YRQS7VAH.js} +1 -1
- package/dist/{chunk-DLM4FFNH.js → chunk-ZQSCMYZO.js} +1 -1
- package/dist/client/index.js +4 -4
- package/dist/contracts/campaign-normalize.d.ts +2 -0
- package/dist/contracts/entries.d.ts +1 -1
- package/dist/contracts/frame-ready.d.ts +23 -0
- package/dist/contracts/funnels.d.ts +576 -0
- package/dist/contracts/inbox.d.ts +42 -0
- package/dist/contracts/llms.d.ts +200 -0
- package/dist/contracts/portfolio.d.ts +361 -8
- package/dist/contracts/publication-branding.d.ts +10 -2
- package/dist/contracts/site-cache.d.ts +2 -0
- package/dist/fleet/index.js +6 -6
- package/dist/forms/index.js +9 -9
- package/dist/forms/server.js +2 -2
- package/dist/images/index.js +4 -4
- package/dist/index.js +1 -1
- package/dist/landing/contract.d.ts +2 -13
- package/dist/landing/contract.js +2 -0
- package/dist/landing/index.js +4 -2
- package/dist/landing/managed-server.d.ts +21 -0
- package/dist/landing/managed.d.ts +11 -0
- package/dist/landing/metadata.js +2 -0
- package/dist/landing/server.d.ts +5 -0
- package/dist/landing/server.js +20 -2
- package/dist/layout/client.js +9 -9
- package/dist/layout/index.js +10 -10
- package/dist/llms/contract.js +2 -1
- package/dist/llms/index.js +9 -7
- package/dist/llms/merge.d.ts +29 -0
- package/dist/maps/index.js +3 -3
- package/dist/mcp/sonor.js +9 -8
- package/dist/og/pages.d.ts +3 -3
- package/dist/portfolio/contract.js +705 -1
- package/dist/press-kit/articles/article-body.d.ts +12 -0
- package/dist/press-kit/articles/article-heading-ids.d.ts +7 -0
- package/dist/press-kit/articles/article-headings.d.ts +61 -0
- package/dist/press-kit/articles/html-tags.d.ts +46 -0
- package/dist/press-kit/articles/types.d.ts +2 -0
- package/dist/revalidate/index.js +3 -3
- package/dist/runtime/index.js +3 -3
- package/dist/seo/client.js +6 -6
- package/dist/seo/index.js +14 -13
- package/dist/seo/llms/contract.js +2 -1
- package/dist/seo/llms.js +9 -7
- package/dist/seo/meta/contract.js +2 -1
- package/dist/seo/register-sitemap-cli.js +1 -1
- package/dist/seo/server.js +5 -4
- package/dist/seo/sitemap.js +7 -5
- package/dist/seo-meta/contract.js +2 -1
- package/dist/server/index.js +3 -3
- package/dist/{server-api-EGOKC7Q3.js → server-api-XIKDWSNO.js} +2 -2
- package/dist/shared/build-entries.d.ts +3 -1
- package/dist/shared/frame.d.ts +1 -1
- package/dist/shared/markdown.d.ts +7 -6
- package/dist/shared/source-graph.d.ts +5 -5
- package/dist/shared/version.d.ts +1 -1
- package/dist/signal/index.js +2 -2
- package/dist/sitemap/index.js +7 -5
- package/dist/sitemap/shared.d.ts +1 -2
- package/dist/slots/index.js +2 -2
- package/dist/sync/index.js +5 -5
- package/dist/website/contract.js +2 -1
- package/dist/website/images.js +4 -4
- package/dist/website/index.js +10 -9
- package/dist/website/landing/server.js +20 -2
- package/dist/website/landing.js +4 -2
- package/dist/website/popups.js +9 -8
- package/dist/website/slots.js +2 -2
- package/dist/{writeLLMsTxt-KHVHNSDM.js → writeLLMsTxt-4HHUAOA6.js} +6 -4
- package/docs.json +5 -0
- package/package.json +1 -1
- package/src/analytics/README.md +1 -1
- package/src/landing/README.md +64 -0
- package/src/llms/README.md +52 -20
- package/dist/SiteChat-3MKYL2XM.js +0 -6
- package/dist/SiteDesignReporter-AJDIBNHI.js +0 -12
- package/dist/SitemapSync-UJA6JVPW.js +0 -9
- package/dist/article-tables-6XHVULPU.js +0 -2
- package/dist/chunk-EKBEOXTH.js +0 -114
- package/dist/chunk-NZ4RXN3G.js +0 -145
- package/dist/chunk-OSYCOC3K.js +0 -21
- /package/dist/{chunk-7ZWO5HDQ.js → chunk-BTHEQDN4.js} +0 -0
- /package/dist/{chunk-BQR4IPBW.js → chunk-ISRBSTB7.js} +0 -0
- /package/dist/{chunk-IHG36STL.js → chunk-XHYE24HU.js} +0 -0
package/dist/website/slots.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { ManagedLink, ManagedList, ManagedRichText, ManagedSlot, SLOTS_CACHE_TAG, createSlotsRevalidateHandler, ManagedSlot_default as default, getSlot, renderSlotRichText, resolveSlots } from '../chunk-
|
|
1
|
+
export { ManagedLink, ManagedList, ManagedRichText, ManagedSlot, SLOTS_CACHE_TAG, createSlotsRevalidateHandler, ManagedSlot_default as default, getSlot, renderSlotRichText, resolveSlots } from '../chunk-2WDWQI7K.js';
|
|
2
2
|
export { SLOTS_CONTRACT_VERSION, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-VCJYLYJV.js';
|
|
3
3
|
export { SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, SLOT_LIST_MAX_ITEMS, isSafeSlotHref, isValidSlotId, normalizeSlotValue, parseSlotRichText } from '../chunk-LPH5FANE.js';
|
|
4
|
-
import '../chunk-
|
|
4
|
+
import '../chunk-HY7ZEU2C.js';
|
|
5
5
|
import '../chunk-C7TLGL3R.js';
|
|
6
6
|
import '../chunk-MH2DSGC6.js';
|
|
7
7
|
import '../chunk-PKBMQBKP.js';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { writeLLMsTxtToPublic } from './chunk-
|
|
2
|
-
import './chunk-
|
|
3
|
-
import './chunk-
|
|
1
|
+
export { writeLLMsTxtToPublic } from './chunk-KG5IEV7I.js';
|
|
2
|
+
import './chunk-EK7P62DT.js';
|
|
3
|
+
import './chunk-7DYMM6SS.js';
|
|
4
|
+
import './chunk-IBHBEUZV.js';
|
|
4
5
|
import './chunk-4K735O3J.js';
|
|
5
6
|
import './chunk-UAYAJPEP.js';
|
|
6
|
-
import './chunk-
|
|
7
|
+
import './chunk-XAZC2TUD.js';
|
|
8
|
+
import './chunk-ISRBSTB7.js';
|
|
7
9
|
import './chunk-MJP63SSS.js';
|
|
8
10
|
import './chunk-YVKRYQRG.js';
|
|
9
11
|
import './chunk-FESKKJAS.js';
|
package/docs.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sonordev/site-kit",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@11.5.3",
|
|
6
6
|
"description": "Sonor's Next.js foundation: SEO, analytics, forms, chat, booking, AI visibility and agent tools. Add @sonordev/press-kit for publications or @sonordev/commerce-kit for selling. Legacy article imports remain compatible in 8.x.",
|
package/src/analytics/README.md
CHANGED
|
@@ -168,7 +168,7 @@ interface AnalyticsConfig {
|
|
|
168
168
|
- **Page views**: on every route change (path, URL, title, referrer, UTM params, device/browser/OS)
|
|
169
169
|
- **Web Vitals**: LCP, CLS, TTFB, INP, FCP with good/needs-improvement/poor ratings
|
|
170
170
|
- **Contact clicks**: `tel:` and `mailto:` links tracked as conversions, once `<ContactTracking />` is mounted (it isn't by default)
|
|
171
|
-
- **DOM metadata**: full snapshot per page view (meta tags, H1, word count, links, content, FAQs)
|
|
171
|
+
- **DOM metadata**: full snapshot per page view (meta tags, H1, word count, links, content, FAQs). The content text keeps the page's elements apart (a space after every block element and line break) and leaves out menus, form fields, buttons and dialogs, so a heading never runs into the paragraph after it
|
|
172
172
|
|
|
173
173
|
## Embedded pages report nothing (`analytics.allowInFrame`)
|
|
174
174
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Managed landing pages
|
|
2
|
+
|
|
3
|
+
Use the server entry for an opt-in managed landing route. The same bounded layout supports a newsletter signup, a requested guide or an inquiry. Page text renders on the server and follows the site's existing layout. Managed forms handle browser submission.
|
|
4
|
+
|
|
5
|
+
First check your site's route inventory for `/f` collisions. Install this adapter only when the prefix is available, then verify the deployed route before enabling managed publication in Sonor. Existing-page funnels work independently of this adapter.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
// app/f/[slug]/page.tsx
|
|
9
|
+
import { notFound } from 'next/navigation'
|
|
10
|
+
import {
|
|
11
|
+
getPublishedFunnelPage,
|
|
12
|
+
managedFunnelMetadata,
|
|
13
|
+
ManagedFunnelPage,
|
|
14
|
+
} from '@sonordev/site-kit/website/landing/server'
|
|
15
|
+
|
|
16
|
+
const options = { site: 'example.com' } // Your registered site host.
|
|
17
|
+
type PageProps = { params: Promise<{ slug: string }> }
|
|
18
|
+
|
|
19
|
+
export async function generateMetadata({ params }: PageProps) {
|
|
20
|
+
const { slug } = await params
|
|
21
|
+
const page = await getPublishedFunnelPage(slug, options)
|
|
22
|
+
return page ? managedFunnelMetadata(page) : { robots: { index: false } }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default async function Page({ params }: PageProps) {
|
|
26
|
+
const { slug } = await params
|
|
27
|
+
const page = await getPublishedFunnelPage(slug, options)
|
|
28
|
+
if (!page) notFound()
|
|
29
|
+
return <ManagedFunnelPage page={page} />
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Keep `SiteKitLayout` in your root layout and use its deferred analytics sibling. The adapter doesn't require analytics to submit a form. It reads the server credential through the existing server transport and requires an explicit registered site. Responses from another host or path are refused.
|
|
34
|
+
|
|
35
|
+
Published-page reads use `no-store` in this version so an outdated or removed publication isn't served from the fetch cache. Keep the route dynamic and avoid wrapping this reader in a persistent cache. `managedFunnelMetadata` defaults to noindex, nofollow. Keep these campaign routes out of your sitemap.
|
|
36
|
+
|
|
37
|
+
Use the normal revalidation route:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// app/api/seo-revalidate/route.ts
|
|
41
|
+
export { POST } from '@sonordev/site-kit/revalidate'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Sonor can invalidate the exact published path and `SITE_CACHE_TAGS.funnels`. That refresh is distinct from installing and verifying the route.
|
|
45
|
+
|
|
46
|
+
Private previews use the exact owner-authorized dashboard DTO. Pausing follow-ups retains the published landing page and independent form confirmations. `FunnelPageContent` from the server entry accepts `preview` and displays an inert form placeholder with a noindex and reporting-suppression marker. `ManagedFunnelPage preview` also skips form fetching. Serve private preview responses with `Cache-Control: no-store`; these components don't authorize requests or set HTTP headers. No public draft-token route is included.
|
|
47
|
+
|
|
48
|
+
Content is limited to text paragraphs and bullet lists. Logos use resolved, validated asset URLs, colors and fonts use the shared brand contract, and the renderer accepts no stored HTML, scripts or CSS. If a full logo is missing, the verified display name appears. Theme variants, square marks and publisher attribution retain their separate roles.
|
|
49
|
+
|
|
50
|
+
For developer-authored paid pages, the existing `LandingPage` component remains available from `@sonordev/site-kit/website/landing`. Import `landingPageMetadata` from the server entry. Campaign normalization now comes from the shared contracts package.
|
|
51
|
+
|
|
52
|
+
## Record the installation
|
|
53
|
+
|
|
54
|
+
An authorized repository owner completes this checklist once per site:
|
|
55
|
+
|
|
56
|
+
1. Inventory the whole `/f` prefix, including routes, rewrites and dynamic route collisions.
|
|
57
|
+
2. Install the adapter above and verify server-rendered content, form behavior, exact host binding, private preview isolation and mobile/desktop layouts for that source revision.
|
|
58
|
+
3. Record the inventory digest, source revision, supported adapter version and named verification through the existing owner-authorized project settings path.
|
|
59
|
+
|
|
60
|
+
The installation record lives at `projects.settings.managed_funnel_pages[normalizedOwnedHost]`. It contains `schemaVersion: 1`, `enabled`, `prefix: '/f'`, `adapterVersion`, `routeInventoryDigest`, `sourceRevision`, `verifiedAt`, `verifiedBy` and `provenance: 'owner_installation_attestation'`. The server attributes the actor and provenance. Use the adapter version supported by your Sonor installation. Each record belongs to one registered host.
|
|
61
|
+
|
|
62
|
+
A draft or a browser report can't assert that the route is installed. This record describes an operator's installation check. Report a verified deployment and current live reachability separately when you've observed them. Publishing a funnel doesn't install the adapter.
|
|
63
|
+
|
|
64
|
+
Disable the record before removing the adapter. For route, host or adapter changes, invalidate the old record, repeat the checks and replace its revision and digest. Sonor snapshots this installation evidence during preview and rechecks it at launch and public resolution. Existing-page funnels remain available when managed-page installation is missing.
|
package/src/llms/README.md
CHANGED
|
@@ -42,6 +42,7 @@ The GEO system uses a shared contract (`@sonordev/site-kit/llms/contract`) consu
|
|
|
42
42
|
- **`LLM_GEO_CONTRACT_VERSION`** (currently `1`) — increment on breaking payload changes
|
|
43
43
|
- **`LLMS_PUBLIC_SUMMARY_MAX_LENGTH`** (`400`) — max chars for page link notes
|
|
44
44
|
- **Sanitizers** — `sanitizeLlmsPublicSummary()`, `sanitizeLlmsDisclaimerLine()`, `sanitizePrimaryLanguageTag()`, `pickManagedLlmSchemaForJsonLd()`
|
|
45
|
+
- **Page notes, titles and order** — `pickLlmsPageDescription()`, `llmsPageListNote()`, `llmsPageTitle()`, `isLlmsListablePage()`, `selectLlmsPages()`, `groupLlmsPages()`, `cleanScrapedPageText()`, `stripContactDetails()`. The Sonor API and site-kit's local generator both build page lines through these, so a page reads the same wherever the file is built (see [What a page line says](#what-a-page-line-says)).
|
|
45
46
|
|
|
46
47
|
See `LLM_GEO_CONTRACT.md` for the full spec.
|
|
47
48
|
|
|
@@ -337,8 +338,8 @@ export async function POST(request: Request) {
|
|
|
337
338
|
### How `generateLLMsTxt()` Works
|
|
338
339
|
|
|
339
340
|
1. Fetches all data via `getLLMsData()` → `GET /api/public/llms/data`
|
|
340
|
-
2. Optionally merges with local data (`getLocalData` callback) when Sonor returns empty
|
|
341
|
-
3. Builds markdown sections in order: **Header → About →
|
|
341
|
+
2. Optionally merges with local data (`getLocalData` callback) when Sonor returns nothing, leaves services, FAQs or pages empty, or leaves the business's name, description, phone or address empty. The merge is field by field: a field Sonor fills wins, and a field Sonor leaves empty never deletes the value the site supplied
|
|
342
|
+
3. Builds markdown sections in order: **Header → About → Contact → Services → Site Pages → Portfolio → FAQ → Full Context Link → Knowledge Graph → Topic Clusters → Custom Sections → Agent access → Optional**. The business and the contact block come first because they are short and answer the commonest question; the page index comes before the FAQ; `## Optional` is last because the spec lets a short-context reader stop there
|
|
342
343
|
4. Portfolio, Entity, and Topic Cluster sections are fetched **in parallel** via `Promise.allSettled()`
|
|
343
344
|
5. Returns `{ markdown, metadata }` where metadata includes `sections`, `attempted_sections`, and `failed_sections`
|
|
344
345
|
|
|
@@ -356,9 +357,27 @@ export async function POST(request: Request) {
|
|
|
356
357
|
|
|
357
358
|
Business description...
|
|
358
359
|
|
|
360
|
+
## Contact Information
|
|
361
|
+
|
|
362
|
+
- **Phone:** 555-0100
|
|
363
|
+
- **Email:** info@example.com
|
|
364
|
+
- **Address:** 123 Main St, Springfield, OH 45501
|
|
365
|
+
- **Hours:** Mon-Fri 8am-5pm
|
|
366
|
+
|
|
359
367
|
## Services
|
|
360
368
|
|
|
361
|
-
- [Service Name](/services/slug): Brief description
|
|
369
|
+
- [Service Name](https://example.com/services/slug): Brief description
|
|
370
|
+
|
|
371
|
+
## Site Pages
|
|
372
|
+
|
|
373
|
+
### Key pages
|
|
374
|
+
|
|
375
|
+
- [Home](https://example.com/): Public-safe summary of the page content
|
|
376
|
+
- [About](https://example.com/about): Learn about our team and mission
|
|
377
|
+
|
|
378
|
+
### Services
|
|
379
|
+
|
|
380
|
+
- [Furnace repair](https://example.com/services/furnace-repair): Same-day furnace repair in Springfield
|
|
362
381
|
|
|
363
382
|
## Portfolio & Case Studies
|
|
364
383
|
|
|
@@ -366,27 +385,12 @@ Business description...
|
|
|
366
385
|
|
|
367
386
|
Description of the project.
|
|
368
387
|
|
|
369
|
-
## Contact Information
|
|
370
|
-
|
|
371
|
-
- **Phone:** 555-1234
|
|
372
|
-
- **Email:** info@example.com
|
|
373
|
-
- **Address:** 123 Main St, City, State
|
|
374
|
-
|
|
375
388
|
## Frequently Asked Questions
|
|
376
389
|
|
|
377
390
|
### How do you work?
|
|
378
391
|
|
|
379
392
|
We follow a proven process...
|
|
380
393
|
|
|
381
|
-
## Site Pages
|
|
382
|
-
|
|
383
|
-
- [Home](https://example.com/): Public-safe summary of the page content
|
|
384
|
-
- [About](https://example.com/about): Learn about our team and mission
|
|
385
|
-
|
|
386
|
-
## Optional
|
|
387
|
-
|
|
388
|
-
- [Privacy Policy](https://example.com/privacy): How we collect and use your information
|
|
389
|
-
|
|
390
394
|
## Full context
|
|
391
395
|
|
|
392
396
|
- [llms-full.txt](https://example.com/llms-full.txt): Expanded index for large-context systems.
|
|
@@ -408,6 +412,10 @@ Service page: https://example.com/services/family-law
|
|
|
408
412
|
Pillar: [Complete Guide to Family Law](/article/family-law-guide)
|
|
409
413
|
- [How to File for Divorce](/article/filing-divorce) (Mar 15, 2026)
|
|
410
414
|
- [Child Custody Laws](/article/custody-laws) (Mar 10, 2026)
|
|
415
|
+
|
|
416
|
+
## Optional
|
|
417
|
+
|
|
418
|
+
- [Privacy Policy](https://example.com/privacy): How we collect and use your information
|
|
411
419
|
```
|
|
412
420
|
|
|
413
421
|
### Configuration Options
|
|
@@ -433,7 +441,7 @@ generateLLMsTxt({
|
|
|
433
441
|
// Spec features
|
|
434
442
|
linkToFullLlms: true, // Append "Full context" section
|
|
435
443
|
optionalPagePaths: ['/privacy', '/terms'], // Moved from Site Pages to ## Optional
|
|
436
|
-
pageListNotesFromPublicSummaryOnly: false, // Strict mode:
|
|
444
|
+
pageListNotesFromPublicSummaryOnly: false, // Strict mode: a note only from llms_public_summary
|
|
437
445
|
|
|
438
446
|
// Overrides (take precedence over Sonor settings)
|
|
439
447
|
headerPrimaryLanguage: 'en',
|
|
@@ -447,6 +455,30 @@ generateLLMsTxt({
|
|
|
447
455
|
})
|
|
448
456
|
```
|
|
449
457
|
|
|
458
|
+
### What a page line says
|
|
459
|
+
|
|
460
|
+
Every `- [Title](url): note` line is built by the same rules, in `@sonordev/contracts/llms`, wherever the file is generated.
|
|
461
|
+
|
|
462
|
+
**The note**, best source first (`pickLlmsPageDescription`):
|
|
463
|
+
|
|
464
|
+
1. the page's `llms_public_summary`, written for this file. A phone number or an email address in it is taken out along with the "call" or "email us at" in front of it, and the rest is used. (A summary used to be thrown away whole, and the page fell back to its scrape.) A lead-in such as "This page covers" is dropped, so the note starts at what the page is about;
|
|
465
|
+
2. the curated meta description (`managed_meta_description`);
|
|
466
|
+
3. the meta description the page serves;
|
|
467
|
+
4. the page's schema description;
|
|
468
|
+
5. last, the page's scraped text, cleaned (`cleanScrapedPageText`): the whitespace between elements put back, form labels, menu items, cookie banners and button text dropped, contact details removed.
|
|
469
|
+
|
|
470
|
+
Every note is cut at a sentence end or a word boundary, 500 characters at the most, and never mid-word. A note that only repeats the page's title, or whose text is an error page, is skipped. `pageListNotesFromPublicSummaryOnly` still limits notes to source 1.
|
|
471
|
+
|
|
472
|
+
**The title** is the curated title, else the title the page serves, else its h1, each without its `| Brand` tail; the home page is the business name; a page with none of these is named from the last segment of its path ("Accounting" for `/features/accounting`). It is never the raw path.
|
|
473
|
+
|
|
474
|
+
**Which pages are listed.** A page the last visit saw answer with an error, a page that asks search engines to leave it out (`noindex`), and a utility page (a cart, a thank-you page, a login) are left out, and each path is listed once.
|
|
475
|
+
|
|
476
|
+
**Order and groups** (`groupLlmsPages`). The home page first, then the top-level pages by value (what the site does, where, the proof, who, the cost, contact), then one `###` group per section of the site (`/services/*` is "Services"), the biggest unlisted sections last, and the legal pages at the very end. A site of 12 pages or fewer is one list. When `maxPages` is smaller than the site, `selectLlmsPages` keeps every section represented, most valuable first, and the shorter list is always a subset of `llms-full.txt`.
|
|
477
|
+
|
|
478
|
+
**Contact block.** Phone, email, one-line address and hours. A field with nothing in it is not printed, and a contact with nothing at all leaves the whole section out.
|
|
479
|
+
|
|
480
|
+
**The first line** under the name is the tagline, else the first sentence of the description (not the text up to its first full stop, which cuts "Smith & Sons, Inc. is..." at "Inc").
|
|
481
|
+
|
|
450
482
|
### Optional pages
|
|
451
483
|
|
|
452
484
|
`optionalPagePaths` demotes pages to `## Optional`, the section llmstxt.org lets a
|
|
@@ -465,7 +497,7 @@ const { markdown, metadata } = await generateLLMsTxt({})
|
|
|
465
497
|
|
|
466
498
|
metadata.generated_at // ISO timestamp
|
|
467
499
|
metadata.project_id // From API key
|
|
468
|
-
metadata.sections // ['header', 'about', '
|
|
500
|
+
metadata.sections // ['header', 'about', 'contact', 'services', 'pages', 'faq', ...]
|
|
469
501
|
metadata.attempted_sections // ['portfolio', 'knowledge-graph', 'topic-clusters']
|
|
470
502
|
metadata.failed_sections // ['knowledge-graph'] — if entity API was unavailable
|
|
471
503
|
```
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export { SiteDesignReporter } from './chunk-DSD3XZQQ.js';
|
|
2
|
-
import './chunk-NUXXFCIT.js';
|
|
3
|
-
import './chunk-IHG36STL.js';
|
|
4
|
-
import './chunk-YC7ELZS3.js';
|
|
5
|
-
import './chunk-S22FSH7C.js';
|
|
6
|
-
import './chunk-DLM4FFNH.js';
|
|
7
|
-
import './chunk-EKBEOXTH.js';
|
|
8
|
-
import './chunk-IYCSDFPN.js';
|
|
9
|
-
import './chunk-QQT2T6JQ.js';
|
|
10
|
-
import './chunk-W7OPEMGJ.js';
|
|
11
|
-
import './chunk-BUE3Z32O.js';
|
|
12
|
-
import './chunk-PKBMQBKP.js';
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { SitemapSync } from './chunk-6NXFA7HE.js';
|
|
2
|
-
import './chunk-YVKRYQRG.js';
|
|
3
|
-
import './chunk-DLM4FFNH.js';
|
|
4
|
-
import './chunk-EKBEOXTH.js';
|
|
5
|
-
import './chunk-IYCSDFPN.js';
|
|
6
|
-
import './chunk-QQT2T6JQ.js';
|
|
7
|
-
import './chunk-W7OPEMGJ.js';
|
|
8
|
-
import './chunk-BUE3Z32O.js';
|
|
9
|
-
import './chunk-PKBMQBKP.js';
|
package/dist/chunk-EKBEOXTH.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// src/contracts/portfolio.ts
|
|
2
|
-
var FRAME_READY_MESSAGE = "sonor:frame-ready";
|
|
3
|
-
function isFrameReadyMessage(data) {
|
|
4
|
-
return !!data && typeof data === "object" && data.type === FRAME_READY_MESSAGE && data.v === 1;
|
|
5
|
-
}
|
|
6
|
-
var PORTFOLIO_CONTRACT_VERSION = 1;
|
|
7
|
-
var LIGHTHOUSE_RE = /lighthouse/i;
|
|
8
|
-
function isLighthouseKpi(kpi) {
|
|
9
|
-
return LIGHTHOUSE_RE.test(kpi.label || "");
|
|
10
|
-
}
|
|
11
|
-
function curateHeroKpis(kpis) {
|
|
12
|
-
const business = kpis.filter((k) => !isLighthouseKpi(k));
|
|
13
|
-
const lighthouse = kpis.filter(isLighthouseKpi);
|
|
14
|
-
if (lighthouse.length <= 1) return kpis;
|
|
15
|
-
const allPerfect = lighthouse.length >= 4 && lighthouse.every((k) => k.value === 100);
|
|
16
|
-
const summary = allPerfect ? {
|
|
17
|
-
...lighthouse[0],
|
|
18
|
-
label: "Lighthouse, all 4 categories",
|
|
19
|
-
description: "Perfect scores across performance, SEO, accessibility, and best practices."
|
|
20
|
-
} : lighthouse.find((k) => /performance/i.test(k.label)) ?? lighthouse[0];
|
|
21
|
-
return [...business, summary];
|
|
22
|
-
}
|
|
23
|
-
var PORTFOLIO_KPI_JSON_SCHEMA = {
|
|
24
|
-
$schema: "http://json-schema.org/draft-07/schema#",
|
|
25
|
-
$id: "https://sonor.io/schemas/portfolio-kpi.v1.json",
|
|
26
|
-
title: "KpiLike",
|
|
27
|
-
description: "The subset of a portfolio KPI tile the Lighthouse-collapse policy reads.",
|
|
28
|
-
type: "object",
|
|
29
|
-
required: ["label", "value"],
|
|
30
|
-
properties: {
|
|
31
|
-
label: { type: "string" },
|
|
32
|
-
value: { type: "number" },
|
|
33
|
-
description: { type: "string" },
|
|
34
|
-
source: { enum: ["measured", "reported", "estimated"] },
|
|
35
|
-
reportedBy: {
|
|
36
|
-
type: "object",
|
|
37
|
-
required: ["name", "organization", "date"],
|
|
38
|
-
properties: {
|
|
39
|
-
name: { type: "string", minLength: 1 },
|
|
40
|
-
role: { type: "string" },
|
|
41
|
-
organization: { type: "string", minLength: 1 },
|
|
42
|
-
date: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$" }
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
// A reported number is only valid with someone named behind it.
|
|
47
|
-
if: { properties: { source: { const: "reported" } }, required: ["source"] },
|
|
48
|
-
then: { required: ["reportedBy"] },
|
|
49
|
-
additionalProperties: true
|
|
50
|
-
};
|
|
51
|
-
var METRIC_SOURCES = ["measured", "reported", "estimated"];
|
|
52
|
-
var ISO_DAY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
53
|
-
var MONTHS = [
|
|
54
|
-
"January",
|
|
55
|
-
"February",
|
|
56
|
-
"March",
|
|
57
|
-
"April",
|
|
58
|
-
"May",
|
|
59
|
-
"June",
|
|
60
|
-
"July",
|
|
61
|
-
"August",
|
|
62
|
-
"September",
|
|
63
|
-
"October",
|
|
64
|
-
"November",
|
|
65
|
-
"December"
|
|
66
|
-
];
|
|
67
|
-
function isMetricSource(value) {
|
|
68
|
-
return typeof value === "string" && METRIC_SOURCES.includes(value);
|
|
69
|
-
}
|
|
70
|
-
function metricSourceProblem(metric) {
|
|
71
|
-
if (metric.source === void 0 || metric.source === null) return null;
|
|
72
|
-
if (!isMetricSource(metric.source)) return `source must be one of ${METRIC_SOURCES.join(", ")}`;
|
|
73
|
-
if (metric.source !== "reported") return null;
|
|
74
|
-
const by = metric.reportedBy;
|
|
75
|
-
if (!by || typeof by !== "object") {
|
|
76
|
-
return "a reported number needs reportedBy: who reported it, where they work, and when";
|
|
77
|
-
}
|
|
78
|
-
if (typeof by.name !== "string" || !by.name.trim()) return "reportedBy.name is required";
|
|
79
|
-
if (typeof by.organization !== "string" || !by.organization.trim()) {
|
|
80
|
-
return "reportedBy.organization is required";
|
|
81
|
-
}
|
|
82
|
-
if (typeof by.date !== "string" || !ISO_DAY_RE.test(by.date) || Number.isNaN(Date.parse(by.date))) {
|
|
83
|
-
return "reportedBy.date must be a real day, YYYY-MM-DD";
|
|
84
|
-
}
|
|
85
|
-
if (by.role !== void 0 && typeof by.role !== "string") return "reportedBy.role must be text";
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
function isHeadlineMetric(metric) {
|
|
89
|
-
if (metric.source === "measured") return true;
|
|
90
|
-
return metric.source === "reported" && metricSourceProblem(metric) === null;
|
|
91
|
-
}
|
|
92
|
-
function metricSourceLabel(source) {
|
|
93
|
-
if (source === "measured") return "Measured";
|
|
94
|
-
if (source === "reported") return "Client-reported";
|
|
95
|
-
if (source === "estimated") return "Estimated";
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
function formatAttribution(by) {
|
|
99
|
-
const [year, month, day] = by.date.split("-").map(Number);
|
|
100
|
-
const when = MONTHS[month - 1] ? `${MONTHS[month - 1]} ${day}, ${year}` : by.date;
|
|
101
|
-
return [`Reported by ${by.name.trim()}`, by.role?.trim(), by.organization.trim(), when].filter(Boolean).join(", ");
|
|
102
|
-
}
|
|
103
|
-
function shortAttribution(by) {
|
|
104
|
-
return `per ${by.organization.trim()}`;
|
|
105
|
-
}
|
|
106
|
-
function carryReportedMetrics(previous, next, keyOf) {
|
|
107
|
-
const norm = (metric) => String(keyOf(metric) ?? "").trim().toLowerCase();
|
|
108
|
-
const reported = (previous ?? []).filter((m) => m?.source === "reported");
|
|
109
|
-
const taken = new Set(reported.map(norm));
|
|
110
|
-
const generated = (next ?? []).filter((m) => m && m.source !== "reported" && !taken.has(norm(m)));
|
|
111
|
-
return [...reported, ...generated];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export { FRAME_READY_MESSAGE, METRIC_SOURCES, PORTFOLIO_CONTRACT_VERSION, PORTFOLIO_KPI_JSON_SCHEMA, carryReportedMetrics, curateHeroKpis, formatAttribution, isFrameReadyMessage, isHeadlineMetric, isLighthouseKpi, isMetricSource, metricSourceLabel, metricSourceProblem, shortAttribution };
|
package/dist/chunk-NZ4RXN3G.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// src/contracts/llms.ts
|
|
2
|
-
var LLM_GEO_CONTRACT_VERSION = 1;
|
|
3
|
-
var LLMS_PUBLIC_SUMMARY_MAX_LENGTH = 400;
|
|
4
|
-
var LLMS_DISCLAIMER_MAX_LENGTH = 280;
|
|
5
|
-
var MANAGED_LLM_SCHEMA_KNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
6
|
-
"@context",
|
|
7
|
-
"@type",
|
|
8
|
-
"name",
|
|
9
|
-
"description",
|
|
10
|
-
"url",
|
|
11
|
-
"keywords",
|
|
12
|
-
"audience",
|
|
13
|
-
"about",
|
|
14
|
-
"mainEntity",
|
|
15
|
-
"isPartOf",
|
|
16
|
-
"speakable",
|
|
17
|
-
"additionalType"
|
|
18
|
-
]);
|
|
19
|
-
var SENTENCE_SHARE = 0.4;
|
|
20
|
-
var ELLIPSIS = "\u2026";
|
|
21
|
-
var CLOSERS = /* @__PURE__ */ new Set(['"', "'", ")", "]", "\u201D", "\u2019", "\xBB"]);
|
|
22
|
-
var ABBREVIATIONS = /* @__PURE__ */ new Set([
|
|
23
|
-
"mr",
|
|
24
|
-
"mrs",
|
|
25
|
-
"ms",
|
|
26
|
-
"dr",
|
|
27
|
-
"prof",
|
|
28
|
-
"st",
|
|
29
|
-
"mt",
|
|
30
|
-
"ft",
|
|
31
|
-
"jr",
|
|
32
|
-
"sr",
|
|
33
|
-
"inc",
|
|
34
|
-
"ltd",
|
|
35
|
-
"co",
|
|
36
|
-
"corp",
|
|
37
|
-
"vs",
|
|
38
|
-
"no"
|
|
39
|
-
]);
|
|
40
|
-
var TRAILING_SEPARATORS = /[\s.,;:\-\u2013\u2014/&([{]+$/;
|
|
41
|
-
function clipAtWordBoundary(text, max) {
|
|
42
|
-
const flat = text.replace(/\s+/g, " ").trim();
|
|
43
|
-
if (flat.length <= max) return flat;
|
|
44
|
-
if (max < 2) return flat.slice(0, Math.max(0, max));
|
|
45
|
-
const floor = Math.floor(max * (1 - SENTENCE_SHARE));
|
|
46
|
-
for (let end = max; end > floor; end--) {
|
|
47
|
-
if (endsSentence(flat, end)) return flat.slice(0, end);
|
|
48
|
-
}
|
|
49
|
-
const room = max - 1;
|
|
50
|
-
const space = flat.lastIndexOf(" ", room);
|
|
51
|
-
if (space <= 0) return `${flat.slice(0, room)}${ELLIPSIS}`;
|
|
52
|
-
if (endsSentence(flat, space)) return flat.slice(0, space);
|
|
53
|
-
const head = flat.slice(0, space).replace(TRAILING_SEPARATORS, "");
|
|
54
|
-
return `${head || flat.slice(0, room)}${ELLIPSIS}`;
|
|
55
|
-
}
|
|
56
|
-
function endsSentence(flat, end) {
|
|
57
|
-
if (flat[end] !== " ") return false;
|
|
58
|
-
let i = end - 1;
|
|
59
|
-
while (i > 0 && CLOSERS.has(flat[i])) i--;
|
|
60
|
-
const terminator = flat[i];
|
|
61
|
-
if (terminator === "!" || terminator === "?") return true;
|
|
62
|
-
return terminator === "." && !isAbbreviation(flat, i);
|
|
63
|
-
}
|
|
64
|
-
function isAbbreviation(flat, dot) {
|
|
65
|
-
let start = dot;
|
|
66
|
-
while (start > 0 && flat[start - 1] !== " ") start--;
|
|
67
|
-
const token = flat.slice(start, dot).replace(/^[("'[\u201c\u2018]+/, "").toLowerCase();
|
|
68
|
-
if (token.includes(".") || /^[a-z]$/.test(token)) return true;
|
|
69
|
-
return ABBREVIATIONS.has(token);
|
|
70
|
-
}
|
|
71
|
-
function sanitizeLlmsPublicSummary(raw) {
|
|
72
|
-
if (raw === null || raw === void 0) return null;
|
|
73
|
-
if (typeof raw !== "string") return null;
|
|
74
|
-
let s = raw.trim().replace(/\s+/g, " ");
|
|
75
|
-
s = s.replace(/[\[\]\r\n]/g, " ").replace(/\s+/g, " ").trim();
|
|
76
|
-
s = clipAtWordBoundary(s, LLMS_PUBLIC_SUMMARY_MAX_LENGTH);
|
|
77
|
-
if (/\b[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}\b/.test(s)) return null;
|
|
78
|
-
if (/\b\+?\d[\d\s().-]{8,}\b/.test(s)) return null;
|
|
79
|
-
if (s.length < 8) return null;
|
|
80
|
-
return s;
|
|
81
|
-
}
|
|
82
|
-
function sanitizeLlmsDisclaimerLine(raw) {
|
|
83
|
-
if (raw === null || raw === void 0) return null;
|
|
84
|
-
if (typeof raw !== "string") return null;
|
|
85
|
-
let s = raw.trim().replace(/\s+/g, " ");
|
|
86
|
-
s = s.replace(/[\[\]\r\n]/g, " ").replace(/\s+/g, " ").trim();
|
|
87
|
-
s = clipAtWordBoundary(s, LLMS_DISCLAIMER_MAX_LENGTH);
|
|
88
|
-
if (/\b[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}\b/.test(s)) return null;
|
|
89
|
-
if (/\b\+?\d[\d\s().-]{8,}\b/.test(s)) return null;
|
|
90
|
-
if (s.length < 3) return null;
|
|
91
|
-
return s;
|
|
92
|
-
}
|
|
93
|
-
function sanitizePrimaryLanguageTag(raw) {
|
|
94
|
-
if (raw === null || raw === void 0) return null;
|
|
95
|
-
if (typeof raw !== "string") return null;
|
|
96
|
-
const s = raw.trim();
|
|
97
|
-
if (!/^[\w-]{2,20}$/.test(s)) return null;
|
|
98
|
-
return s;
|
|
99
|
-
}
|
|
100
|
-
function isEmittableReviewNode(node) {
|
|
101
|
-
const author = node.author;
|
|
102
|
-
if (typeof author === "string") return author.trim().length > 0;
|
|
103
|
-
if (Array.isArray(author)) {
|
|
104
|
-
return author.some((a) => isEmittableReviewNode({ author: a }));
|
|
105
|
-
}
|
|
106
|
-
if (author && typeof author === "object") {
|
|
107
|
-
const name = author.name;
|
|
108
|
-
return typeof name === "string" && name.trim().length > 0;
|
|
109
|
-
}
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
var DROP = /* @__PURE__ */ Symbol("drop-invalid-review");
|
|
113
|
-
function stripInvalidReviewNodes(value) {
|
|
114
|
-
if (Array.isArray(value)) {
|
|
115
|
-
return value.map((v) => stripInvalidReviewNodes(v)).filter((v) => v !== DROP);
|
|
116
|
-
}
|
|
117
|
-
if (value && typeof value === "object") {
|
|
118
|
-
const src = value;
|
|
119
|
-
if (src["@type"] === "Review" && !isEmittableReviewNode(src)) return DROP;
|
|
120
|
-
const out = {};
|
|
121
|
-
for (const key of Object.keys(src)) {
|
|
122
|
-
const child = stripInvalidReviewNodes(src[key]);
|
|
123
|
-
if (child === DROP) continue;
|
|
124
|
-
out[key] = child;
|
|
125
|
-
}
|
|
126
|
-
return out;
|
|
127
|
-
}
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
function pickManagedLlmSchemaForJsonLd(schema) {
|
|
131
|
-
if (!schema || typeof schema !== "object") return null;
|
|
132
|
-
const out = {};
|
|
133
|
-
const omitFromJsonLd = /* @__PURE__ */ new Set(["llms_public_summary"]);
|
|
134
|
-
for (const key of Object.keys(schema)) {
|
|
135
|
-
if (omitFromJsonLd.has(key)) continue;
|
|
136
|
-
if (MANAGED_LLM_SCHEMA_KNOWN_KEYS.has(key)) {
|
|
137
|
-
const value = stripInvalidReviewNodes(schema[key]);
|
|
138
|
-
if (value === DROP) continue;
|
|
139
|
-
out[key] = value;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return Object.keys(out).length ? out : null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export { LLMS_DISCLAIMER_MAX_LENGTH, LLMS_PUBLIC_SUMMARY_MAX_LENGTH, LLM_GEO_CONTRACT_VERSION, MANAGED_LLM_SCHEMA_KNOWN_KEYS, clipAtWordBoundary, isEmittableReviewNode, pickManagedLlmSchemaForJsonLd, sanitizeLlmsDisclaimerLine, sanitizeLlmsPublicSummary, sanitizePrimaryLanguageTag };
|
package/dist/chunk-OSYCOC3K.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// src/landing/contract.ts
|
|
2
|
-
function normalizeCampaignSlug(input) {
|
|
3
|
-
if (!input || typeof input !== "string") return null;
|
|
4
|
-
const slug = input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
5
|
-
return slug || null;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
// src/landing/metadata.ts
|
|
9
|
-
function landingPageMetadata(meta = {}) {
|
|
10
|
-
const callerRobots = meta.robots && typeof meta.robots === "object" ? meta.robots : void 0;
|
|
11
|
-
return {
|
|
12
|
-
...meta,
|
|
13
|
-
robots: {
|
|
14
|
-
index: false,
|
|
15
|
-
follow: false,
|
|
16
|
-
...callerRobots
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export { landingPageMetadata, normalizeCampaignSlug };
|
|
File without changes
|
|
File without changes
|
|
File without changes
|