wiki-formant 0.83.1 → 0.85.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/dist/text.js CHANGED
@@ -19,7 +19,7 @@ export function stripHtml(html) {
19
19
  .replace(/<a[^>]+href="([^"]*)"[^>]*>(.*?)<\/a>/gi, ' $2 ($1) ')
20
20
  .replace(/<br\s*\/?>/gi, '\n')
21
21
  .replace(/<\/(?:p|h[1-6]|li|tr|th|td|div)>/gi, '\n')
22
- .replace(/<(?:li)>/gi, '- ')
22
+ .replace(/<li\b[^>]*>/gi, '- ')
23
23
  .replace(/<[^>]+>/g, ''))
24
24
  .replace(/\n{3,}/g, '\n\n')
25
25
  .trim();
package/dist/tiptap.d.ts CHANGED
@@ -82,6 +82,11 @@ export interface TabsOptions {
82
82
  /** Names a newly added tab. Receives the 1-based position. */
83
83
  tabLabel?: (index: number) => string;
84
84
  }
85
+ /**
86
+ * A tab group and its items, which must be registered together — `tabGroup`'s
87
+ * content expression is `tabItem+`, so registering one without the other leaves
88
+ * a node type the schema cannot satisfy.
89
+ */
85
90
  export declare function createTabs({ classNames: own, icons: ownIcons, tabLabel, }?: TabsOptions): {
86
91
  TabGroup: TiptapNode<any, any>;
87
92
  TabItem: TiptapNode<any, any>;
package/dist/tiptap.js CHANGED
@@ -200,7 +200,8 @@ export function createMapEmbed({ resolveMapUrl }) {
200
200
  .insertContent({ type: 'mapEmbed', attrs: { src: 'about:blank', url } })
201
201
  .run();
202
202
  resolveMapUrl(url).then(resolved => {
203
- if (!resolved)
203
+ // The editor may have closed while the redirect was followed.
204
+ if (!resolved || ext.editor.isDestroyed)
204
205
  return;
205
206
  // Find the placeholder by its url rather than by a captured
206
207
  // position: the document has been editable throughout the hop.
@@ -282,11 +283,6 @@ export function createCodeBlock({ langs = CODE_LANGS, defaultLang = DEFAULT_LANG
282
283
  },
283
284
  });
284
285
  }
285
- /**
286
- * A tab group and its items, which must be registered together — `tabGroup`'s
287
- * content expression is `tabItem+`, so registering one without the other leaves
288
- * a node type the schema cannot satisfy.
289
- */
290
286
  /** The classes `base.css` draws the tab editor with, each replaceable. */
291
287
  const TABS_CLASSES = {
292
288
  editor: 'tabs-editor',
@@ -298,6 +294,11 @@ const TABS_CLASSES = {
298
294
  add: 'tab-add',
299
295
  content: 'tabs-content',
300
296
  };
297
+ /**
298
+ * A tab group and its items, which must be registered together — `tabGroup`'s
299
+ * content expression is `tabItem+`, so registering one without the other leaves
300
+ * a node type the schema cannot satisfy.
301
+ */
301
302
  export function createTabs({ classNames: own = {}, icons: ownIcons = {}, tabLabel = i => `Tab ${i}`, } = {}) {
302
303
  const classNames = { ...TABS_CLASSES, ...own };
303
304
  const icons = { add: '+', remove: '×', ...ownIcons };
@@ -328,8 +329,11 @@ export function createTabs({ classNames: own = {}, icons: ownIcons = {}, tabLabe
328
329
  return;
329
330
  const pos = posOf(index);
330
331
  editor.chain().focus().deleteRange({ from: pos, to: pos + tabs[index].nodeSize }).run();
331
- if (activeTab >= tabs.length - 1)
332
- setActiveTab(Math.max(0, tabs.length - 2));
332
+ // The open tab stays open: removing one before it moves it down a place.
333
+ const at = Math.min(activeTab, tabs.length - 1);
334
+ const next = Math.max(0, index < at ? at - 1 : Math.min(at, tabs.length - 2));
335
+ if (next !== activeTab)
336
+ setActiveTab(next);
333
337
  };
334
338
  const renameTab = (index, title) => {
335
339
  const { tr } = editor.state;
@@ -23,12 +23,16 @@ export function safeLinkHref(raw) {
23
23
  if (!raw)
24
24
  return null;
25
25
  // Strip C0 controls (including tab, newline and CR) and DEL before reading
26
- // the scheme, so a newline cannot smuggle one past the match below.
27
- const href = raw.replace(/[\u0000-\u0020\u007f]/g, '').trim();
26
+ // the scheme, so a newline cannot smuggle one past the match below. A space
27
+ // is not one: inside a URL it is the author's (`/My Photo.jpg`), and at
28
+ // either end `trim` takes it.
29
+ const href = raw.replace(/[\u0000-\u001f\u007f]/g, '').trim();
28
30
  if (!href)
29
31
  return null;
30
- if (href.startsWith('//'))
31
- return null; // protocol-relative -> external host
32
+ // Protocol-relative -> external host. A browser reads `\` as `/` here, so
33
+ // `/\evil.com` is the same link.
34
+ if (/^[\\/]{2}/.test(href))
35
+ return null;
32
36
  const scheme = href.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
33
37
  if (scheme) {
34
38
  const s = scheme[1].toLowerCase();
@@ -124,6 +124,8 @@ export interface McpManifestConfig {
124
124
  url: string;
125
125
  /** Who runs it. Defaults to `name`. */
126
126
  provider?: string;
127
+ /** The MCP endpoint's absolute URL. Defaults to `${url}/api/mcp`. */
128
+ mcpEndpoint?: string;
127
129
  tools: readonly ManifestTool[];
128
130
  /** Auth notes, per-tenant endpoints, payment terms — what this origin has that others do not. */
129
131
  extra?: Record<string, unknown>;
@@ -186,7 +186,7 @@ export function registryAuthHandler(publicKey = process.env.MCP_REGISTRY_PUBLIC_
186
186
  * so the manifest cannot advertise a version the server does not speak.
187
187
  */
188
188
  export function mcpManifest(config) {
189
- const { name, registryName, version, description, url, provider, tools, extra } = config;
189
+ const { name, registryName, version, description, url, provider, mcpEndpoint, tools, extra } = config;
190
190
  return {
191
191
  schema_version: '1.0',
192
192
  name,
@@ -197,7 +197,7 @@ export function mcpManifest(config) {
197
197
  provider: { name: provider ?? name, url },
198
198
  api: { type: 'openapi', url: `${url}/.well-known/openapi.json` },
199
199
  mcp: {
200
- endpoint: `${url}/api/mcp`,
200
+ endpoint: mcpEndpoint ?? `${url}/api/mcp`,
201
201
  transport: 'streamable-http',
202
202
  protocol: 'JSON-RPC 2.0',
203
203
  protocolVersion: MCP_PROTOCOL_VERSION,
@@ -34,7 +34,7 @@ export interface WikiToolsOptions<B extends DiffBlock> {
34
34
  metadata?: (tagPath: string, metadata: PageMetadata) => Record<string, unknown> | null;
35
35
  /** A category's name as the tree lists it. Defaults to the node's `name`. */
36
36
  label?: (node: WikiNode) => string;
37
- /** The corpus as sections, when the app's llms-full.txt sets their form. Defaults to one per page from the store. */
37
+ /** The corpus as sections, when the app sets their form. Defaults to one per page from the store. */
38
38
  corpus?: (tagPath?: string) => Promise<CorpusSection[]>;
39
39
  /** What an empty search says. */
40
40
  noMatch?: (query: string, tagPath?: string) => string;
@@ -8,9 +8,9 @@
8
8
  // lists a page here lists it the way its HTML route does, hidden paths included.
9
9
  //
10
10
  // What is the app's: where a page lives (`url`), how blocks read as text,
11
- // what a row adds (`metadata`), and how its corpus reads when it has an
12
- // llms-full.txt to match. Writes stay with each app, because who may write
13
- // is the one thing no two of them agree on.
11
+ // what a row adds (`metadata`), and how its corpus reads when it sets its own
12
+ // sections. Writes stay with each app, because who may write is the one thing
13
+ // no two of them agree on.
14
14
  import { McpToolError, readArgs, withAdjustments } from './mcp.js';
15
15
  import { listEnvelope } from './pagination.js';
16
16
  import { CORPUS_BUDGET, sliceCorpus } from './corpus.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-formant",
3
- "version": "0.83.1",
3
+ "version": "0.85.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, conditional-GET plumbing, and the headers and CSP every origin sends.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,6 +57,11 @@
57
57
  "import": "./dist/sitemap.js",
58
58
  "default": "./dist/sitemap.js"
59
59
  },
60
+ "./queues": {
61
+ "types": "./dist/queues.d.ts",
62
+ "import": "./dist/queues.js",
63
+ "default": "./dist/queues.js"
64
+ },
60
65
  "./versioning": {
61
66
  "types": "./dist/versioning.d.ts",
62
67
  "import": "./dist/versioning.js",
package/prisma/wiki.sql CHANGED
@@ -44,8 +44,6 @@ CREATE INDEX "WikiPage_siteId_tagPath_updatedAt_idx" ON "WikiPage"("siteId", "ta
44
44
  -- CreateIndex
45
45
  CREATE INDEX "WikiPage_siteId_updatedAt_idx" ON "WikiPage"("siteId", "updatedAt");
46
46
 
47
- -- CreateIndex
48
-
49
47
  -- CreateIndex
50
48
  CREATE UNIQUE INDEX "WikiPage_siteId_tagPath_slug_key" ON "WikiPage"("siteId", "tagPath", "slug");
51
49