@sonordev/contracts 2.2.0 → 2.4.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 +21 -0
- package/README.md +103 -1
- package/dist/color.d.ts +1 -1
- package/dist/error-page.d.ts +183 -0
- package/dist/error-page.js +424 -0
- package/dist/error-page.mjs +416 -0
- package/dist/llms.d.ts +1 -0
- package/dist/llms.js +55 -6
- package/dist/llms.mjs +55 -7
- package/dist/schema-placeholders.d.ts +87 -0
- package/dist/schema-placeholders.js +321 -0
- package/dist/schema-placeholders.mjs +317 -0
- package/package.json +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.0
|
|
4
|
+
|
|
5
|
+
A minor release: one new entry, `@sonordev/contracts/schema-placeholders`, and the llms.txt sanitizers stop cutting mid-word. Every other entry is unchanged.
|
|
6
|
+
|
|
7
|
+
- **New entry `@sonordev/contracts/schema-placeholders`**: whether stored JSON-LD carries a template's unfilled slots, so the API that serves it and the site that renders it drop the same nodes.
|
|
8
|
+
- `withoutSchemaPlaceholders(value)` returns `{ value, dropped, notes }`: the JSON-LD without its placeholder nodes (the same reference when nothing was found, `null` when nothing real is left), every node it removed with the reason and the evidence, and the AI note keys it stripped from nodes it kept.
|
|
9
|
+
- `findSchemaPlaceholders(value)` returns just the nodes, and `describeSchemaPlaceholder(node)` turns one into a line a person can act on.
|
|
10
|
+
- It finds reserved example domains (`example.com`, `example.net`, `example.org`, `.example`) in URLs, hosts and emails, whole-value template names ("Example", "Your Business Name"), placeholder phones, bracketed and JSX slots (`[Resident Name]`, `{plan.name}`, `{{business_name}}`), `REPLACE_WITH_` markers, and objects that are only a note. A URI template's `{search_term_string}`, a real name that contains "Example" and a 555 number outside the fiction range are left alone.
|
|
11
|
+
- **`llms`**: `clipAtWordBoundary(text, max)` cuts prose to a length without splitting a word. It ends at a sentence when one closes in the last 40% of the window, otherwise at the last whole word with an ellipsis, and never returns more than `max` characters. `sanitizeLlmsPublicSummary` and `sanitizeLlmsDisclaimerLine` now cut with it, so a long summary ends at a sentence or a whole word instead of mid-word. The limits are unchanged (400 and 280).
|
|
12
|
+
|
|
13
|
+
## 2.3.0
|
|
14
|
+
|
|
15
|
+
A minor release: one new entry, `@sonordev/contracts/error-page`. Every other entry is unchanged.
|
|
16
|
+
|
|
17
|
+
- **New entry `@sonordev/contracts/error-page`**: telling a rendered error page from a page's content, and copy that calls a working page missing from copy that describes it, so every writer and reader of page text answers the same way.
|
|
18
|
+
- `looksLikeErrorPage(text, wordCount?)` is true when stored text is an error page: a 404 in its common wordings, a framework or server error, a status page, a "something went wrong" or "temporarily unavailable" screen or a maintenance notice. It reads how the text opens, decodes entities, doesn't depend on word boundaries (scraped text runs elements together, as in "404This page could not be found."), and leaves phone numbers, addresses and pages that only describe an error alone.
|
|
19
|
+
- `usablePageText(text, wordCount?)` returns the text, or `null` for an error page.
|
|
20
|
+
- `findErrorPageClaim(copy, pageText?)` and `describesErrorPage(copy, pageText?)` find generated copy that says the page itself is missing or unavailable ("Page Not Found", "this page is currently unavailable", "returns a 404", "a page-not-found message"). A phrase the page's own text really says is allowed, and copy about something unavailable on a working page isn't a claim about the page.
|
|
21
|
+
- `findPageCopyClaim(fields, pageText?)` runs that check over every string in a set of fields, JSON-LD included, and returns the first `{ field, phrase }`.
|
|
22
|
+
- `ERROR_PAGE_MAX_WORDS` and `ERROR_PAGE_CONTRACT_VERSION` are exported alongside.
|
|
23
|
+
|
|
3
24
|
## 2.2.0
|
|
4
25
|
|
|
5
26
|
A minor release: one new entry, `@sonordev/contracts/proposal-sitemap`. Every other entry is unchanged.
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ pnpm add @sonordev/contracts
|
|
|
14
14
|
| `@sonordev/contracts/sites` | The canonical shape of a site host (the multi-site `site` dimension) | every ingest and read that filters by site |
|
|
15
15
|
| `@sonordev/contracts/seo-pages` | Which `seo_pages` row a path resolves to on a multi-site project | the Sonor API, Signal, site-kit's sitemap sync |
|
|
16
16
|
| `@sonordev/contracts/seo-meta` | Whether a title or description is fit to ship | site-kit, Signal's SEO gate |
|
|
17
|
-
| `@sonordev/contracts/llms` | llms.txt sanitizers and contract version | site-kit, the Sonor API, Signal's schema validation |
|
|
17
|
+
| `@sonordev/contracts/llms` | llms.txt sanitizers, the word-safe cut they use (`clipAtWordBoundary`), and the contract version | site-kit, the Sonor API, Signal's schema validation |
|
|
18
18
|
| `@sonordev/contracts/forms` | The honeypot policy | site-kit forms, the Sonor API's form intake |
|
|
19
19
|
| `@sonordev/contracts/fleet` | The fleet heartbeat's wire shape | site-kit, the Sonor API |
|
|
20
20
|
| `@sonordev/contracts/slots` | Slot payload signing, contract v2 (HMAC; Node only). Signs and verifies v1 for older site-kit releases | the Sonor API, site-kit on the server |
|
|
@@ -24,6 +24,42 @@ pnpm add @sonordev/contracts
|
|
|
24
24
|
| `@sonordev/contracts/portfolio` | Where a portfolio number comes from and which may headline; the live-frame message | the Sonor API, Signal, agency-site-kit |
|
|
25
25
|
| `@sonordev/contracts/proposal-sitemap` | How a proposal's site plan counts its pages, when its before-and-after comparison may show, and whether the page counts stated elsewhere in the proposal add up | the Sonor API, Signal's proposal writer, the dashboard's proposal pages |
|
|
26
26
|
| `@sonordev/contracts/voice` | The house voice's contraction rule. `findUncontracted` and `contractText` find and apply full forms that should be contractions; `findContracted` finds the contractions a question-form heading should expand | anything that checks or rewrites copy: Sonor's APIs and dashboard |
|
|
27
|
+
| `@sonordev/contracts/error-page` | Whether stored text is a rendered error page rather than a page's content, and whether generated copy says the page itself is missing | anything that stores page text or writes page copy from it: the Sonor API's page-view and sitemap writers, Signal's metadata generators |
|
|
28
|
+
| `@sonordev/contracts/schema-placeholders` | Whether stored JSON-LD carries a template's unfilled slots (`example.com` URLs, an `Organization` named "Example", a placeholder phone, `[Resident Name]`, `{plan.name}`), and the value without them | site-kit's schema output, the Sonor API's schema reads and writes |
|
|
29
|
+
|
|
30
|
+
## Schema placeholders
|
|
31
|
+
|
|
32
|
+
`schema-placeholders` finds the parts of a JSON-LD value that a template left
|
|
33
|
+
unfilled, so a site never publishes them as the business's identity. Schema
|
|
34
|
+
extracted from a site's source, where the base URL was a variable, comes back
|
|
35
|
+
as `https://example.com`; a template filled in by hand or by an AI can leave
|
|
36
|
+
"Example", `+1-000-000-0000`, `[Resident Name]`, `{plan.name}`, or an object
|
|
37
|
+
that's only a note about what goes there.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { withoutSchemaPlaceholders, describeSchemaPlaceholder } from '@sonordev/contracts/schema-placeholders'
|
|
41
|
+
|
|
42
|
+
const { value, dropped } = withoutSchemaPlaceholders(storedJsonLd)
|
|
43
|
+
// value: the JSON-LD without its placeholder nodes (the same reference when
|
|
44
|
+
// nothing was found, null when nothing real is left)
|
|
45
|
+
dropped.map(describeSchemaPlaceholder)
|
|
46
|
+
// ['Organization at $: name "Example" is a placeholder name']
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **The unit is the node.** The innermost object with an `@type` or `@id` (or
|
|
50
|
+
a top-level or `@graph` member) whose own values hold the placeholder is
|
|
51
|
+
dropped whole. Its real siblings, and a real parent it hangs off, stay. A
|
|
52
|
+
node left with nothing but JSON-LD keywords once its placeholder children are
|
|
53
|
+
gone goes too.
|
|
54
|
+
- **Conservative on purpose.** A false positive removes real schema from a live
|
|
55
|
+
site, so names match whole values only ("Example Plumbing Co" is a real
|
|
56
|
+
name), domains are only the reserved example domains, phones are only
|
|
57
|
+
all-zero, sequential, `XXX` and the 555-0100 to 555-0199 fiction range, and a
|
|
58
|
+
URI template's `{search_term_string}` is how schema.org spells a
|
|
59
|
+
`SearchAction`, never a finding.
|
|
60
|
+
- **Notes.** An AI's `note` key on a real node isn't schema.org vocabulary: it's
|
|
61
|
+
stripped and the node stays. An object that's only a note is dropped.
|
|
62
|
+
- **Pure.** The input is never mutated.
|
|
27
63
|
|
|
28
64
|
## The voice contract
|
|
29
65
|
|
|
@@ -185,6 +221,72 @@ pages") isn't read, and a count of pages that aren't in the plan (say, a
|
|
|
185
221
|
competitor's) is reported like any other; the issues are for a person to look
|
|
186
222
|
at, not edits to apply.
|
|
187
223
|
|
|
224
|
+
## The error-page contract
|
|
225
|
+
|
|
226
|
+
A page's stored text is whatever a browser or crawler rendered. Visit a URL
|
|
227
|
+
while it's briefly broken and the error page's words become that page's
|
|
228
|
+
content, and any title, description or summary written from that content
|
|
229
|
+
describes a working page as broken. `error-page` is how writers refuse both
|
|
230
|
+
halves of that.
|
|
231
|
+
|
|
232
|
+
- **`looksLikeErrorPage(text, wordCount?)`** is true when the text is a
|
|
233
|
+
rendered error page: a 404 in its common wordings ("404", "This page could
|
|
234
|
+
not be found", "Page not found"), a framework error ("a client-side
|
|
235
|
+
exception has occurred", "This page couldn't load", "Internal Server
|
|
236
|
+
Error", "Cannot GET /x"), a status page ("502 Bad Gateway", "HTTP Status
|
|
237
|
+
404"), a "something went wrong" or "temporarily unavailable" screen, or a
|
|
238
|
+
maintenance notice. It reads how the text *opens* (the first 300
|
|
239
|
+
characters), decodes HTML entities, and doesn't depend on word boundaries,
|
|
240
|
+
because scraped text runs elements together ("404This page could not be
|
|
241
|
+
found."). Text up to 120 words is checked for every wording, though the
|
|
242
|
+
loosest ones (a 404 glued to its copy, "refresh the page and try again", a
|
|
243
|
+
route's "<name> not found") only read the first 60 or 40 words; up to 400
|
|
244
|
+
words only the check for how it opens after a meta description and skip link
|
|
245
|
+
applies; beyond that it's prose. A phone number, room number or street
|
|
246
|
+
address that starts with 404 isn't an error page, and neither is a help page
|
|
247
|
+
that only *describes* an error ("how to fix a 404 error", "if something went
|
|
248
|
+
wrong"). Pass the word count of the text itself, never a whole page's count
|
|
249
|
+
(a browser's includes the navigation and footer and can hide a short error
|
|
250
|
+
state): a supplied count can only raise the count the check uses.
|
|
251
|
+
- **`usablePageText(text, wordCount?)`** returns the text, or `null` when it's
|
|
252
|
+
an error page. It's what a model should be shown as "the page's content".
|
|
253
|
+
- **`findErrorPageClaim(copy, pageText?)`** returns the phrase in generated
|
|
254
|
+
copy that says the page itself is missing or unavailable ("Page Not Found",
|
|
255
|
+
"the pricing page is currently unavailable", "returns a 404", "a
|
|
256
|
+
page-not-found message"), or `null`. Copy about something unavailable on a
|
|
257
|
+
working page ("the patio is closed on Mondays", "tours are paused for
|
|
258
|
+
winter") isn't a claim about the page. Pass the page's own text and a phrase
|
|
259
|
+
the page really says is allowed, so a guide to fixing "page not found"
|
|
260
|
+
errors can be titled that way; the text of an error page is never accepted
|
|
261
|
+
as that exemption. `describesErrorPage` is the boolean form.
|
|
262
|
+
- **`findPageCopyClaim(fields, pageText?)`** runs `findErrorPageClaim` over
|
|
263
|
+
every string in a set of fields (a title, a description, keywords, a
|
|
264
|
+
JSON-LD object; any object or class instance) and returns the first
|
|
265
|
+
`{ field, phrase }`, or `null`. It's how a writer or a reader judges a whole
|
|
266
|
+
draft in one call. It reads at most 2,000 strings, 8 levels deep and 100,000
|
|
267
|
+
characters in all, and each string's first 5,000 characters, so a hostile
|
|
268
|
+
record can't make it slow.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
import { describesErrorPage, findPageCopyClaim, usablePageText } from '@sonordev/contracts/error-page'
|
|
272
|
+
|
|
273
|
+
usablePageText('404This page could not be found.') // null: don't generate from it
|
|
274
|
+
usablePageText('Weekly mowing for property managers.') // the text
|
|
275
|
+
|
|
276
|
+
describesErrorPage('Commercial Mowing Page Not Found | Example Co') // true: don't ship it
|
|
277
|
+
describesErrorPage('Seasonal tours are paused until April.') // false
|
|
278
|
+
|
|
279
|
+
findPageCopyClaim({
|
|
280
|
+
title: 'Commercial Mowing | Example Co',
|
|
281
|
+
schema: { '@type': 'WebPage', description: 'This URL currently returns a 404.' },
|
|
282
|
+
})
|
|
283
|
+
// { field: 'schema', phrase: 'returns a 404' }
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
A text-only check can't prove a page failed. Where you observed the HTTP
|
|
287
|
+
status, require 400 or more as well and use this as the fallback; the header of
|
|
288
|
+
`src/contracts/error-page.ts` lists the other known limits.
|
|
289
|
+
|
|
188
290
|
## Why it exists
|
|
189
291
|
|
|
190
292
|
A rule that lives in several copies is how two of them end up disagreeing.
|
package/dist/color.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Single source of truth for turning a CSS colour string into something the
|
|
5
5
|
* kit can reason about. Used at build time by the brand-profile extractor
|
|
6
6
|
* (`src/brand/extract-brand-profile.ts`, theme detection) and in the browser
|
|
7
|
-
* by Echo (`src/
|
|
7
|
+
* by Echo (`src/chat/brand-color.ts`, brand text contrast). Pure, no
|
|
8
8
|
* imports, safe in both.
|
|
9
9
|
*/
|
|
10
10
|
export type Rgb = {
|
|
@@ -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;
|