wiki-formant 0.7.0 → 0.9.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.
Files changed (45) hide show
  1. package/README.md +117 -1
  2. package/dist/blocks.d.ts +34 -0
  3. package/dist/blocks.d.ts.map +1 -1
  4. package/dist/blocks.js +42 -0
  5. package/dist/blocks.js.map +1 -1
  6. package/dist/combobox.d.ts +60 -0
  7. package/dist/combobox.d.ts.map +1 -0
  8. package/dist/combobox.js +59 -0
  9. package/dist/combobox.js.map +1 -0
  10. package/dist/conformance.d.ts.map +1 -1
  11. package/dist/conformance.js +8 -0
  12. package/dist/conformance.js.map +1 -1
  13. package/dist/crawlers.d.ts +45 -0
  14. package/dist/crawlers.d.ts.map +1 -0
  15. package/dist/crawlers.js +80 -0
  16. package/dist/crawlers.js.map +1 -0
  17. package/dist/feed.d.ts +50 -0
  18. package/dist/feed.d.ts.map +1 -0
  19. package/dist/feed.js +94 -0
  20. package/dist/feed.js.map +1 -0
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +5 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/license.d.ts +39 -0
  26. package/dist/license.d.ts.map +1 -0
  27. package/dist/license.js +50 -0
  28. package/dist/license.js.map +1 -0
  29. package/dist/pagination.d.ts +10 -1
  30. package/dist/pagination.d.ts.map +1 -1
  31. package/dist/pagination.js +13 -5
  32. package/dist/pagination.js.map +1 -1
  33. package/dist/react.d.ts +80 -1
  34. package/dist/react.d.ts.map +1 -1
  35. package/dist/react.js +105 -1
  36. package/dist/react.js.map +1 -1
  37. package/dist/revisions.d.ts +99 -0
  38. package/dist/revisions.d.ts.map +1 -0
  39. package/dist/revisions.js +175 -0
  40. package/dist/revisions.js.map +1 -0
  41. package/dist/well-known.d.ts +27 -0
  42. package/dist/well-known.d.ts.map +1 -1
  43. package/dist/well-known.js +30 -0
  44. package/dist/well-known.js.map +1 -1
  45. package/package.json +21 -1
