wiki-formant 0.17.1 → 0.18.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/bin/check-classes.mjs +44 -7
- package/dist/block-views.js +6 -7
- package/dist/combobox.js +6 -15
- package/dist/conformance.js +4 -6
- package/dist/crawlers.js +6 -12
- package/dist/dom.d.ts +1 -2
- package/dist/dom.js +1 -2
- package/dist/editor.d.ts +1 -24
- package/dist/editor.js +10 -19
- package/dist/headings.js +1 -8
- package/dist/link-check.js +6 -9
- package/dist/maps.d.ts +0 -2
- package/dist/maps.js +1 -1
- package/dist/mcp.d.ts +0 -4
- package/dist/mcp.js +9 -12
- package/dist/pagination.d.ts +2 -6
- package/dist/pagination.js +4 -6
- package/dist/rate-limit.d.ts +3 -4
- package/dist/rate-limit.js +3 -4
- package/dist/react-server.d.ts +7 -11
- package/dist/react-server.js +7 -11
- package/dist/react.d.ts +1 -10
- package/dist/react.js +1 -1
- package/dist/revisions.d.ts +0 -9
- package/dist/revisions.js +4 -12
- package/dist/rola.js +5 -11
- package/dist/taxonomy.d.ts +5 -7
- package/dist/taxonomy.js +1 -1
- package/dist/text.d.ts +0 -9
- package/dist/text.js +1 -1
- package/dist/tiptap.js +2 -7
- package/dist/versioning.js +1 -1
- package/dist/well-known.d.ts +0 -6
- package/dist/well-known.js +1 -1
- package/package.json +1 -1
package/bin/check-classes.mjs
CHANGED
|
@@ -18,11 +18,20 @@
|
|
|
18
18
|
// are utilities is derived, not listed — anything the build emits that globals.css
|
|
19
19
|
// does not define is Tailwind's.
|
|
20
20
|
//
|
|
21
|
+
// And the reverse: a selector globals.css defines that nothing the build ships
|
|
22
|
+
// references. The shared blocks and chrome render their markup inside
|
|
23
|
+
// wiki-formant while each app styles it, so references are read from the build
|
|
24
|
+
// output, which holds this repo's code, the package's components and bundled
|
|
25
|
+
// libraries alike. A class renamed in the package surfaces here as its old
|
|
26
|
+
// selector going unreferenced. HTML stored in a database is not in the build,
|
|
27
|
+
// so a selector only stored content uses is reported too.
|
|
28
|
+
//
|
|
21
29
|
// npx check-classes # exit 1 on any dead token
|
|
22
30
|
// npx check-classes --warn # report and exit 0
|
|
23
31
|
// npx check-classes --compositions # also fail on 3+ inline utilities
|
|
32
|
+
// npx check-classes --unused # also fail on unreferenced selectors
|
|
24
33
|
//
|
|
25
|
-
// Run it from the repo root: `.next
|
|
34
|
+
// Run it from the repo root: `.next` and `src` are resolved against cwd.
|
|
26
35
|
//
|
|
27
36
|
// Needs a build first (npm run build): without one there is nothing to check
|
|
28
37
|
// against, and the script says so and exits 0 rather than failing blind.
|
|
@@ -30,7 +39,7 @@ import fs from 'node:fs';
|
|
|
30
39
|
import path from 'node:path';
|
|
31
40
|
|
|
32
41
|
const WARN_ONLY = process.argv.includes('--warn');
|
|
33
|
-
const
|
|
42
|
+
const STATIC_DIR = '.next/static';
|
|
34
43
|
const SRC_DIR = 'src';
|
|
35
44
|
|
|
36
45
|
const walk = (dir, ext, out = []) => {
|
|
@@ -43,7 +52,7 @@ const walk = (dir, ext, out = []) => {
|
|
|
43
52
|
return out;
|
|
44
53
|
};
|
|
45
54
|
|
|
46
|
-
const cssFiles = walk(
|
|
55
|
+
const cssFiles = walk(STATIC_DIR, '.css');
|
|
47
56
|
if (!cssFiles.length) {
|
|
48
57
|
console.log('check-classes: no built CSS under .next/static — run `npm run build` first. Skipping.');
|
|
49
58
|
process.exit(0);
|
|
@@ -59,12 +68,18 @@ for (const f of cssFiles) {
|
|
|
59
68
|
|
|
60
69
|
// Classes this project defines itself. Everything else the build emitted is a
|
|
61
70
|
// Tailwind utility, which is what makes the composition count derivable rather
|
|
62
|
-
// than a maintained prefix list.
|
|
71
|
+
// than a maintained prefix list. Read from rule preludes only, so a decimal in a
|
|
72
|
+
// value or a class named in a comment is not taken for a selector.
|
|
63
73
|
const globalsFile = walk(SRC_DIR, '.css').find(f => f.endsWith('globals.css'));
|
|
64
74
|
const named = new Set();
|
|
65
75
|
if (globalsFile) {
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
const css = fs.readFileSync(globalsFile, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
77
|
+
for (const [, prelude] of css.matchAll(/([^{};]*)\{/g)) {
|
|
78
|
+
if (prelude.trim().startsWith('@')) continue;
|
|
79
|
+
for (const m of prelude.matchAll(/\.((?:\\.|[-\w])+)/g)) {
|
|
80
|
+
const name = m[1].replace(/\\/g, '');
|
|
81
|
+
if (/^[A-Za-z_-]/.test(name)) named.add(name);
|
|
82
|
+
}
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
85
|
|
|
@@ -92,6 +107,7 @@ for (const f of walk(SRC_DIR, '.tsx')) {
|
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
const CHECK_COMPOSITIONS = process.argv.includes('--compositions');
|
|
110
|
+
const CHECK_UNUSED = process.argv.includes('--unused');
|
|
95
111
|
|
|
96
112
|
if (compositions.length) {
|
|
97
113
|
const verb = CHECK_COMPOSITIONS ? 'must be named' : 'should be named (advisory)';
|
|
@@ -103,9 +119,30 @@ if (compositions.length) {
|
|
|
103
119
|
console.error('');
|
|
104
120
|
}
|
|
105
121
|
|
|
122
|
+
// Every token in the JavaScript the build ships, server and client. A selector
|
|
123
|
+
// is referenced when its name appears whole, or when a prefix of it ending in `-`
|
|
124
|
+
// or `_` does, which is how `editorial-banner-${variant}` is written.
|
|
125
|
+
const shipped = new Set();
|
|
126
|
+
for (const f of [...walk(STATIC_DIR, '.js'), ...walk('.next/server', '.js')]) {
|
|
127
|
+
for (const [tok] of fs.readFileSync(f, 'utf8').matchAll(/[\w-]+/g)) shipped.add(tok);
|
|
128
|
+
}
|
|
129
|
+
const referenced = name =>
|
|
130
|
+
shipped.has(name) ||
|
|
131
|
+
[...name].some((c, i) => i >= 3 && (c === '-' || c === '_') && shipped.has(name.slice(0, i + 1)));
|
|
132
|
+
const unused = [...named].filter(n => !referenced(n)).sort();
|
|
133
|
+
|
|
134
|
+
if (unused.length) {
|
|
135
|
+
const verb = CHECK_UNUSED ? 'must be deleted' : 'advisory';
|
|
136
|
+
console.error(`check-classes: ${unused.length} selector(s) in ${globalsFile} referenced by nothing the build ships (${verb}):\n`);
|
|
137
|
+
console.error(` ${unused.join(', ')}\n`);
|
|
138
|
+
console.error(' Class names inside HTML stored in a database do not count, so check stored content before deleting one.\n');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const advisoryFailed = (CHECK_COMPOSITIONS && compositions.length > 0) || (CHECK_UNUSED && unused.length > 0);
|
|
142
|
+
|
|
106
143
|
if (!dead.size) {
|
|
107
144
|
console.log(`check-classes: clean — every className token resolves against ${emitted.size} emitted selectors.`);
|
|
108
|
-
process.exit(
|
|
145
|
+
process.exit(advisoryFailed && !WARN_ONLY ? 1 : 0);
|
|
109
146
|
}
|
|
110
147
|
|
|
111
148
|
console.error(`check-classes: ${dead.size} className token(s) style nothing:\n`);
|
package/dist/block-views.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
// block-views.tsx — the block
|
|
3
|
+
// block-views.tsx — the block leaves the wikis share.
|
|
4
4
|
//
|
|
5
5
|
// Behind its own subpath for the reason `react.tsx` is: React is an OPTIONAL
|
|
6
6
|
// peer, so a consumer that only wants the taxonomy or the MCP transport still
|
|
@@ -10,13 +10,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
10
10
|
// that way (see the Block Model note in the workspace CLAUDE.md) — a closed
|
|
11
11
|
// union is what makes `switch (block.type)` exhaustive, so a new block type is
|
|
12
12
|
// a compile error rather than a silent blank. What is NOT per-repo is what a
|
|
13
|
-
// codeTabs or a linkGrid LOOKS like once you have dispatched to it
|
|
14
|
-
//
|
|
15
|
-
// with the caller and the leaves move here.
|
|
13
|
+
// codeTabs or a linkGrid LOOKS like once you have dispatched to it, down to the
|
|
14
|
+
// class names. So the dispatch stays with the caller and the leaves live here.
|
|
16
15
|
//
|
|
17
|
-
// The class names are NOT props.
|
|
18
|
-
//
|
|
19
|
-
//
|
|
16
|
+
// The class names are NOT props. They are the shared convention every
|
|
17
|
+
// stylesheet implements, and making them configurable would let that convention
|
|
18
|
+
// fork. Everything that
|
|
20
19
|
// genuinely differs — the prose of a banner, whether references run through an
|
|
21
20
|
// HTML processor, the router's link — arrives as a prop.
|
|
22
21
|
import { Fragment, useState } from 'react';
|
package/dist/combobox.js
CHANGED
|
@@ -4,21 +4,12 @@
|
|
|
4
4
|
// pure, so it can be tested without a DOM, and `react.tsx` is left holding only
|
|
5
5
|
// the hook that calls it.
|
|
6
6
|
//
|
|
7
|
-
// `useTypeahead`
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// - one gave the rows `role="option"` but still no listbox, so the options
|
|
14
|
-
// had no owner;
|
|
15
|
-
// - two had no roles whatsoever;
|
|
16
|
-
// - one used a `data-highlighted` attribute, which no assistive technology
|
|
17
|
-
// reads.
|
|
18
|
-
//
|
|
19
|
-
// None of the five set `aria-activedescendant`, which is the attribute that
|
|
20
|
-
// actually announces the highlighted row as the reader arrows through it. A
|
|
21
|
-
// combobox without it is a text field that silently changes what Enter does.
|
|
7
|
+
// `useTypeahead` shares the state machine; this is the ARIA that goes with it:
|
|
8
|
+
// a `role="listbox"` container that owns `role="option"` rows, `aria-selected`
|
|
9
|
+
// on the option rather than on a plain `<button>`, and `aria-activedescendant`,
|
|
10
|
+
// the attribute that announces the highlighted row as the reader arrows
|
|
11
|
+
// through it. A combobox without it is a text field that silently changes what
|
|
12
|
+
// Enter does.
|
|
22
13
|
/** The id of one option row. Exported because a caller that scrolls the
|
|
23
14
|
* highlighted row into view has to find it by the same id the input points at. */
|
|
24
15
|
export function optionId(baseId, listKey, index) {
|
package/dist/conformance.js
CHANGED
|
@@ -19,8 +19,7 @@ import { DEFAULT_MAX_BATCH, MCP_CORS, MCP_PROTOCOL_VERSION } from './mcp.js';
|
|
|
19
19
|
/**
|
|
20
20
|
* Does the live preflight carry every token `MCP_CORS` declares for this header?
|
|
21
21
|
*
|
|
22
|
-
* Derived rather than listed
|
|
23
|
-
* so a fourth added to `MCP_CORS` was tested by nobody.
|
|
22
|
+
* Derived rather than listed, so a header added to `MCP_CORS` is tested too.
|
|
24
23
|
*/
|
|
25
24
|
const corsCovers = (res, header) => {
|
|
26
25
|
const live = res.headers.get(header) ?? '';
|
|
@@ -306,10 +305,9 @@ export async function annotationChecks(t, opts = {}) {
|
|
|
306
305
|
*/
|
|
307
306
|
export async function payloadBudget(t, calls, maxBytes = 120_000) {
|
|
308
307
|
// Every read-only tool a caller can invoke with no arguments at all, whether
|
|
309
|
-
// or not the suite thought to name it
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
// the four tools beside it. A tool with required arguments still has to be
|
|
308
|
+
// or not the suite thought to name it, so a no-argument tool answering with
|
|
309
|
+
// megabytes cannot pass by going unlisted. A tool with required arguments
|
|
310
|
+
// still has to be
|
|
313
311
|
// listed: the suite is the only thing that knows a valid pair.
|
|
314
312
|
const listed = new Set(calls.map(c => c.name));
|
|
315
313
|
const bare = ((await t.rpc('tools/list')).result?.tools ?? [])
|
package/dist/crawlers.js
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
// crawlers.ts — one roster of AI crawler tokens, for both surfaces that need it.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// the set of agents that get their own group. The two copies were each
|
|
6
|
-
// byte-identical across all three repos — and they were different lists.
|
|
3
|
+
// The proxy counts an "AI Bot Visit" by user-agent substring, and `robots.ts`
|
|
4
|
+
// gives each agent its own group. Both read this one roster.
|
|
7
5
|
//
|
|
8
|
-
//
|
|
6
|
+
// One difference between the two uses is deliberate. `Applebot-Extended`
|
|
9
7
|
// never fetches a page: it is a robots.txt-only token that Applebot consults
|
|
10
8
|
// before using already-crawled data for AI, so counting it would count nothing.
|
|
11
9
|
// That belongs in robots and not in the matcher, and it is declared here.
|
|
12
10
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// an agent with no group of its own falls through to `*` and is granted
|
|
17
|
-
// whatever that grants. The wikis were measuring five crawlers they had never
|
|
18
|
-
// addressed. One roster makes that a property of the data rather than of which
|
|
19
|
-
// file you happened to edit.
|
|
11
|
+
// Every other token appears in both, because a crawler obeys only its
|
|
12
|
+
// most-specific matching group: an agent with no group of its own falls through
|
|
13
|
+
// to `*` and is granted whatever that grants.
|
|
20
14
|
/**
|
|
21
15
|
* Order is significant: `detectAiBot` returns the first token the user agent
|
|
22
16
|
* contains, so a token that is a substring of another must come after it.
|
package/dist/dom.d.ts
CHANGED
|
@@ -19,8 +19,7 @@ export interface CopyButtonOptions {
|
|
|
19
19
|
export declare function addCopyButtons(root: ParentNode, options?: CopyButtonOptions): number;
|
|
20
20
|
/**
|
|
21
21
|
* The one place this origin is written down. It is both the embed host and the
|
|
22
|
-
* allow-list the resize listener checks
|
|
23
|
-
* four call sites across the two wikis.
|
|
22
|
+
* allow-list the resize listener checks.
|
|
24
23
|
*/
|
|
25
24
|
export declare const TWITTER_ORIGIN = "https://platform.twitter.com";
|
|
26
25
|
/** The embed iframe's src. `dnt=true` opts the embed out of Twitter's tracking. */
|
package/dist/dom.js
CHANGED
|
@@ -55,8 +55,7 @@ export function addCopyButtons(root, options = {}) {
|
|
|
55
55
|
// ---- twitter embeds ---------------------------------------------------------
|
|
56
56
|
/**
|
|
57
57
|
* The one place this origin is written down. It is both the embed host and the
|
|
58
|
-
* allow-list the resize listener checks
|
|
59
|
-
* four call sites across the two wikis.
|
|
58
|
+
* allow-list the resize listener checks.
|
|
60
59
|
*/
|
|
61
60
|
export const TWITTER_ORIGIN = 'https://platform.twitter.com';
|
|
62
61
|
/** The embed iframe's src. `dnt=true` opts the embed out of Twitter's tracking. */
|
package/dist/editor.d.ts
CHANGED
|
@@ -5,8 +5,7 @@ import { type ChangeEvent, type RefObject } from 'react';
|
|
|
5
5
|
*
|
|
6
6
|
* Each of these packages ships a `declare module '@tiptap/core'` block that adds
|
|
7
7
|
* its commands to `ChainedCommands` — `toggleBold`, `insertTable`, `setLink`,
|
|
8
|
-
* `setImage`.
|
|
9
|
-
* a side effect of the import; now that this module owns them, it has to carry
|
|
8
|
+
* `setImage`. Because this module owns the extensions, it has to carry
|
|
10
9
|
* the augmentation across the package boundary, and a re-exported type is what
|
|
11
10
|
* makes the emitted `.d.ts` load the module that declares it. Without these
|
|
12
11
|
* four lines every `editor.chain().focus().toggleBold()` in every consumer
|
|
@@ -16,19 +15,6 @@ export type { StarterKitOptions } from '@tiptap/starter-kit';
|
|
|
16
15
|
export type { LinkOptions } from '@tiptap/extension-link';
|
|
17
16
|
export type { ImageOptions } from '@tiptap/extension-image';
|
|
18
17
|
export type { TableOptions } from '@tiptap/extension-table';
|
|
19
|
-
/**
|
|
20
|
-
* Reduce pasted HTML to structure plus the three attributes that carry meaning.
|
|
21
|
-
*
|
|
22
|
-
* A paste from a word processor or a web page arrives carrying its whole
|
|
23
|
-
* stylesheet inline. Keeping any of it means the wiki's own typography loses to
|
|
24
|
-
* whatever the author copied from, per paragraph, invisibly — and `style` on a
|
|
25
|
-
* pasted node is also the cheapest way to smuggle a full-bleed overlay into a
|
|
26
|
-
* page body.
|
|
27
|
-
*
|
|
28
|
-
* Browser-only: it parses with `DOMParser`. Called from `transformPastedHTML`,
|
|
29
|
-
* which only ever runs in response to a paste.
|
|
30
|
-
*/
|
|
31
|
-
export declare function cleanPastedHtml(html: string): string;
|
|
32
18
|
export interface WikiEditorExtensionOptions {
|
|
33
19
|
/** Empty-document prompt. */
|
|
34
20
|
placeholder?: string;
|
|
@@ -42,15 +28,6 @@ export interface WikiEditorExtensionOptions {
|
|
|
42
28
|
*/
|
|
43
29
|
nodes?: readonly AnyExtension[];
|
|
44
30
|
}
|
|
45
|
-
/**
|
|
46
|
-
* The editor's extension set.
|
|
47
|
-
*
|
|
48
|
-
* `codeBlock: false` on StarterKit is load-bearing: the consumer registers its
|
|
49
|
-
* own via `createCodeBlock`, and leaving StarterKit's in place would give the
|
|
50
|
-
* schema two nodes claiming the same name. Headings stop at h2 — the page title
|
|
51
|
-
* is the only h1 a wiki page has.
|
|
52
|
-
*/
|
|
53
|
-
export declare function wikiEditorExtensions({ placeholder, nodes, }?: WikiEditorExtensionOptions): AnyExtension[];
|
|
54
31
|
/**
|
|
55
32
|
* Turn a pasted URL into the richest node that fits it, falling back to a bare
|
|
56
33
|
* iframe. Order matters: a YouTube URL is also a valid iframe source, so the
|
package/dist/editor.js
CHANGED
|
@@ -4,25 +4,16 @@
|
|
|
4
4
|
// `tiptap.tsx` holds the custom NODES both wikis needed (iframe, tweet, map,
|
|
5
5
|
// code block, tabs). This holds what sits around them: which extensions are
|
|
6
6
|
// configured how, what a paste is scrubbed down to, how a pasted URL becomes
|
|
7
|
-
// the right embed, and the state a toolbar reads.
|
|
8
|
-
// both repos — the same twelve-entry extension array, the same paste scrubber,
|
|
9
|
-
// the same embed dispatch including the shortened-map async swap.
|
|
7
|
+
// the right embed, and the state a toolbar reads.
|
|
10
8
|
//
|
|
11
|
-
// TWO
|
|
12
|
-
// exists at all is that each repo had exactly one of them:
|
|
9
|
+
// TWO GUARDS HERE ARE LOAD-BEARING:
|
|
13
10
|
//
|
|
14
|
-
// 1. `onChangeRef.current = onChange`
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// keystroke of every edit that ended in a click was dropped. Blur flushes
|
|
21
|
-
// the pending debounce.
|
|
22
|
-
//
|
|
23
|
-
// Neither repo was "behind": each had shipped the fix the other lacked, which
|
|
24
|
-
// is the drift that costs the most, because neither file looks like the one to
|
|
25
|
-
// fix.
|
|
11
|
+
// 1. `onChangeRef.current = onChange` is written in an effect, never during
|
|
12
|
+
// render: a ref mutated mid-render can tear under concurrent rendering.
|
|
13
|
+
// It is only read from events and timeouts, which run later.
|
|
14
|
+
// 2. Blur flushes the pending debounce. The change is debounced 150ms, and
|
|
15
|
+
// clicking Save blurs the editor before the click lands, so without the
|
|
16
|
+
// flush the final keystroke of an edit that ends in a click is dropped.
|
|
26
17
|
//
|
|
27
18
|
// Every @tiptap package here is an OPTIONAL PEER. A consumer that only wants
|
|
28
19
|
// the taxonomy or the MCP transport installs none of them.
|
|
@@ -50,7 +41,7 @@ import { toMapEmbedUrl } from './maps.js';
|
|
|
50
41
|
* Browser-only: it parses with `DOMParser`. Called from `transformPastedHTML`,
|
|
51
42
|
* which only ever runs in response to a paste.
|
|
52
43
|
*/
|
|
53
|
-
|
|
44
|
+
function cleanPastedHtml(html) {
|
|
54
45
|
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
55
46
|
doc.querySelectorAll('style, script, meta, link, svg, canvas, noscript').forEach(el => el.remove());
|
|
56
47
|
doc.querySelectorAll('*').forEach(el => {
|
|
@@ -72,7 +63,7 @@ export function cleanPastedHtml(html) {
|
|
|
72
63
|
* schema two nodes claiming the same name. Headings stop at h2 — the page title
|
|
73
64
|
* is the only h1 a wiki page has.
|
|
74
65
|
*/
|
|
75
|
-
|
|
66
|
+
function wikiEditorExtensions({ placeholder = '', nodes = [], } = {}) {
|
|
76
67
|
return [
|
|
77
68
|
StarterKit.configure({ heading: { levels: [2, 3, 4] }, codeBlock: false }),
|
|
78
69
|
TiptapLink.configure({ openOnClick: false, HTMLAttributes: { class: 'link' } }),
|
package/dist/headings.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
// headings.ts — stable ids on a wiki page's headings, and the list a table of
|
|
2
2
|
// contents is built from.
|
|
3
3
|
//
|
|
4
|
-
// Two of the three wikis had already written this out (caper's `injectHeadingIds`,
|
|
5
|
-
// radix-wiki's heading branch in `processHtml`), down to byte-identical
|
|
6
|
-
// `stripTags` and `getAttr` helpers — caper's file says "ported in spirit from
|
|
7
|
-
// radix-wiki" at the top, which is the drift admitting itself. What they had
|
|
8
|
-
// drifted on is below.
|
|
9
|
-
//
|
|
10
4
|
// The slug rule is a parameter, not a decision this module makes. A heading id
|
|
11
5
|
// is a live URL: readers link to `#the-shape-of-a-code`, and so does the page's
|
|
12
6
|
// own permalink anchor. Unifying two slug rules would silently move every
|
|
@@ -15,8 +9,7 @@
|
|
|
15
9
|
//
|
|
16
10
|
// Deduping, by contrast, is not a choice: two headings with the same text
|
|
17
11
|
// otherwise mint the same id twice and every link to the second one lands on
|
|
18
|
-
// the first
|
|
19
|
-
// dedupes.
|
|
12
|
+
// the first, so this always dedupes.
|
|
20
13
|
import { getAttr, stripTags } from './html.js';
|
|
21
14
|
/** The default slug rule: lowercase words joined by hyphens. */
|
|
22
15
|
export function slugifyHeading(text) {
|
package/dist/link-check.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
// link-check.ts — the dead-link probe
|
|
1
|
+
// link-check.ts — the dead-link probe wiki sweep scripts share.
|
|
2
2
|
//
|
|
3
3
|
// Node-only (fetch, AbortSignal, URL). No framework, no database: what a
|
|
4
4
|
// checker does with the verdicts — which pages to walk, what counts as an
|
|
5
5
|
// internal path, how to report — stays with the caller, because that is the
|
|
6
6
|
// half that is genuinely per-wiki.
|
|
7
7
|
//
|
|
8
|
-
// EVERY GUARD BELOW WAS PAID FOR BY A FALSE POSITIVE
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// serialising per hostname is what fixes it. Neither copy was behind. A sweep
|
|
14
|
-
// running either one alone strips good citations for reasons the other repo had
|
|
15
|
-
// already written down — which is the entire argument for this file.
|
|
8
|
+
// EVERY GUARD BELOW WAS PAID FOR BY A FALSE POSITIVE. npmjs.com 403s scripted
|
|
9
|
+
// requests, an expired certificate is not a dead host, a YouTube /embed/ URL
|
|
10
|
+
// answers 200 for a deleted video, and a connect refusal is usually concurrency
|
|
11
|
+
// rather than death, which serialising per hostname fixes. A sweep missing any
|
|
12
|
+
// one of these strips good citations.
|
|
16
13
|
import { stripTags } from './html.js';
|
|
17
14
|
const DEFAULTS = { timeoutMs: 12_000, slowTimeoutMs: 40_000 };
|
|
18
15
|
/**
|
package/dist/maps.d.ts
CHANGED
|
@@ -3,8 +3,6 @@ export interface MapCoords {
|
|
|
3
3
|
lon: number;
|
|
4
4
|
zoom?: number | undefined;
|
|
5
5
|
}
|
|
6
|
-
/** A plain embed URL for a coordinate pair. */
|
|
7
|
-
export declare function mapsEmbedUrl(lat: number, lon: number, zoom?: number): string;
|
|
8
6
|
/**
|
|
9
7
|
* Dig a coordinate pair out of a maps URL. Four shapes in descending
|
|
10
8
|
* specificity: the `@lat,lon,zoom` path segment, a `/search/lat,lon`, the
|
package/dist/maps.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// pure string work over Google and Apple Maps URL shapes, with no framework or
|
|
5
5
|
// database in it, which is why neither copy had a reason to diverge.
|
|
6
6
|
/** A plain embed URL for a coordinate pair. */
|
|
7
|
-
|
|
7
|
+
function mapsEmbedUrl(lat, lon, zoom = 15) {
|
|
8
8
|
return `https://maps.google.com/maps?q=${lat},${lon}&z=${zoom}&output=embed`;
|
|
9
9
|
}
|
|
10
10
|
/**
|
package/dist/mcp.d.ts
CHANGED
|
@@ -11,10 +11,6 @@ export declare const MCP_PROTOCOL_VERSIONS: readonly ["2025-06-18", "2025-03-26"
|
|
|
11
11
|
export type McpProtocolVersion = (typeof MCP_PROTOCOL_VERSIONS)[number];
|
|
12
12
|
/** The newest version spoken here, and what an unrecognised ask falls back to. */
|
|
13
13
|
export declare const MCP_PROTOCOL_VERSION: McpProtocolVersion;
|
|
14
|
-
/** Echo the client's version when it is one we speak, else offer the newest. */
|
|
15
|
-
export declare function negotiateProtocol(requested: unknown): McpProtocolVersion;
|
|
16
|
-
/** The version a post-initialize request is operating under. */
|
|
17
|
-
export declare function requestProtocol(request: Request): McpProtocolVersion;
|
|
18
14
|
import { type RateLimitOptions } from './rate-limit.js';
|
|
19
15
|
export type ToolParam = {
|
|
20
16
|
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
package/dist/mcp.js
CHANGED
|
@@ -28,11 +28,11 @@ export const MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSIONS[0];
|
|
|
28
28
|
const ASSUMED_VERSION = '2025-03-26';
|
|
29
29
|
const speaks = (v) => MCP_PROTOCOL_VERSIONS.includes(v);
|
|
30
30
|
/** Echo the client's version when it is one we speak, else offer the newest. */
|
|
31
|
-
|
|
31
|
+
function negotiateProtocol(requested) {
|
|
32
32
|
return speaks(requested) ? requested : MCP_PROTOCOL_VERSION;
|
|
33
33
|
}
|
|
34
34
|
/** The version a post-initialize request is operating under. */
|
|
35
|
-
|
|
35
|
+
function requestProtocol(request) {
|
|
36
36
|
const header = request.headers.get('mcp-protocol-version');
|
|
37
37
|
return speaks(header) ? header : ASSUMED_VERSION;
|
|
38
38
|
}
|
|
@@ -143,9 +143,7 @@ function methodsFor(config) {
|
|
|
143
143
|
...BASE_METHODS,
|
|
144
144
|
// The list methods answer whether or not anything is registered: an empty
|
|
145
145
|
// list is a better answer to a client that asked than a -32601 it has to
|
|
146
|
-
// interpret.
|
|
147
|
-
// answered them, so the error message contradicted the server describing
|
|
148
|
-
// itself.
|
|
146
|
+
// interpret.
|
|
149
147
|
'resources/list',
|
|
150
148
|
'resources/templates/list',
|
|
151
149
|
'prompts/list',
|
|
@@ -286,13 +284,12 @@ async function handleRpc(req, config, dispatch) {
|
|
|
286
284
|
try {
|
|
287
285
|
const data = await tool.handler(args, ctx);
|
|
288
286
|
const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
289
|
-
// Every object answer, not only the schema-bearing ones.
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
// no longer the price of admission. The text block stays regardless:
|
|
287
|
+
// Every object answer, not only the schema-bearing ones. Gated on
|
|
288
|
+
// `outputSchema`, a JSON-returning tool without one would carry no
|
|
289
|
+
// `structuredContent`, and agents would parse prose to reach data the
|
|
290
|
+
// server has in hand. A declared `outputSchema` is the stronger
|
|
291
|
+
// contract (a client validates against it), not the price of
|
|
292
|
+
// admission. The text block stays regardless:
|
|
296
293
|
// the spec asks for the serialised twin, and a client that reads only
|
|
297
294
|
// content still has to be able to read the answer.
|
|
298
295
|
const structured = data !== null && typeof data === 'object' && !Array.isArray(data)
|
package/dist/pagination.d.ts
CHANGED
|
@@ -7,8 +7,6 @@ export interface PaginatedResponse<T> extends Pagination {
|
|
|
7
7
|
total: number;
|
|
8
8
|
totalPages: number;
|
|
9
9
|
}
|
|
10
|
-
export declare const MAX_PAGE_SIZE = 100;
|
|
11
|
-
export declare const DEFAULT_PAGE_SIZE = 20;
|
|
12
10
|
/**
|
|
13
11
|
* `page` clamped to ≥1, `pageSize` clamped to 1–`max`. Never trust either.
|
|
14
12
|
*
|
|
@@ -55,14 +53,12 @@ export declare function toOffset({ page, pageSize }: Pagination): {
|
|
|
55
53
|
* The ordering is the caller's, deliberately — it is the one thing here that is
|
|
56
54
|
* never portable. A wiki's sequence is its section's configured sort, a
|
|
57
55
|
* knowledge base's is a taxonomy walk, and a company log's is `updatedAt` desc.
|
|
58
|
-
* What both wikis had written twice is this scan, not the sort.
|
|
59
56
|
*
|
|
60
57
|
* `null` on both sides when the page is not in the list, so a page reached by a
|
|
61
58
|
* URL its own section does not list renders no nav rather than a wrong one.
|
|
62
59
|
*
|
|
63
|
-
* No query.
|
|
64
|
-
*
|
|
65
|
-
* neighbours used to cost were the reason one wiki dropped the control.
|
|
60
|
+
* No query. Callers already hold the ordered siblings for something else, a
|
|
61
|
+
* related-pages panel or a section listing, so the neighbours cost no lookups.
|
|
66
62
|
*/
|
|
67
63
|
export declare function adjacentPages<T>(ordered: readonly T[], isCurrent: (page: T) => boolean): {
|
|
68
64
|
prev: T | null;
|
package/dist/pagination.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Forked three ways once: a re-typed clamp that dropped `totalPages`, and an
|
|
4
4
|
// offset redone by hand in raw SQL. The response shape is the contract a client
|
|
5
5
|
// codes against, so reshaping it per repo is a breaking change nobody declared.
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const MAX_PAGE_SIZE = 100;
|
|
7
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
8
8
|
/**
|
|
9
9
|
* `page` clamped to ≥1, `pageSize` clamped to 1–`max`. Never trust either.
|
|
10
10
|
*
|
|
@@ -62,14 +62,12 @@ export function toOffset({ page, pageSize }) {
|
|
|
62
62
|
* The ordering is the caller's, deliberately — it is the one thing here that is
|
|
63
63
|
* never portable. A wiki's sequence is its section's configured sort, a
|
|
64
64
|
* knowledge base's is a taxonomy walk, and a company log's is `updatedAt` desc.
|
|
65
|
-
* What both wikis had written twice is this scan, not the sort.
|
|
66
65
|
*
|
|
67
66
|
* `null` on both sides when the page is not in the list, so a page reached by a
|
|
68
67
|
* URL its own section does not list renders no nav rather than a wrong one.
|
|
69
68
|
*
|
|
70
|
-
* No query.
|
|
71
|
-
*
|
|
72
|
-
* neighbours used to cost were the reason one wiki dropped the control.
|
|
69
|
+
* No query. Callers already hold the ordered siblings for something else, a
|
|
70
|
+
* related-pages panel or a section listing, so the neighbours cost no lookups.
|
|
73
71
|
*/
|
|
74
72
|
export function adjacentPages(ordered, isCurrent) {
|
|
75
73
|
const i = ordered.findIndex(isCurrent);
|
package/dist/rate-limit.d.ts
CHANGED
|
@@ -69,10 +69,9 @@ export declare function resetRateLimits(): void;
|
|
|
69
69
|
* `.well-known/mcp.json`, the OpenAPI spec, agents.md, llms.txt and the
|
|
70
70
|
* `initialize` instructions all quote this, so the endpoint enforces exactly
|
|
71
71
|
* what the documents claim. It is a cross-surface contract, which is why it
|
|
72
|
-
* lives here rather than three times over
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* difference from a stale one.
|
|
72
|
+
* lives here rather than three times over. A second copy is how a number
|
|
73
|
+
* becomes impossible to change safely: nobody can tell a considered difference
|
|
74
|
+
* from a stale one.
|
|
76
75
|
*
|
|
77
76
|
* A surface with a genuine reason to differ passes its own `RateLimitOptions`.
|
|
78
77
|
* What it must not do is restate this one.
|
package/dist/rate-limit.js
CHANGED
|
@@ -117,10 +117,9 @@ export function resetRateLimits() {
|
|
|
117
117
|
* `.well-known/mcp.json`, the OpenAPI spec, agents.md, llms.txt and the
|
|
118
118
|
* `initialize` instructions all quote this, so the endpoint enforces exactly
|
|
119
119
|
* what the documents claim. It is a cross-surface contract, which is why it
|
|
120
|
-
* lives here rather than three times over
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* difference from a stale one.
|
|
120
|
+
* lives here rather than three times over. A second copy is how a number
|
|
121
|
+
* becomes impossible to change safely: nobody can tell a considered difference
|
|
122
|
+
* from a stale one.
|
|
124
123
|
*
|
|
125
124
|
* A surface with a genuine reason to differ passes its own `RateLimitOptions`.
|
|
126
125
|
* What it must not do is restate this one.
|
package/dist/react-server.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export interface FacetBarProps {
|
|
|
54
54
|
* dropping the active facets — and the A–Z row leads with the reset control.
|
|
55
55
|
*
|
|
56
56
|
* `aria-current`, not `aria-pressed`: a link is not a toggle button and does not
|
|
57
|
-
* take that attribute.
|
|
57
|
+
* take that attribute.
|
|
58
58
|
*/
|
|
59
59
|
export declare function FacetBar({ link: Link, facets, letters, alphaLabel, classNames, }: FacetBarProps): import("react").JSX.Element | null;
|
|
60
60
|
export interface BreadcrumbItem {
|
|
@@ -114,7 +114,7 @@ export declare function Breadcrumbs({ items, base, className, link: Link, lastAs
|
|
|
114
114
|
* The standard page-top row: the trail, plus optional right-aligned actions.
|
|
115
115
|
*
|
|
116
116
|
* The column it sits in is the one its route declares — the row reads the
|
|
117
|
-
* page's own max-width by inheritance, so a trail
|
|
117
|
+
* page's own max-width by inheritance, so a trail cannot disagree with
|
|
118
118
|
* the content it titles.
|
|
119
119
|
*/
|
|
120
120
|
export declare function BreadcrumbsRow({ actions, className, ...props }: BreadcrumbsProps & {
|
|
@@ -146,15 +146,11 @@ export interface PageNavProps {
|
|
|
146
146
|
* lateral links do not cover.
|
|
147
147
|
*
|
|
148
148
|
* Ordering is entirely the caller's; pair this with `adjacentPages` from
|
|
149
|
-
* `wiki-formant/pagination` over a list you already hold.
|
|
150
|
-
* markup and had already drifted on the parts that matter rather than the parts
|
|
151
|
-
* that show: one carried `rel="prev"`/`rel="next"` and an `aria-label` and the
|
|
152
|
-
* other carried neither, and the one without expressed its right-hand alignment
|
|
153
|
-
* as two inline utilities instead of the modifier its own stylesheet defines.
|
|
149
|
+
* `wiki-formant/pagination` over a list you already hold.
|
|
154
150
|
*
|
|
155
|
-
* The `page-nav__*` class names are NOT props
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
151
|
+
* The `page-nav__*` class names are NOT props: they are the convention every
|
|
152
|
+
* stylesheet implements, and making them configurable is how a convention
|
|
153
|
+
* forks. The empty `<div>` holds the first article's left column so the next
|
|
154
|
+
* link stays in the right one.
|
|
159
155
|
*/
|
|
160
156
|
export declare function PageNav({ prev, next, link: Link, label, prevLabel, nextLabel, prevGlyph, nextGlyph, }: PageNavProps): import("react").JSX.Element | null;
|
package/dist/react-server.js
CHANGED
|
@@ -34,7 +34,7 @@ export const Anchor = ({ href, className, children }) => _jsx("a", { href: href,
|
|
|
34
34
|
* dropping the active facets — and the A–Z row leads with the reset control.
|
|
35
35
|
*
|
|
36
36
|
* `aria-current`, not `aria-pressed`: a link is not a toggle button and does not
|
|
37
|
-
* take that attribute.
|
|
37
|
+
* take that attribute.
|
|
38
38
|
*/
|
|
39
39
|
export function FacetBar({ link: Link, facets, letters, alphaLabel = 'A–Z', classNames = {}, }) {
|
|
40
40
|
if (!facets.length && !letters.length)
|
|
@@ -85,7 +85,7 @@ export function Breadcrumbs({ items, base, className = '', link: Link = Anchor,
|
|
|
85
85
|
* The standard page-top row: the trail, plus optional right-aligned actions.
|
|
86
86
|
*
|
|
87
87
|
* The column it sits in is the one its route declares — the row reads the
|
|
88
|
-
* page's own max-width by inheritance, so a trail
|
|
88
|
+
* page's own max-width by inheritance, so a trail cannot disagree with
|
|
89
89
|
* the content it titles.
|
|
90
90
|
*/
|
|
91
91
|
export function BreadcrumbsRow({ actions, className = '', ...props }) {
|
|
@@ -96,16 +96,12 @@ export function BreadcrumbsRow({ actions, className = '', ...props }) {
|
|
|
96
96
|
* lateral links do not cover.
|
|
97
97
|
*
|
|
98
98
|
* Ordering is entirely the caller's; pair this with `adjacentPages` from
|
|
99
|
-
* `wiki-formant/pagination` over a list you already hold.
|
|
100
|
-
* markup and had already drifted on the parts that matter rather than the parts
|
|
101
|
-
* that show: one carried `rel="prev"`/`rel="next"` and an `aria-label` and the
|
|
102
|
-
* other carried neither, and the one without expressed its right-hand alignment
|
|
103
|
-
* as two inline utilities instead of the modifier its own stylesheet defines.
|
|
99
|
+
* `wiki-formant/pagination` over a list you already hold.
|
|
104
100
|
*
|
|
105
|
-
* The `page-nav__*` class names are NOT props
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
101
|
+
* The `page-nav__*` class names are NOT props: they are the convention every
|
|
102
|
+
* stylesheet implements, and making them configurable is how a convention
|
|
103
|
+
* forks. The empty `<div>` holds the first article's left column so the next
|
|
104
|
+
* link stays in the right one.
|
|
109
105
|
*/
|
|
110
106
|
export function PageNav({ prev, next, link: Link = Anchor, label = 'Article navigation', prevLabel = 'Previous', nextLabel = 'Next', prevGlyph = '\u2190', nextGlyph = '\u2192', }) {
|
|
111
107
|
if (!prev && !next)
|
package/dist/react.d.ts
CHANGED
|
@@ -22,14 +22,6 @@ export interface SidebarState {
|
|
|
22
22
|
*/
|
|
23
23
|
ready: boolean;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Collapse state for a wiki rail: remembered across loads, defaulted from the
|
|
27
|
-
* viewport only when the reader has never chosen.
|
|
28
|
-
*
|
|
29
|
-
* The reader's choice outranks the breakpoint. Someone who collapses the rail
|
|
30
|
-
* on a laptop and then narrows the window has still collapsed the rail.
|
|
31
|
-
*/
|
|
32
|
-
export declare function useCollapsibleSidebar(options?: SidebarOptions): SidebarState;
|
|
33
25
|
export interface SidebarProviderProps extends SidebarOptions {
|
|
34
26
|
children: ReactNode;
|
|
35
27
|
}
|
|
@@ -286,8 +278,7 @@ export declare class ErrorBoundary extends Component<{
|
|
|
286
278
|
children: ReactNode;
|
|
287
279
|
/**
|
|
288
280
|
* `error` is passed as well as `retry` because a fallback that cannot see
|
|
289
|
-
* what failed can only say "something went wrong"
|
|
290
|
-
* the copies this replaced said, while the other had the message and used it.
|
|
281
|
+
* what failed can only say "something went wrong".
|
|
291
282
|
*/
|
|
292
283
|
fallback: (retry: () => void, error: unknown) => ReactNode;
|
|
293
284
|
onError?: (error: unknown) => void;
|
package/dist/react.js
CHANGED
|
@@ -47,7 +47,7 @@ const readStored = (key) => {
|
|
|
47
47
|
* The reader's choice outranks the breakpoint. Someone who collapses the rail
|
|
48
48
|
* on a laptop and then narrows the window has still collapsed the rail.
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
function useCollapsibleSidebar(options = {}) {
|
|
51
51
|
const { storageKey = 'wiki:sidebar', breakpoint = 1024 } = options;
|
|
52
52
|
// Start open on the server and on the first client paint. SSR has no viewport
|
|
53
53
|
// and no storage, so any other guess is a guaranteed mismatch on some loads.
|
package/dist/revisions.d.ts
CHANGED
|
@@ -54,15 +54,6 @@ interface Located<B> {
|
|
|
54
54
|
}
|
|
55
55
|
/** Every block in the tree, flattened, each with the path that addresses it. */
|
|
56
56
|
export declare function extractBlocks<B>(blocks: readonly B[], containers: (block: B) => BlockGroup<B>[] | null, basePath?: string): Located<B>[];
|
|
57
|
-
/**
|
|
58
|
-
* Which scalar fields differ. Nested block arrays are skipped: they are walked
|
|
59
|
-
* as their own entries, and comparing them here would report a container as
|
|
60
|
-
* modified every time anything inside it moved.
|
|
61
|
-
*/
|
|
62
|
-
export declare function diffAttributes<B extends DiffBlock>(oldBlock: B, newBlock: B, containers: (block: B) => BlockGroup<B>[] | null): Record<string, {
|
|
63
|
-
from: unknown;
|
|
64
|
-
to: unknown;
|
|
65
|
-
}> | undefined;
|
|
66
57
|
/**
|
|
67
58
|
* Blocks matched by id, so a block that moved is reported as moved rather than
|
|
68
59
|
* as one removal and one addition.
|
package/dist/revisions.js
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
// revisions.ts — what changed between two versions of a page.
|
|
2
2
|
//
|
|
3
3
|
// The semver lives next door in `versioning.ts`; this is the walk that decides
|
|
4
|
-
// which bump to ask for.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// They differed in what each had learned since. One grew a leaf-level diff, a
|
|
8
|
-
// second changed-flag for the page's banner, and a `patch` classification for
|
|
9
|
-
// when only that flag moved. The other grew none of those and instead dropped
|
|
10
|
-
// twenty-five lines the first still carries: two Maps keyed by a recursive
|
|
11
|
-
// JSON.stringify of every block, built on every save and never read once.
|
|
12
|
-
// Matching is by id and always was.
|
|
13
|
-
//
|
|
14
|
-
// This is the union, minus the dead half.
|
|
4
|
+
// which bump to ask for. It carries a leaf-level diff, a changed-flag for the
|
|
5
|
+
// page's banner, and a `patch` classification for when only that flag moved.
|
|
6
|
+
// Matching is by id.
|
|
15
7
|
import { incrementVersion, parseVersion } from './versioning.js';
|
|
16
8
|
/** Every block in the tree, flattened, each with the path that addresses it. */
|
|
17
9
|
export function extractBlocks(blocks, containers, basePath = 'root') {
|
|
@@ -30,7 +22,7 @@ export function extractBlocks(blocks, containers, basePath = 'root') {
|
|
|
30
22
|
* as their own entries, and comparing them here would report a container as
|
|
31
23
|
* modified every time anything inside it moved.
|
|
32
24
|
*/
|
|
33
|
-
|
|
25
|
+
function diffAttributes(oldBlock, newBlock, containers) {
|
|
34
26
|
const diffs = {};
|
|
35
27
|
// Derived from these two blocks rather than from the tree, so a container
|
|
36
28
|
// nested inside another container is still exempted. Deriving it once from
|
package/dist/rola.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
// rola.ts — Radix On-Ledger Authentication: challenge, proof, session.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// passed `type: 'account'` unconditionally, and the error logging that makes a
|
|
7
|
-
// failed verification diagnosable at all.
|
|
3
|
+
// Parameterised on the two things a second wiki would differ in: the cookie
|
|
4
|
+
// name and its dApp identity. It verifies persona (identity_*) proofs as well
|
|
5
|
+
// as account proofs, and logs a failed verification so it is diagnosable.
|
|
8
6
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// **One consumer, as of Sep 2026.** miow is retired, so the second wiki this was
|
|
13
|
-
// parameterised for no longer exists; caper's wallet auth never used this stack
|
|
14
|
-
// and acuiq gates only its wiki editor, on a shared secret. Keep it — the
|
|
7
|
+
// **One consumer.** caper's wallet auth never used this stack and acuiq gates
|
|
8
|
+
// only its wiki editor, on a shared secret. Keep it — the
|
|
15
9
|
// persona-proof fix and the error logging are worth not losing, and the ports
|
|
16
10
|
// below are what make it reusable at all — but do not mistake it for a proven
|
|
17
11
|
// shared abstraction. It is radix-wiki's auth that happens to live in a package.
|
package/dist/taxonomy.d.ts
CHANGED
|
@@ -44,12 +44,11 @@ export interface RelatedRanking<T> {
|
|
|
44
44
|
* value is precisely a set the category view already filters and counts, so the
|
|
45
45
|
* row is the way into that set rather than dead text.
|
|
46
46
|
*
|
|
47
|
-
* Rows, not markup:
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* keys appear and where each one links.
|
|
47
|
+
* Rows, not markup: wikis render the same selection differently (an HTML
|
|
48
|
+
* `<table>` string folded into a block, a React `<aside>`, a markdown twin) and
|
|
49
|
+
* differ only in how a value is *formatted*: a `url` gets an external link
|
|
50
|
+
* here, a shortened display there. Which keys appear and where each one links
|
|
51
|
+
* is what they share.
|
|
53
52
|
*/
|
|
54
53
|
export interface MetadataRow extends MetadataKeyDefinition {
|
|
55
54
|
value: string;
|
|
@@ -119,7 +118,6 @@ export interface TaxonomyConfig {
|
|
|
119
118
|
export declare function hrefBuilder(prefix?: string): (tagPath: string, state: CategoryState) => string;
|
|
120
119
|
/** Categories mounted at the root. */
|
|
121
120
|
export declare const defaultHref: (tagPath: string, state: CategoryState) => string;
|
|
122
|
-
export declare const DEFAULT_ALPHA_INDEX_MIN_PAGES = 40;
|
|
123
121
|
/** Bucket for titles that don't start with a letter (numerals, `$CAPER`). */
|
|
124
122
|
export declare function firstLetter(title: string): string;
|
|
125
123
|
/** Adding a filter already active removes it, so every chip is its own off-switch. */
|
package/dist/taxonomy.js
CHANGED
|
@@ -36,7 +36,7 @@ export function hrefBuilder(prefix = '') {
|
|
|
36
36
|
}
|
|
37
37
|
/** Categories mounted at the root. */
|
|
38
38
|
export const defaultHref = hrefBuilder();
|
|
39
|
-
|
|
39
|
+
const DEFAULT_ALPHA_INDEX_MIN_PAGES = 40;
|
|
40
40
|
const metaValue = (page, key) => (page.metadata?.[key] ?? '').trim();
|
|
41
41
|
/** Bucket for titles that don't start with a letter (numerals, `$CAPER`). */
|
|
42
42
|
export function firstLetter(title) {
|
package/dist/text.d.ts
CHANGED
|
@@ -33,15 +33,6 @@ export declare function bannerToText(label: string, text?: string | null): strin
|
|
|
33
33
|
export declare function codeTabsToText(tabs: readonly CodeTab[]): string;
|
|
34
34
|
/** A numbered reference list, or `''` when there are none. */
|
|
35
35
|
export declare function referencesToText(items: readonly ReferenceItem[]): string;
|
|
36
|
-
/**
|
|
37
|
-
* Every `text` value at any depth of a block tree, in document order.
|
|
38
|
-
*
|
|
39
|
-
* Deliberately NOT the typed extractor above: that walks a switch and formats
|
|
40
|
-
* for reading (labels, bullets, reference numbering), so it can surface text a
|
|
41
|
-
* search index never matched and miss text it did. A snippet claiming to show
|
|
42
|
-
* why a row matched has to read the same bytes the match was made against.
|
|
43
|
-
*/
|
|
44
|
-
export declare function collectText(node: unknown, out?: string[]): string[];
|
|
45
36
|
/**
|
|
46
37
|
* The passage that matched `query`, not the opening of the page.
|
|
47
38
|
*
|
package/dist/text.js
CHANGED
|
@@ -73,7 +73,7 @@ export function referencesToText(items) {
|
|
|
73
73
|
* search index never matched and miss text it did. A snippet claiming to show
|
|
74
74
|
* why a row matched has to read the same bytes the match was made against.
|
|
75
75
|
*/
|
|
76
|
-
|
|
76
|
+
function collectText(node, out = []) {
|
|
77
77
|
if (Array.isArray(node)) {
|
|
78
78
|
for (const item of node)
|
|
79
79
|
collectText(item, out);
|
package/dist/tiptap.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
// tiptap.tsx — the custom editor nodes
|
|
3
|
+
// tiptap.tsx — the custom editor nodes the wikis share.
|
|
4
4
|
//
|
|
5
5
|
// Behind its own subpath for the same reason `react.tsx` is: the four
|
|
6
6
|
// `@tiptap/*` packages are OPTIONAL peer dependencies, so a consumer that only
|
|
7
7
|
// wants the taxonomy or the MCP transport still installs a package with no
|
|
8
8
|
// runtime dependencies at all.
|
|
9
9
|
//
|
|
10
|
-
// The two copies had drifted in both directions — one had grown tabs the other
|
|
11
|
-
// lacked, the other had extracted the Twitter helper the first still wrote out
|
|
12
|
-
// three times — which is the shape of drift that costs the most: neither copy
|
|
13
|
-
// is behind, so neither looks like the one to fix.
|
|
14
|
-
//
|
|
15
10
|
// What is shared is the node schema and its behaviour, never appearance. Class
|
|
16
11
|
// names and icons are injected, which is what lets one wiki keep `text-jupiter`
|
|
17
12
|
// and the other `text-accent` without either forking the node. It also keeps
|
|
@@ -64,7 +59,7 @@ export const YouTube = TiptapYoutube.extend({
|
|
|
64
59
|
function TwitterEmbedView({ node }) {
|
|
65
60
|
const containerRef = useRef(null);
|
|
66
61
|
// The embed posts its measured height back; `onTweetResize` owns the origin
|
|
67
|
-
// check
|
|
62
|
+
// check.
|
|
68
63
|
useEffect(() => onTweetResize(height => {
|
|
69
64
|
const iframe = containerRef.current?.querySelector('iframe');
|
|
70
65
|
if (iframe)
|
package/dist/versioning.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// The block-diff half stays in the app: it has to walk a block tree, and every
|
|
4
4
|
// project owns its own type set. What travels is the version arithmetic and the
|
|
5
|
-
// rule for choosing a bump
|
|
5
|
+
// rule for choosing a bump.
|
|
6
6
|
/** Tolerant of null, empty and malformed input — a page always has a version. */
|
|
7
7
|
export function parseVersion(version) {
|
|
8
8
|
if (!version)
|
package/dist/well-known.d.ts
CHANGED
|
@@ -55,12 +55,6 @@ export interface AgentCardLicense {
|
|
|
55
55
|
/** What the licence covers, e.g. `'content'`. */
|
|
56
56
|
scope?: string;
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
59
|
-
* The A2A revision these cards are written to. Required since v0.3 — a card
|
|
60
|
-
* without it is not a card a spec-current client will accept, and all three
|
|
61
|
-
* origins here were serving one.
|
|
62
|
-
*/
|
|
63
|
-
export declare const A2A_PROTOCOL_VERSION = "0.3.0";
|
|
64
58
|
export interface AgentCardConfig {
|
|
65
59
|
name: string;
|
|
66
60
|
description: string;
|
package/dist/well-known.js
CHANGED
|
@@ -61,7 +61,7 @@ export function skillsFromTools(tools) {
|
|
|
61
61
|
* without it is not a card a spec-current client will accept, and all three
|
|
62
62
|
* origins here were serving one.
|
|
63
63
|
*/
|
|
64
|
-
|
|
64
|
+
const A2A_PROTOCOL_VERSION = '0.3.0';
|
|
65
65
|
/**
|
|
66
66
|
* An A2A Agent Card. Serve the same object at both `/.well-known/agent.json`
|
|
67
67
|
* and `/.well-known/agent-card.json`: v0.3 renamed the path and defined no
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-formant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
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, and conditional-GET plumbing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|