wiki-formant 0.46.0 → 0.47.1
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/README.md +7 -0
- package/dist/metadata.d.ts +21 -0
- package/dist/metadata.js +49 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -431,6 +431,13 @@ The schema.org nodes sit beside it, because they state the same facts to a diffe
|
|
|
431
431
|
<JsonLd data={articleLd({ headline: title, url, image, published, modified, publisher: { '@id': `${SITE_URL}/#organization` }, citation: citationsFromReferences(refs) })} />
|
|
432
432
|
```
|
|
433
433
|
|
|
434
|
+
`questionPairs(html)` reads a page's FAQ out of its own headings: every `h2`/`h3` ending in `?`, answered by the text beneath it. It never rewrites a statement heading as a question, so a page earns `FAQPage` by being written as one. `faqPageLd(pairs, url)` is that node, or null under two pairs, and takes an authored list just as well.
|
|
435
|
+
|
|
436
|
+
```tsx
|
|
437
|
+
const faq = faqPageLd(questionPairs(html), url);
|
|
438
|
+
{faq && <JsonLd data={faq} />}
|
|
439
|
+
```
|
|
440
|
+
|
|
434
441
|
`sitePageMetadata({ siteName, handle })` binds the three fields Next drops from every page that sets its own card, with `locale` defaulting to `en_US`. `siteGraphLd` is the Organization, WebSite (with its `SearchAction`) and `WebAPI` as one `@graph` for the root layout, every node carrying an `@id`, and `siteRefs(url)` hands out the same ids for a page's `publisher` and `isPartOf` — so no page writes an inline copy of the publisher that can disagree with the layout's.
|
|
435
442
|
|
|
436
443
|
```ts
|
package/dist/metadata.d.ts
CHANGED
|
@@ -121,6 +121,27 @@ export declare function collectionLd(o: CollectionLdOptions): LdNode;
|
|
|
121
121
|
* an authored `&` is not what a crawler reads.
|
|
122
122
|
*/
|
|
123
123
|
export declare function citationsFromReferences(items: readonly ReferenceItem[], max?: number): LdNode[];
|
|
124
|
+
export interface QaPair {
|
|
125
|
+
q: string;
|
|
126
|
+
a: string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The question/answer pairs in a page's HTML: every `h2`/`h3` that ends in a
|
|
130
|
+
* question mark, with the text up to the next such heading as its answer.
|
|
131
|
+
*
|
|
132
|
+
* It never turns a statement heading into a question. Markup that rewrites
|
|
133
|
+
* "Protocol fees" as "What is Protocol fees?" claims a question the page never
|
|
134
|
+
* asked, so a page earns an FAQ by being written as one. An answer under 40
|
|
135
|
+
* characters is a heading with no section, and is skipped.
|
|
136
|
+
*/
|
|
137
|
+
export declare function questionPairs(html: string): QaPair[];
|
|
138
|
+
/**
|
|
139
|
+
* `FAQPage` over question/answer pairs, or null under two. One question is an
|
|
140
|
+
* article with a question for a heading, not an FAQ. Pairs come from
|
|
141
|
+
* `questionPairs` on stored HTML, or from a list the caller authors directly
|
|
142
|
+
* as plain text, so a home-page FAQ and a wiki article render the same node.
|
|
143
|
+
*/
|
|
144
|
+
export declare function faqPageLd(pairs: readonly QaPair[], url: string): LdNode | null;
|
|
124
145
|
/**
|
|
125
146
|
* Characters a document `<title>` has before a search result truncates it.
|
|
126
147
|
*
|
package/dist/metadata.js
CHANGED
|
@@ -107,6 +107,55 @@ export function citationsFromReferences(items, max = 50) {
|
|
|
107
107
|
})
|
|
108
108
|
.slice(0, max);
|
|
109
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* The question/answer pairs in a page's HTML: every `h2`/`h3` that ends in a
|
|
112
|
+
* question mark, with the text up to the next such heading as its answer.
|
|
113
|
+
*
|
|
114
|
+
* It never turns a statement heading into a question. Markup that rewrites
|
|
115
|
+
* "Protocol fees" as "What is Protocol fees?" claims a question the page never
|
|
116
|
+
* asked, so a page earns an FAQ by being written as one. An answer under 40
|
|
117
|
+
* characters is a heading with no section, and is skipped.
|
|
118
|
+
*/
|
|
119
|
+
export function questionPairs(html) {
|
|
120
|
+
// Plain text, not `stripHtml`, which keeps an href in parentheses for a model
|
|
121
|
+
// following citations: a heading's permalink anchor would end it in "(#id)"
|
|
122
|
+
// rather than "?". The anchor itself goes, glyph and all.
|
|
123
|
+
const plain = (s) => decodeEntities(s.replace(/<a\b[^>]*href="#[^"]*"[^>]*>[\s\S]*?<\/a>/gi, '').replace(/<[^>]+>/g, ' '))
|
|
124
|
+
.replace(/\s+/g, ' ')
|
|
125
|
+
.trim();
|
|
126
|
+
const pairs = [];
|
|
127
|
+
const sections = html.split(/<h[23][^>]*>/i);
|
|
128
|
+
for (const section of sections.slice(1)) {
|
|
129
|
+
const [heading = '', ...rest] = section.split(/<\/h[23]>/i);
|
|
130
|
+
const q = plain(heading).replace(/\s+([?.,;:!])/g, '$1');
|
|
131
|
+
if (!q.endsWith('?'))
|
|
132
|
+
continue;
|
|
133
|
+
const a = plain(rest.join('')).replace(/\s+([?.,;:!])/g, '$1');
|
|
134
|
+
if (a.length >= 40)
|
|
135
|
+
pairs.push({ q, a: a.slice(0, 1000) });
|
|
136
|
+
}
|
|
137
|
+
return pairs;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* `FAQPage` over question/answer pairs, or null under two. One question is an
|
|
141
|
+
* article with a question for a heading, not an FAQ. Pairs come from
|
|
142
|
+
* `questionPairs` on stored HTML, or from a list the caller authors directly
|
|
143
|
+
* as plain text, so a home-page FAQ and a wiki article render the same node.
|
|
144
|
+
*/
|
|
145
|
+
export function faqPageLd(pairs, url) {
|
|
146
|
+
if (pairs.length < 2)
|
|
147
|
+
return null;
|
|
148
|
+
return {
|
|
149
|
+
'@context': 'https://schema.org',
|
|
150
|
+
'@type': 'FAQPage',
|
|
151
|
+
url,
|
|
152
|
+
mainEntity: pairs.map(p => ({
|
|
153
|
+
'@type': 'Question',
|
|
154
|
+
name: p.q,
|
|
155
|
+
acceptedAnswer: { '@type': 'Answer', text: p.a },
|
|
156
|
+
})),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
110
159
|
/**
|
|
111
160
|
* Characters a document `<title>` has before a search result truncates it.
|
|
112
161
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-formant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.1",
|
|
4
4
|
"description": "The portable half of a wiki: derived taxonomy and facet controls, a version-negotiating MCP transport, markdown twins, block rendering, a rich-text editor engine, conditional-GET plumbing, and the headers and CSP every origin sends.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|