package/dist/feed.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ export declare const escXml: (s: string) => string;
2
+ /**
3
+ * `content:encoded` carries raw HTML, so it travels inside CDATA. A literal
4
+ * `]]>` in the body would close the section early and corrupt every item after
5
+ * it — not just the one that contained it.
6
+ */
7
+ export declare const cdata: (html: string) => string;
8
+ /**
9
+ * Aggregators hard-truncate a description around 150 characters. Trimming on a
10
+ * word boundary first means the cut lands between words rather than inside one.
11
+ */
12
+ export declare function clampWords(s: string, max?: number): string;
13
+ /**
14
+ * Relative hrefs and srcs are meaningless once an item is syndicated, so every
15
+ * internal link is absolutised on the way out. Protocol-relative and anchor-only
16
+ * targets are left alone.
17
+ */
18
+ export declare function absolutise(html: string, baseUrl: string): string;
19
+ export interface FeedItem {
20
+ title: string;
21
+ url: string;
22
+ description: string;
23
+ date: Date;
24
+ /** Becomes an `<enclosure>`. */
25
+ image?: string;
26
+ /** Becomes `content:encoded`. Full-text RSS is what lets a feed be mirrored. */
27
+ html?: string;
28
+ categories?: string[];
29
+ }
30
+ export declare function renderItem(item: FeedItem): string;
31
+ export interface FeedChannel {
32
+ title: string;
33
+ link: string;
34
+ description: string;
35
+ /** The feed's own URL, for `atom:link rel="self"`. */
36
+ self: string;
37
+ language?: string;
38
+ /** Channel-level licence, so the grant travels with the feed. */
39
+ copyright?: string;
40
+ }
41
+ /**
42
+ * `lastBuildDate` comes from the newest item, not from the clock.
43
+ *
44
+ * A feed whose build date moves on every request tells every poller it changed
45
+ * when it did not, which is exactly the recrawl the conditional-GET helpers
46
+ * exist to prevent. An empty feed has no build date rather than a fictional one.
47
+ */
48
+ export declare function renderFeed(channel: FeedChannel, items: readonly FeedItem[]): string;
49
+ export declare const FEED_HEADERS: Record<string, string>;
50
+ //# sourceMappingURL=feed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AA0BA,eAAO,MAAM,MAAM,GAAI,GAAG,MAAM,KAAG,MAAyD,CAAC;AAE7F;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,KAAG,MAAyD,CAAC;AAE/F;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAKvD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;IACX,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAajD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,CAoBnF;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG/C,CAAC"}
package/dist/feed.js ADDED
@@ -0,0 +1,94 @@
1
+ // feed.ts — an RSS 2.0 channel, assembled.
2
+ //
3
+ // Three repos, four feeds, and the same 47 of 64 significant lines in each —
4
+ // including, verbatim in two of them, the four-line comment explaining why the
5
+ // apostrophe uses a numeric reference rather than `&apos;`. That comment
6
+ // travelling by copy-paste is what made this worth lifting.
7
+ //
8
+ // The union, because each copy had something the others lacked: one had
9
+ // `content:encoded`, `<enclosure>`, `<category>` and a word-boundary clamp; two
10
+ // had a `lastBuildDate` derived from the newest item, where the third stamped
11
+ // `new Date()` on every request — a validator that changes when nothing has,
12
+ // which is the same waste the ETag helpers exist to avoid.
13
+ //
14
+ // What stays with the caller is the channel's identity and the block walk that
15
+ // turns a page into HTML. Every project owns its own type set.
16
+ // Numeric reference for the apostrophe: `&apos;` is an XML entity that older
17
+ // readers parsing the feed as HTML do not carry in their entity table.
18
+ const XML_ESCAPES = {
19
+ '&': '&amp;',
20
+ '<': '&lt;',
21
+ '>': '&gt;',
22
+ '"': '&quot;',
23
+ "'": '&#39;',
24
+ };
25
+ export const escXml = (s) => s.replace(/[&<>"']/g, c => XML_ESCAPES[c] ?? c);
26
+ /**
27
+ * `content:encoded` carries raw HTML, so it travels inside CDATA. A literal
28
+ * `]]>` in the body would close the section early and corrupt every item after
29
+ * it — not just the one that contained it.
30
+ */
31
+ export const cdata = (html) => `<![CDATA[${html.replace(/]]>/g, ']]&gt;')}]]>`;
32
+ /**
33
+ * Aggregators hard-truncate a description around 150 characters. Trimming on a
34
+ * word boundary first means the cut lands between words rather than inside one.
35
+ */
36
+ export function clampWords(s, max = 150) {
37
+ if (s.length <= max)
38
+ return s;
39
+ const cut = s.slice(0, max);
40
+ const lastSpace = cut.lastIndexOf(' ');
41
+ return (lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[.,;:–-]$/, '');
42
+ }
43
+ /**
44
+ * Relative hrefs and srcs are meaningless once an item is syndicated, so every
45
+ * internal link is absolutised on the way out. Protocol-relative and anchor-only
46
+ * targets are left alone.
47
+ */
48
+ export function absolutise(html, baseUrl) {
49
+ return html.replace(/\s(href|src)="\/(?!\/)([^"]*)"/gi, (_m, attr, path) => ` ${attr}="${baseUrl}/${path}"`);
50
+ }
51
+ export function renderItem(item) {
52
+ return [
53
+ ' <item>',
54
+ ` <title>${escXml(item.title)}</title>`,
55
+ ` <link>${escXml(item.url)}</link>`,
56
+ ` <guid isPermaLink="true">${escXml(item.url)}</guid>`,
57
+ ` <description>${escXml(item.description)}</description>`,
58
+ ...(item.categories ?? []).map(c => ` <category>${escXml(c)}</category>`),
59
+ ` <pubDate>${item.date.toUTCString()}</pubDate>`,
60
+ ...(item.image ? [` <enclosure url="${escXml(item.image)}" type="image/png" />`] : []),
61
+ ...(item.html ? [` <content:encoded>${cdata(item.html)}</content:encoded>`] : []),
62
+ ' </item>',
63
+ ].join('\n');
64
+ }
65
+ /**
66
+ * `lastBuildDate` comes from the newest item, not from the clock.
67
+ *
68
+ * A feed whose build date moves on every request tells every poller it changed
69
+ * when it did not, which is exactly the recrawl the conditional-GET helpers
70
+ * exist to prevent. An empty feed has no build date rather than a fictional one.
71
+ */
72
+ export function renderFeed(channel, items) {
73
+ const newest = items.reduce((max, item) => (!max || item.date > max ? item.date : max), null);
74
+ return [
75
+ '<?xml version="1.0" encoding="UTF-8"?>',
76
+ '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">',
77
+ ' <channel>',
78
+ ` <title>${escXml(channel.title)}</title>`,
79
+ ` <link>${escXml(channel.link)}</link>`,
80
+ ` <description>${escXml(channel.description)}</description>`,
81
+ ` <language>${escXml(channel.language ?? 'en')}</language>`,
82
+ ...(channel.copyright ? [` <copyright>${escXml(channel.copyright)}</copyright>`] : []),
83
+ ` <atom:link href="${escXml(channel.self)}" rel="self" type="application/rss+xml" />`,
84
+ ...(newest ? [` <lastBuildDate>${newest.toUTCString()}</lastBuildDate>`] : []),
85
+ ...items.map(renderItem),
86
+ ' </channel>',
87
+ '</rss>',
88
+ ].join('\n');
89
+ }
90
+ export const FEED_HEADERS = {
91
+ 'Content-Type': 'application/rss+xml; charset=utf-8',
92
+ 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
93
+ };
94
+ //# sourceMappingURL=feed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -13,4 +13,9 @@ export * from './blocks.js';
13
13
  export * from './text.js';
14
14
  export * from './maps.js';
15
15
  export * from './freshness.js';
16
+ export * from './combobox.js';
17
+ export * from './crawlers.js';
18
+ export * from './revisions.js';
19
+ export * from './feed.js';
20
+ export * from './license.js';
16
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -18,4 +18,9 @@ export * from './blocks.js';
18
18
  export * from './text.js';
19
19
  export * from './maps.js';
20
20
  export * from './freshness.js';
21
+ export * from './combobox.js';
22
+ export * from './crawlers.js';
23
+ export * from './revisions.js';
24
+ export * from './feed.js';
25
+ export * from './license.js';
21
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,mBAAmB;AAEnB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,mBAAmB;AAEnB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,39 @@
1
+ export interface License {
2
+ /** SPDX identifier, e.g. `CC-BY-4.0`. */
3
+ spdx: string;
4
+ /** Human name, e.g. `Creative Commons Attribution 4.0 International`. */
5
+ name: string;
6
+ url: string;
7
+ /** The credit line a dataset should carry. */
8
+ attribution: string;
9
+ }
10
+ /** The grant every one of these wikis makes, parameterised by who is making it. */
11
+ export declare function ccBy40(opts: {
12
+ siteName: string;
13
+ siteUrl: string;
14
+ }): License;
15
+ export interface LicenseBlockOptions {
16
+ license: License;
17
+ /**
18
+ * What the grant covers, as a sentence fragment completing "… is licensed
19
+ * under". Defaults to naming the whole site, which is the rarer case: every
20
+ * consumer of this so far grants over some of what it serves and not all.
21
+ */
22
+ scope?: string;
23
+ /** Sentences appended after the identifiers — carve-outs, caveats, terms. */
24
+ excludes?: readonly string[];
25
+ /** `Licence` where the site's prose says so. Defaults to `License`. */
26
+ heading?: string;
27
+ }
28
+ /**
29
+ * The block, as lines, ready to join into any text export.
30
+ *
31
+ * Returned as an array rather than a string because every caller splices it
32
+ * into a document it is already building line by line, and a caller that wants
33
+ * the string can join it.
34
+ */
35
+ export declare function licenseLines({ license, scope, excludes, heading, }: LicenseBlockOptions): string[];
36
+ export declare function licenseBlock(opts: LicenseBlockOptions): string;
37
+ /** The one-line form, for a frontmatter field or a feed's `<copyright>`. */
38
+ export declare function licenseNote(license: License): string;
39
+ //# sourceMappingURL=license.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,OAAO;IACtB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,mFAAmF;AACnF,wBAAgB,MAAM,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAO3E;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,KAAsB,EACtB,QAAa,EACb,OAAiC,GAClC,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAchC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,50 @@
1
+ // license.ts — the licence declaration every agent surface has to carry.
2
+ //
3
+ // S10 requires a licence on every export — llms.txt, the agent card, the
4
+ // OpenAPI info block, the JSON-LD, the feed's `<copyright>` — and each repo
5
+ // satisfied that requirement by writing the same block again. The shape was
6
+ // identical in all three, and the sentence beginning "You may ingest, embed,
7
+ // and redistribute" was verbatim in all three.
8
+ //
9
+ // What is genuinely per-project is the scope: which half of a site the grant
10
+ // covers, and what it deliberately excludes. That is the `scope` and `excludes`
11
+ // below, and it is the only part a consumer should be writing by hand.
12
+ /** The grant every one of these wikis makes, parameterised by who is making it. */
13
+ export function ccBy40(opts) {
14
+ return {
15
+ spdx: 'CC-BY-4.0',
16
+ name: 'Creative Commons Attribution 4.0 International',
17
+ url: 'https://creativecommons.org/licenses/by/4.0/',
18
+ attribution: `Source: ${opts.siteName} (${opts.siteUrl}), CC BY 4.0`,
19
+ };
20
+ }
21
+ /**
22
+ * The block, as lines, ready to join into any text export.
23
+ *
24
+ * Returned as an array rather than a string because every caller splices it
25
+ * into a document it is already building line by line, and a caller that wants
26
+ * the string can join it.
27
+ */
28
+ export function licenseLines({ license, scope = 'This content', excludes = [], heading = 'License & Attribution', }) {
29
+ return [
30
+ `## ${heading}`,
31
+ '',
32
+ `${scope} is licensed under the ${license.name} (${license.spdx}): ${license.url}`,
33
+ '',
34
+ 'You may ingest, embed, and redistribute this content in RAG systems, fine-tuning',
35
+ 'datasets, or other derivative works, including commercially. Attribution at the',
36
+ 'dataset or system level is sufficient.',
37
+ '',
38
+ `- Recommended attribution: "${license.attribution}"`,
39
+ `- SPDX identifier: ${license.spdx}`,
40
+ ...(excludes.length ? ['', ...excludes] : []),
41
+ ];
42
+ }
43
+ export function licenseBlock(opts) {
44
+ return licenseLines(opts).join('\n');
45
+ }
46
+ /** The one-line form, for a frontmatter field or a feed's `<copyright>`. */
47
+ export function licenseNote(license) {
48
+ return `${license.name} (${license.spdx}): ${license.url}`;
49
+ }
50
+ //# sourceMappingURL=license.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license.js","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,yEAAyE;AACzE,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,+CAA+C;AAC/C,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,uEAAuE;AAYvE,mFAAmF;AACnF,MAAM,UAAU,MAAM,CAAC,IAA2C;IAChE,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,gDAAgD;QACtD,GAAG,EAAE,8CAA8C;QACnD,WAAW,EAAE,WAAW,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,cAAc;KACrE,CAAC;AACJ,CAAC;AAgBD;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,OAAO,EACP,KAAK,GAAG,cAAc,EACtB,QAAQ,GAAG,EAAE,EACb,OAAO,GAAG,uBAAuB,GACb;IACpB,OAAO;QACL,MAAM,OAAO,EAAE;QACf,EAAE;QACF,GAAG,KAAK,0BAA0B,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE;QAClF,EAAE;QACF,kFAAkF;QAClF,iFAAiF;QACjF,wCAAwC;QACxC,EAAE;QACF,+BAA+B,OAAO,CAAC,WAAW,GAAG;QACrD,sBAAsB,OAAO,CAAC,IAAI,EAAE;QACpC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAyB;IACpD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC;AAC7D,CAAC"}
@@ -9,9 +9,18 @@ export interface PaginatedResponse<T> extends Pagination {
9
9
  }
10
10
  export declare const MAX_PAGE_SIZE = 100;
11
11
  export declare const DEFAULT_PAGE_SIZE = 20;
12
- /** `page` clamped to ≥1, `pageSize` clamped to 1–100. Never trust either. */
12
+ /**
13
+ * `page` clamped to ≥1, `pageSize` clamped to 1–`max`. Never trust either.
14
+ *
15
+ * `max` defaults to 100 and exists because the cap is a per-route decision, not
16
+ * a package-wide one: a route whose rows are whole pages caps lower than one
17
+ * whose rows are titles. Without it the one consumer that caps at 50 could not
18
+ * express itself here, so it kept its own clamp — and then disagreed with
19
+ * itself, capping at 50 in the REST route and 100 in the MCP tool beside it.
20
+ */
13
21
  export declare function parsePagination(searchParams: URLSearchParams, defaults?: {
14
22
  pageSize?: number;
23
+ max?: number;
15
24
  }): Pagination;
16
25
  export declare function paginatedResponse<T>(items: T[], total: number, page: number, pageSize: number): PaginatedResponse<T>;
17
26
  /** The `skip`/`take` an ORM wants, from the same clamped pair. */
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,UAAU;IACtD,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,6EAA6E;AAC7E,wBAAgB,eAAe,CAC7B,YAAY,EAAE,eAAe,EAC7B,QAAQ,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,UAAU,CAYZ;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,iBAAiB,CAAC,CAAC,CAAC,CAEtB;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAEvF"}
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC,CAAE,SAAQ,UAAU;IACtD,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,eAAe,EAC7B,QAAQ,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,UAAU,CASZ;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,iBAAiB,CAAC,CAAC,CAAC,CAEtB;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAEvF"}
@@ -5,15 +5,23 @@
5
5
  // codes against, so reshaping it per repo is a breaking change nobody declared.
6
6
  export const MAX_PAGE_SIZE = 100;
7
7
  export const DEFAULT_PAGE_SIZE = 20;
8
- /** `page` clamped to ≥1, `pageSize` clamped to 1–100. Never trust either. */
8
+ /**
9
+ * `page` clamped to ≥1, `pageSize` clamped to 1–`max`. Never trust either.
10
+ *
11
+ * `max` defaults to 100 and exists because the cap is a per-route decision, not
12
+ * a package-wide one: a route whose rows are whole pages caps lower than one
13
+ * whose rows are titles. Without it the one consumer that caps at 50 could not
14
+ * express itself here, so it kept its own clamp — and then disagreed with
15
+ * itself, capping at 50 in the REST route and 100 in the MCP tool beside it.
16
+ */
9
17
  export function parsePagination(searchParams, defaults) {
18
+ const max = defaults?.max ?? MAX_PAGE_SIZE;
19
+ const fallbackSize = Math.min(max, defaults?.pageSize ?? DEFAULT_PAGE_SIZE);
10
20
  const rawPage = parseInt(searchParams.get('page') || '1', 10);
11
- const rawSize = parseInt(searchParams.get('pageSize') || String(defaults?.pageSize ?? DEFAULT_PAGE_SIZE), 10);
21
+ const rawSize = parseInt(searchParams.get('pageSize') || String(fallbackSize), 10);
12
22
  return {
13
23
  page: Number.isFinite(rawPage) ? Math.max(1, rawPage) : 1,
14
- pageSize: Number.isFinite(rawSize)
15
- ? Math.min(MAX_PAGE_SIZE, Math.max(1, rawSize))
16
- : (defaults?.pageSize ?? DEFAULT_PAGE_SIZE),
24
+ pageSize: Number.isFinite(rawSize) ? Math.min(max, Math.max(1, rawSize)) : fallbackSize,
17
25
  };
18
26
  }
19
27
  export function paginatedResponse(items, total, page, pageSize) {
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAahF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAC7B,YAA6B,EAC7B,QAAgC;IAEhC,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,QAAQ,CACtB,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,QAAQ,IAAI,iBAAiB,CAAC,EAC/E,EAAE,CACH,CAAC;IACF,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAChC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,IAAI,iBAAiB,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAU,EACV,KAAa,EACb,IAAY,EACZ,QAAgB;IAEhB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAc;IACrD,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAahF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,YAA6B,EAC7B,QAA8C;IAE9C,MAAM,GAAG,GAAG,QAAQ,EAAE,GAAG,IAAI,aAAa,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,IAAI,iBAAiB,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;IACnF,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;KACxF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAU,EACV,KAAa,EACb,IAAY,EACZ,QAAgB;IAEhB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAc;IACrD,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC"}
package/dist/react.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type CSSProperties, type KeyboardEvent, type ReactNode } from 'react';
1
+ import { type ComboboxAria } from './combobox.js';
2
+ import { Component, type CSSProperties, type KeyboardEvent, type ReactNode } from 'react';
2
3
  export interface SidebarOptions {
3
4
  /**
4
5
  * Where the reader's choice is remembered, in `localStorage`. Give each wiki
@@ -151,6 +152,15 @@ export interface TypeaheadState<T> {
151
152
  clearItems: () => void;
152
153
  /** Drop everything. For a pick, or a closing popover. */
153
154
  reset: () => void;
155
+ /**
156
+ * The listbox contract, ready to spread: `inputProps` on the field,
157
+ * `listProps` on the results container, `optionProps(i)` on each row.
158
+ *
159
+ * Pass a `listKey` when one hook feeds more than one rendered list — a header
160
+ * with a desktop and a mobile field renders both into the same document, and
161
+ * one id set across the two duplicates every option id.
162
+ */
163
+ combobox: (listKey?: string, open?: boolean) => ComboboxAria;
154
164
  }
155
165
  /**
156
166
  * The debounced-search state machine behind every typeahead on these sites.
@@ -213,4 +223,73 @@ export interface LinkPreviewState<T> {
213
223
  * three things, and those three are the options above.
214
224
  */
215
225
  export declare function useLinkPreview<T>({ eligible, fetch, cardClassName, cardWidth, cardHeight, showDelay, hideDelay, margin, }: LinkPreviewOptions<T>): LinkPreviewState<T>;
226
+ export type SortDirection = 'asc' | 'desc';
227
+ export interface TableSortState<T, K extends string> {
228
+ sorted: T[];
229
+ sortKey: K;
230
+ direction: SortDirection;
231
+ /** Spread onto a header cell. `active` is what drives `aria-sort`. */
232
+ headerProps: (key: K) => {
233
+ sortKey: K;
234
+ active: boolean;
235
+ direction: SortDirection;
236
+ onSort: (key: K) => void;
237
+ 'aria-sort': 'ascending' | 'descending' | 'none';
238
+ };
239
+ toggle: (key: K) => void;
240
+ }
241
+ /**
242
+ * One active column, direction toggling on re-click, every table the same.
243
+ *
244
+ * One repo had this as a hook; the other inlined the identical state machine in
245
+ * two chart tables, differing only in the `useState` seed and the comparator
246
+ * payload. The comparator record replaces the `if`-chain those copies used, so
247
+ * adding a column is a map entry rather than another branch.
248
+ *
249
+ * `aria-sort` is emitted here because only one of the two header components had
250
+ * it, and a sortable column that never announces its direction is a button whose
251
+ * effect a screen reader cannot observe.
252
+ */
253
+ export declare function useTableSort<T, K extends string>(rows: readonly T[], config: {
254
+ defaultKey: K;
255
+ comparators: Record<K, (a: T, b: T) => number>;
256
+ defaultDirection?: SortDirection;
257
+ }): TableSortState<T, K>;
258
+ /**
259
+ * "Copied", then not, after a beat. Six call sites across three repos wrote the
260
+ * same three statements, and four of the six had no rejection handler — so a
261
+ * denied clipboard permission left the button claiming success. The imperative
262
+ * twin in `wiki-formant/dom` has handled that since it was extracted.
263
+ *
264
+ * The key form is for a surface with several copyable things and one indicator:
265
+ * `copied === 'cite'` rather than a boolean per button.
266
+ */
267
+ export declare function useCopy<K extends string = string>(revertAfter?: number): {
268
+ copied: K | null;
269
+ copy: (text: string, key?: K) => Promise<boolean>;
270
+ };
271
+ /**
272
+ * The one thing hooks cannot express, so it is worth owning once.
273
+ *
274
+ * `fallback` is a render prop taking a `retry` closure, which is the better of
275
+ * the two shapes this was lifted from: the other hardcoded its own "Try again"
276
+ * button and so could not be restyled or relabelled by a caller. Recovery is the
277
+ * caller's, and the boundary only decides when to offer it.
278
+ */
279
+ export declare class ErrorBoundary extends Component<{
280
+ children: ReactNode;
281
+ fallback: (retry: () => void) => ReactNode;
282
+ onError?: (error: unknown) => void;
283
+ }, {
284
+ failed: boolean;
285
+ }> {
286
+ state: {
287
+ failed: boolean;
288
+ };
289
+ static getDerivedStateFromError(): {
290
+ failed: boolean;
291
+ };
292
+ componentDidCatch(error: unknown): void;
293
+ render(): ReactNode;
294
+ }
216
295
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
package/dist/react.js CHANGED
@@ -27,7 +27,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
27
27
  // state on <html> before first paint. The hook keeps that attribute in
28
28
  // sync afterwards, so CSS has one source of truth either side of hydration.
29
29
  import { resolveSidebarOpen, SIDEBAR_ATTRIBUTE } from './sidebar.js';
30
- import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
30
+ import { comboboxAria } from './combobox.js';
31
+ import { Component, createContext, createElement, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, } from 'react';
31
32
  const readStored = (key) => {
32
33
  try {
33
34
  const raw = window.localStorage.getItem(key);
@@ -355,6 +356,10 @@ export function useTypeahead({ fetch, onPick, onEscape, delay = 200, minLength =
355
356
  onPick(target);
356
357
  }
357
358
  };
359
+ // The ids have to be stable per mount and unique per surface; `useId` is what
360
+ // makes a second typeahead on the same page legal.
361
+ const baseId = useId();
362
+ const combobox = useCallback((listKey, open) => comboboxAria({ baseId, listKey, count: items.length, highlight, open }), [baseId, items.length, highlight]);
358
363
  return {
359
364
  query,
360
365
  setQuery,
@@ -366,6 +371,7 @@ export function useTypeahead({ fetch, onPick, onEscape, delay = 200, minLength =
366
371
  onKeyDown,
367
372
  clearItems,
368
373
  reset,
374
+ combobox,
369
375
  };
370
376
  }
371
377
  /**
@@ -467,4 +473,102 @@ export function useLinkPreview({ eligible, fetch, cardClassName = 'link-preview-
467
473
  },
468
474
  };
469
475
  }
476
+ /**
477
+ * One active column, direction toggling on re-click, every table the same.
478
+ *
479
+ * One repo had this as a hook; the other inlined the identical state machine in
480
+ * two chart tables, differing only in the `useState` seed and the comparator
481
+ * payload. The comparator record replaces the `if`-chain those copies used, so
482
+ * adding a column is a map entry rather than another branch.
483
+ *
484
+ * `aria-sort` is emitted here because only one of the two header components had
485
+ * it, and a sortable column that never announces its direction is a button whose
486
+ * effect a screen reader cannot observe.
487
+ */
488
+ export function useTableSort(rows, config) {
489
+ const { defaultKey, comparators, defaultDirection = 'desc' } = config;
490
+ const [sortKey, setSortKey] = useState(defaultKey);
491
+ const [direction, setDirection] = useState(defaultDirection);
492
+ const toggle = useCallback((key) => {
493
+ if (key === sortKey)
494
+ setDirection(d => (d === 'desc' ? 'asc' : 'desc'));
495
+ else {
496
+ setSortKey(key);
497
+ setDirection(defaultDirection);
498
+ }
499
+ }, [sortKey, defaultDirection]);
500
+ const sorted = useMemo(() => {
501
+ const comparator = comparators[sortKey];
502
+ if (!comparator)
503
+ return [...rows];
504
+ const multiplier = direction === 'asc' ? 1 : -1;
505
+ return [...rows].sort((a, b) => multiplier * comparator(a, b));
506
+ }, [rows, sortKey, direction, comparators]);
507
+ const headerProps = useCallback((key) => {
508
+ const active = key === sortKey;
509
+ return {
510
+ sortKey: key,
511
+ active,
512
+ direction,
513
+ onSort: toggle,
514
+ 'aria-sort': (active ? (direction === 'asc' ? 'ascending' : 'descending') : 'none'),
515
+ };
516
+ }, [sortKey, direction, toggle]);
517
+ return { sorted, sortKey, direction, headerProps, toggle };
518
+ }
519
+ // ---- copy to clipboard ------------------------------------------------------
520
+ /**
521
+ * "Copied", then not, after a beat. Six call sites across three repos wrote the
522
+ * same three statements, and four of the six had no rejection handler — so a
523
+ * denied clipboard permission left the button claiming success. The imperative
524
+ * twin in `wiki-formant/dom` has handled that since it was extracted.
525
+ *
526
+ * The key form is for a surface with several copyable things and one indicator:
527
+ * `copied === 'cite'` rather than a boolean per button.
528
+ */
529
+ export function useCopy(revertAfter = 2000) {
530
+ const [copied, setCopied] = useState(null);
531
+ const timer = useRef(null);
532
+ // A component unmounted inside the revert window would otherwise set state on
533
+ // a dead tree.
534
+ useEffect(() => () => { if (timer.current)
535
+ clearTimeout(timer.current); }, []);
536
+ const copy = useCallback(async (text, key) => {
537
+ try {
538
+ await navigator.clipboard.writeText(text);
539
+ }
540
+ catch {
541
+ return false;
542
+ }
543
+ setCopied((key ?? 'copied'));
544
+ if (timer.current)
545
+ clearTimeout(timer.current);
546
+ timer.current = setTimeout(() => setCopied(null), revertAfter);
547
+ return true;
548
+ }, [revertAfter]);
549
+ return { copied, copy };
550
+ }
551
+ // ---- error boundary ---------------------------------------------------------
552
+ /**
553
+ * The one thing hooks cannot express, so it is worth owning once.
554
+ *
555
+ * `fallback` is a render prop taking a `retry` closure, which is the better of
556
+ * the two shapes this was lifted from: the other hardcoded its own "Try again"
557
+ * button and so could not be restyled or relabelled by a caller. Recovery is the
558
+ * caller's, and the boundary only decides when to offer it.
559
+ */
560
+ export class ErrorBoundary extends Component {
561
+ state = { failed: false };
562
+ static getDerivedStateFromError() {
563
+ return { failed: true };
564
+ }
565
+ componentDidCatch(error) {
566
+ this.props.onError?.(error);
567
+ }
568
+ render() {
569
+ if (this.state.failed)
570
+ return this.props.fallback(() => this.setState({ failed: false }));
571
+ return this.props.children;
572
+ }
573
+ }
470
574
  //# sourceMappingURL=react.js.map