diffwiki-core 0.3.0 → 0.4.0-rc.202607220518.0ade9d3

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/index.d.cts CHANGED
@@ -12,6 +12,7 @@ interface Registry {
12
12
  }
13
13
  interface Config {
14
14
  defaultCollection?: string;
15
+ defaultSearch?: string;
15
16
  }
16
17
  interface Article {
17
18
  title: string;
@@ -25,6 +26,107 @@ interface RepoConfig {
25
26
  /** Path to the wiki directory, relative to the repo root. */
26
27
  path: string;
27
28
  }
29
+ /** A node in a collection's article tree. Branches have `children`; leaves have `slug`. */
30
+ interface ArticleNode {
31
+ /** Segment label — dir name for branches, filename-without-ext for leaves. */
32
+ name: string;
33
+ /** Display title — frontmatter `title` (leaf) or the folder's index title, else `name`. */
34
+ title?: string;
35
+ /**
36
+ * Path within the collection, without extension, e.g. "guide/setup". Set on
37
+ * leaves (the article) and on branches (the folder, navigable to its index).
38
+ */
39
+ slug?: string;
40
+ /** Branch only: nested nodes. */
41
+ children?: ArticleNode[];
42
+ }
43
+ /** A resolved article's content, returned by `readArticle`. */
44
+ interface ArticleContent {
45
+ coll: string;
46
+ slug: string;
47
+ title: string;
48
+ tags: string[];
49
+ /** Frontmatter-stripped body (md/mdx). */
50
+ body: string;
51
+ /** Absolute path to the file on disk. */
52
+ path: string;
53
+ }
54
+ type PluginKind = 'search';
55
+ declare const NATIVE_ENGINE = "native";
56
+ interface QueryOptions {
57
+ engine?: string;
58
+ collection?: string;
59
+ type?: string;
60
+ limit?: number;
61
+ }
62
+ interface QueryHit {
63
+ collection: string;
64
+ path: string;
65
+ title: string;
66
+ tags: string[];
67
+ snippet?: string;
68
+ score?: number;
69
+ docid?: string;
70
+ }
71
+ type DiagnosticLevel = 'ok' | 'warn' | 'error';
72
+ interface Diagnostic {
73
+ level: DiagnosticLevel;
74
+ message: string;
75
+ }
76
+ interface PluginRecord {
77
+ name: string;
78
+ kind: PluginKind;
79
+ command: string[];
80
+ version?: string;
81
+ enabled: boolean;
82
+ managed?: boolean;
83
+ }
84
+ interface PluginRegistryFile {
85
+ version: number;
86
+ plugins: PluginRecord[];
87
+ }
88
+ interface PluginCapabilities {
89
+ searchTypes: string[];
90
+ collectionFilter: boolean;
91
+ hooks: boolean;
92
+ setup: boolean;
93
+ }
94
+ interface PluginReadiness {
95
+ toolInstalled: boolean;
96
+ modelsPresent: boolean;
97
+ indexBuilt: boolean;
98
+ docsIndexed: number;
99
+ embeddingsPending: number;
100
+ firstSearchCost: 'instant' | 'model-load' | 'model-download';
101
+ }
102
+ interface PluginHealth {
103
+ ready: boolean;
104
+ readiness?: PluginReadiness;
105
+ diagnostics: Diagnostic[];
106
+ }
107
+ interface SearchMode {
108
+ plugin: string;
109
+ type: string;
110
+ label: string;
111
+ }
112
+ type PluginEvent = 'collection-added' | 'collection-removed' | 'article-created' | 'article-updated' | 'article-removed';
113
+ interface SearchProvider {
114
+ name: string;
115
+ capabilities: PluginCapabilities;
116
+ setup(collections: CollectionEntry[], opts?: {
117
+ embed?: boolean;
118
+ prefetchModels?: boolean;
119
+ }): Promise<{
120
+ ready: boolean;
121
+ }>;
122
+ reindex(collections: CollectionEntry[], opts?: {
123
+ embed?: boolean;
124
+ }): Promise<number>;
125
+ search(term: string, collections: CollectionEntry[], opts?: QueryOptions): Promise<QueryHit[]>;
126
+ notify(event: PluginEvent, collection: string, relPath: string | undefined, collections: CollectionEntry[]): Promise<void>;
127
+ health(collections: CollectionEntry[]): Promise<PluginHealth>;
128
+ close(): Promise<void>;
129
+ }
28
130
 
29
131
  /** Base class for all expected, user-facing diffwiki failures. */
30
132
  declare class DiffwikiError extends Error {
@@ -45,6 +147,8 @@ declare class ArticleNotFoundError extends DiffwikiError {
45
147
  declare class InvalidTargetError extends DiffwikiError {
46
148
  constructor(target: string, expected: string);
47
149
  }
150
+ declare class PluginError extends DiffwikiError {
151
+ }
48
152
 
49
153
  /** Root of all global diffwiki state. Overridable via DIFFWIKI_HOME (used by tests). */
50
154
  declare function resolveHome(): string;
@@ -52,6 +156,8 @@ declare function registryPath(): string;
52
156
  declare function configPath(): string;
53
157
  declare function collectionsDir(): string;
54
158
  declare function collectionDir(name: string): string;
159
+ declare function pluginsDir(): string;
160
+ declare function pluginsRegistryPath(): string;
55
161
 
56
162
  declare function readConfig(): Promise<Config>;
57
163
  declare function writeConfig(config: Config): Promise<void>;
@@ -123,23 +229,118 @@ interface InitOptions {
123
229
  }
124
230
  declare function initRepoWiki(opts: InitOptions): Promise<CollectionEntry>;
125
231
 
126
- interface QueryHit {
127
- collection: string;
128
- path: string;
129
- title: string;
130
- tags: string[];
131
- }
132
232
  /**
133
- * DUMMY search: case-insensitive substring match over title, filename, and tags.
134
- * Real QMD-based semantic search replaces this later.
233
+ * Built-in JS-native search: BM25-ranked full-text search over an article's
234
+ * title, body, tags, and collection name -- across every registered collection's
235
+ * ABSOLUTE registry path (so in-repo wikis outside ~/.diffwiki are searched too).
236
+ * No `find`/OS shelling -- cross-platform and dependency-free.
237
+ *
238
+ * When `term` is empty, returns all articles unranked (no score) to preserve
239
+ * the "browse all" behavior.
240
+ */
241
+ declare function nativeSearch(term: string, collections: CollectionEntry[], collection?: string): Promise<QueryHit[]>;
242
+ /**
243
+ * Resolve the engine + type that `query()` would use when the caller passes no
244
+ * explicit engine/type. Mirrors `resolveDefaultEngine`'s precedence and, when the
245
+ * resolved engine is a plugin, prefers the type from `config.defaultSearch`'s
246
+ * `:type` suffix (if that plugin advertises it) else the plugin's first advertised
247
+ * search type. The native engine always resolves to `basic`. UIs use this to
248
+ * pre-select the search-mode dropdown to the engine actually run server-side.
135
249
  */
136
- declare function query(term: string, collection?: string): Promise<QueryHit[]>;
250
+ declare function resolveDefaultSearchMode(): Promise<{
251
+ engine: string;
252
+ type: string;
253
+ }>;
254
+ declare function query(term: string, opts?: QueryOptions): Promise<QueryHit[]>;
137
255
 
138
- type DiagnosticLevel = 'ok' | 'warn' | 'error';
139
- interface Diagnostic {
140
- level: DiagnosticLevel;
141
- message: string;
142
- }
143
256
  declare function doctor(): Promise<Diagnostic[]>;
144
257
 
145
- export { type Article, ArticleNotFoundError, type CollectionEntry, CollectionExistsError, CollectionNotFoundError, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type InitOptions, InvalidTargetError, NoDefaultCollectionError, type ParsedArticle, type QueryHit, REPO_CONFIG_FILE, type Registry, type RepoConfig, type SerializableArticle, addTags, collectionDir, collectionsDir, configPath, createArticle, createCollection, doctor, findCollection, getConfigValue, initRepoWiki, listCollections, parseAddTarget, parseArticle, parsePathTarget, query, readConfig, readRegistry, readRepoConfig, registerCollection, registryPath, removeArticle, removeCollection, removeTags, resolveHome, serializeArticle, setConfigValue, setTags, slugify, unregisterCollection, updateArticleBody, writeConfig, writeRegistry };
258
+ /**
259
+ * Build a nested ArticleNode[] from a flat list of relative file paths.
260
+ * Pure — no I/O. Directories become branches; files become leaves whose `slug`
261
+ * is the relative path without extension. Dirs-first, then alpha (case-insensitive).
262
+ */
263
+ declare function buildArticleTree(relPaths: string[]): ArticleNode[];
264
+ /**
265
+ * List the article tree of a collection. Walks the collection's registered
266
+ * `entry.path` — so collections living OUTSIDE `~/.diffwiki/collections`
267
+ * (e.g. in-repo wikis registered via `diffwiki init`) work unchanged.
268
+ */
269
+ declare function listArticleTree(coll: string): Promise<ArticleNode[]>;
270
+ /**
271
+ * Read and resolve a single article by collection + slug (relative path without
272
+ * extension). Tries `.mdx` then `.md`, guards against path traversal, and splits
273
+ * frontmatter. Throws CollectionNotFoundError / ArticleNotFoundError / InvalidTargetError.
274
+ */
275
+ declare function readArticle(coll: string, slug: string): Promise<ArticleContent>;
276
+
277
+ /** A heading extracted for the table of contents (h2/h3). */
278
+ interface TocItem {
279
+ slug: string;
280
+ text: string;
281
+ depth: number;
282
+ }
283
+ /** The result of rendering an article body to HTML. */
284
+ interface RenderedArticle {
285
+ /** Fully-rendered, Shiki-highlighted HTML — mounted directly on the client (no eval). */
286
+ html: string;
287
+ /** h2/h3 headings collected during render, in document order. */
288
+ toc: TocItem[];
289
+ }
290
+ /**
291
+ * Render an article body (markdown) to Shiki-highlighted HTML plus its table of
292
+ * contents. Runs entirely server-side; the client mounts the HTML directly with
293
+ * no `eval`/`new Function`, so it works under a strict CSP. Code is highlighted
294
+ * here (dual github-light/github-dark via CSS variables); mermaid fences are
295
+ * marked for client rendering.
296
+ */
297
+ declare function renderArticle(body: string): Promise<RenderedArticle>;
298
+
299
+ declare function readPluginRegistry(): Promise<PluginRegistryFile>;
300
+ declare function writePluginRegistry(registry: PluginRegistryFile): Promise<void>;
301
+ /** Insert or replace the plugin record with the same name. */
302
+ declare function upsertPlugin(record: PluginRecord): Promise<void>;
303
+ declare function deregisterPlugin(name: string): Promise<void>;
304
+ declare function listEnabledPlugins(kind: PluginKind): Promise<PluginRecord[]>;
305
+ declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<PluginRecord | undefined>;
306
+
307
+ declare function spawnPluginClient(record: PluginRecord): Promise<SearchProvider>;
308
+
309
+ declare function loadSearchProvider(name?: string): Promise<SearchProvider | null>;
310
+ declare function listSearchModes(): Promise<SearchMode[]>;
311
+
312
+ /** Create the managed plugin root (an isolated npm project) and return its path. */
313
+ declare function ensurePluginRoot(): Promise<string>;
314
+ /**
315
+ * Install an npm spec into the managed root, register the discovered search bin,
316
+ * then (unless opts.setup === false) run the plugin's end-to-end `setup` so the
317
+ * user lands on a warm, working search. A setup failure does not unwind the
318
+ * install -- the record is saved and `doctor` surfaces the problem later.
319
+ */
320
+ declare function installPlugin(spec: string, opts?: {
321
+ setup?: boolean;
322
+ prefetchModels?: boolean;
323
+ }): Promise<PluginRecord>;
324
+ /**
325
+ * Register an already-present executable as a search plugin. Validates by spawning
326
+ * it (handshake + capability check) and runs its `setup` best-effort.
327
+ */
328
+ declare function registerPlugin(name: string, command: string[]): Promise<PluginRecord>;
329
+ /** Uninstall a managed plugin's package and deregister it. */
330
+ declare function removePlugin(name: string): Promise<void>;
331
+ /** List every registered plugin, marking whether it currently spawns + handshakes. */
332
+ declare function listPlugins(): Promise<Array<PluginRecord & {
333
+ spawnable: boolean;
334
+ }>>;
335
+
336
+ /**
337
+ * Fire a lifecycle hook at the active search plugin -- best-effort and
338
+ * non-blocking. Callers invoke this as `void emitPluginEvent(...)` and never
339
+ * await it, so a slow or absent plugin can neither stall nor fail a content
340
+ * mutation. Every error is swallowed; the lazy mtime-staleness reindex on
341
+ * `search` remains the correctness guarantee, so a dropped hook only costs a
342
+ * later lazy rebuild, never wrong results.
343
+ */
344
+ declare function emitPluginEvent(event: PluginEvent, collection: string, relPath?: string): Promise<void>;
345
+
346
+ export { type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, type CollectionEntry, CollectionExistsError, CollectionNotFoundError, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type InitOptions, InvalidTargetError, NATIVE_ENGINE, NoDefaultCollectionError, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderedArticle, type RepoConfig, type SearchMode, type SearchProvider, type SerializableArticle, type TocItem, addTags, buildArticleTree, collectionDir, collectionsDir, configPath, createArticle, createCollection, deregisterPlugin, doctor, emitPluginEvent, ensurePluginRoot, findCollection, findEnabledPlugin, getConfigValue, initRepoWiki, installPlugin, listArticleTree, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, resolveDefaultSearchMode, resolveHome, serializeArticle, setConfigValue, setTags, slugify, spawnPluginClient, unregisterCollection, updateArticleBody, upsertPlugin, writeConfig, writePluginRegistry, writeRegistry };
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ interface Registry {
12
12
  }
13
13
  interface Config {
14
14
  defaultCollection?: string;
15
+ defaultSearch?: string;
15
16
  }
16
17
  interface Article {
17
18
  title: string;
@@ -25,6 +26,107 @@ interface RepoConfig {
25
26
  /** Path to the wiki directory, relative to the repo root. */
26
27
  path: string;
27
28
  }
29
+ /** A node in a collection's article tree. Branches have `children`; leaves have `slug`. */
30
+ interface ArticleNode {
31
+ /** Segment label — dir name for branches, filename-without-ext for leaves. */
32
+ name: string;
33
+ /** Display title — frontmatter `title` (leaf) or the folder's index title, else `name`. */
34
+ title?: string;
35
+ /**
36
+ * Path within the collection, without extension, e.g. "guide/setup". Set on
37
+ * leaves (the article) and on branches (the folder, navigable to its index).
38
+ */
39
+ slug?: string;
40
+ /** Branch only: nested nodes. */
41
+ children?: ArticleNode[];
42
+ }
43
+ /** A resolved article's content, returned by `readArticle`. */
44
+ interface ArticleContent {
45
+ coll: string;
46
+ slug: string;
47
+ title: string;
48
+ tags: string[];
49
+ /** Frontmatter-stripped body (md/mdx). */
50
+ body: string;
51
+ /** Absolute path to the file on disk. */
52
+ path: string;
53
+ }
54
+ type PluginKind = 'search';
55
+ declare const NATIVE_ENGINE = "native";
56
+ interface QueryOptions {
57
+ engine?: string;
58
+ collection?: string;
59
+ type?: string;
60
+ limit?: number;
61
+ }
62
+ interface QueryHit {
63
+ collection: string;
64
+ path: string;
65
+ title: string;
66
+ tags: string[];
67
+ snippet?: string;
68
+ score?: number;
69
+ docid?: string;
70
+ }
71
+ type DiagnosticLevel = 'ok' | 'warn' | 'error';
72
+ interface Diagnostic {
73
+ level: DiagnosticLevel;
74
+ message: string;
75
+ }
76
+ interface PluginRecord {
77
+ name: string;
78
+ kind: PluginKind;
79
+ command: string[];
80
+ version?: string;
81
+ enabled: boolean;
82
+ managed?: boolean;
83
+ }
84
+ interface PluginRegistryFile {
85
+ version: number;
86
+ plugins: PluginRecord[];
87
+ }
88
+ interface PluginCapabilities {
89
+ searchTypes: string[];
90
+ collectionFilter: boolean;
91
+ hooks: boolean;
92
+ setup: boolean;
93
+ }
94
+ interface PluginReadiness {
95
+ toolInstalled: boolean;
96
+ modelsPresent: boolean;
97
+ indexBuilt: boolean;
98
+ docsIndexed: number;
99
+ embeddingsPending: number;
100
+ firstSearchCost: 'instant' | 'model-load' | 'model-download';
101
+ }
102
+ interface PluginHealth {
103
+ ready: boolean;
104
+ readiness?: PluginReadiness;
105
+ diagnostics: Diagnostic[];
106
+ }
107
+ interface SearchMode {
108
+ plugin: string;
109
+ type: string;
110
+ label: string;
111
+ }
112
+ type PluginEvent = 'collection-added' | 'collection-removed' | 'article-created' | 'article-updated' | 'article-removed';
113
+ interface SearchProvider {
114
+ name: string;
115
+ capabilities: PluginCapabilities;
116
+ setup(collections: CollectionEntry[], opts?: {
117
+ embed?: boolean;
118
+ prefetchModels?: boolean;
119
+ }): Promise<{
120
+ ready: boolean;
121
+ }>;
122
+ reindex(collections: CollectionEntry[], opts?: {
123
+ embed?: boolean;
124
+ }): Promise<number>;
125
+ search(term: string, collections: CollectionEntry[], opts?: QueryOptions): Promise<QueryHit[]>;
126
+ notify(event: PluginEvent, collection: string, relPath: string | undefined, collections: CollectionEntry[]): Promise<void>;
127
+ health(collections: CollectionEntry[]): Promise<PluginHealth>;
128
+ close(): Promise<void>;
129
+ }
28
130
 
29
131
  /** Base class for all expected, user-facing diffwiki failures. */
30
132
  declare class DiffwikiError extends Error {
@@ -45,6 +147,8 @@ declare class ArticleNotFoundError extends DiffwikiError {
45
147
  declare class InvalidTargetError extends DiffwikiError {
46
148
  constructor(target: string, expected: string);
47
149
  }
150
+ declare class PluginError extends DiffwikiError {
151
+ }
48
152
 
49
153
  /** Root of all global diffwiki state. Overridable via DIFFWIKI_HOME (used by tests). */
50
154
  declare function resolveHome(): string;
@@ -52,6 +156,8 @@ declare function registryPath(): string;
52
156
  declare function configPath(): string;
53
157
  declare function collectionsDir(): string;
54
158
  declare function collectionDir(name: string): string;
159
+ declare function pluginsDir(): string;
160
+ declare function pluginsRegistryPath(): string;
55
161
 
56
162
  declare function readConfig(): Promise<Config>;
57
163
  declare function writeConfig(config: Config): Promise<void>;
@@ -123,23 +229,118 @@ interface InitOptions {
123
229
  }
124
230
  declare function initRepoWiki(opts: InitOptions): Promise<CollectionEntry>;
125
231
 
126
- interface QueryHit {
127
- collection: string;
128
- path: string;
129
- title: string;
130
- tags: string[];
131
- }
132
232
  /**
133
- * DUMMY search: case-insensitive substring match over title, filename, and tags.
134
- * Real QMD-based semantic search replaces this later.
233
+ * Built-in JS-native search: BM25-ranked full-text search over an article's
234
+ * title, body, tags, and collection name -- across every registered collection's
235
+ * ABSOLUTE registry path (so in-repo wikis outside ~/.diffwiki are searched too).
236
+ * No `find`/OS shelling -- cross-platform and dependency-free.
237
+ *
238
+ * When `term` is empty, returns all articles unranked (no score) to preserve
239
+ * the "browse all" behavior.
240
+ */
241
+ declare function nativeSearch(term: string, collections: CollectionEntry[], collection?: string): Promise<QueryHit[]>;
242
+ /**
243
+ * Resolve the engine + type that `query()` would use when the caller passes no
244
+ * explicit engine/type. Mirrors `resolveDefaultEngine`'s precedence and, when the
245
+ * resolved engine is a plugin, prefers the type from `config.defaultSearch`'s
246
+ * `:type` suffix (if that plugin advertises it) else the plugin's first advertised
247
+ * search type. The native engine always resolves to `basic`. UIs use this to
248
+ * pre-select the search-mode dropdown to the engine actually run server-side.
135
249
  */
136
- declare function query(term: string, collection?: string): Promise<QueryHit[]>;
250
+ declare function resolveDefaultSearchMode(): Promise<{
251
+ engine: string;
252
+ type: string;
253
+ }>;
254
+ declare function query(term: string, opts?: QueryOptions): Promise<QueryHit[]>;
137
255
 
138
- type DiagnosticLevel = 'ok' | 'warn' | 'error';
139
- interface Diagnostic {
140
- level: DiagnosticLevel;
141
- message: string;
142
- }
143
256
  declare function doctor(): Promise<Diagnostic[]>;
144
257
 
145
- export { type Article, ArticleNotFoundError, type CollectionEntry, CollectionExistsError, CollectionNotFoundError, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type InitOptions, InvalidTargetError, NoDefaultCollectionError, type ParsedArticle, type QueryHit, REPO_CONFIG_FILE, type Registry, type RepoConfig, type SerializableArticle, addTags, collectionDir, collectionsDir, configPath, createArticle, createCollection, doctor, findCollection, getConfigValue, initRepoWiki, listCollections, parseAddTarget, parseArticle, parsePathTarget, query, readConfig, readRegistry, readRepoConfig, registerCollection, registryPath, removeArticle, removeCollection, removeTags, resolveHome, serializeArticle, setConfigValue, setTags, slugify, unregisterCollection, updateArticleBody, writeConfig, writeRegistry };
258
+ /**
259
+ * Build a nested ArticleNode[] from a flat list of relative file paths.
260
+ * Pure — no I/O. Directories become branches; files become leaves whose `slug`
261
+ * is the relative path without extension. Dirs-first, then alpha (case-insensitive).
262
+ */
263
+ declare function buildArticleTree(relPaths: string[]): ArticleNode[];
264
+ /**
265
+ * List the article tree of a collection. Walks the collection's registered
266
+ * `entry.path` — so collections living OUTSIDE `~/.diffwiki/collections`
267
+ * (e.g. in-repo wikis registered via `diffwiki init`) work unchanged.
268
+ */
269
+ declare function listArticleTree(coll: string): Promise<ArticleNode[]>;
270
+ /**
271
+ * Read and resolve a single article by collection + slug (relative path without
272
+ * extension). Tries `.mdx` then `.md`, guards against path traversal, and splits
273
+ * frontmatter. Throws CollectionNotFoundError / ArticleNotFoundError / InvalidTargetError.
274
+ */
275
+ declare function readArticle(coll: string, slug: string): Promise<ArticleContent>;
276
+
277
+ /** A heading extracted for the table of contents (h2/h3). */
278
+ interface TocItem {
279
+ slug: string;
280
+ text: string;
281
+ depth: number;
282
+ }
283
+ /** The result of rendering an article body to HTML. */
284
+ interface RenderedArticle {
285
+ /** Fully-rendered, Shiki-highlighted HTML — mounted directly on the client (no eval). */
286
+ html: string;
287
+ /** h2/h3 headings collected during render, in document order. */
288
+ toc: TocItem[];
289
+ }
290
+ /**
291
+ * Render an article body (markdown) to Shiki-highlighted HTML plus its table of
292
+ * contents. Runs entirely server-side; the client mounts the HTML directly with
293
+ * no `eval`/`new Function`, so it works under a strict CSP. Code is highlighted
294
+ * here (dual github-light/github-dark via CSS variables); mermaid fences are
295
+ * marked for client rendering.
296
+ */
297
+ declare function renderArticle(body: string): Promise<RenderedArticle>;
298
+
299
+ declare function readPluginRegistry(): Promise<PluginRegistryFile>;
300
+ declare function writePluginRegistry(registry: PluginRegistryFile): Promise<void>;
301
+ /** Insert or replace the plugin record with the same name. */
302
+ declare function upsertPlugin(record: PluginRecord): Promise<void>;
303
+ declare function deregisterPlugin(name: string): Promise<void>;
304
+ declare function listEnabledPlugins(kind: PluginKind): Promise<PluginRecord[]>;
305
+ declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<PluginRecord | undefined>;
306
+
307
+ declare function spawnPluginClient(record: PluginRecord): Promise<SearchProvider>;
308
+
309
+ declare function loadSearchProvider(name?: string): Promise<SearchProvider | null>;
310
+ declare function listSearchModes(): Promise<SearchMode[]>;
311
+
312
+ /** Create the managed plugin root (an isolated npm project) and return its path. */
313
+ declare function ensurePluginRoot(): Promise<string>;
314
+ /**
315
+ * Install an npm spec into the managed root, register the discovered search bin,
316
+ * then (unless opts.setup === false) run the plugin's end-to-end `setup` so the
317
+ * user lands on a warm, working search. A setup failure does not unwind the
318
+ * install -- the record is saved and `doctor` surfaces the problem later.
319
+ */
320
+ declare function installPlugin(spec: string, opts?: {
321
+ setup?: boolean;
322
+ prefetchModels?: boolean;
323
+ }): Promise<PluginRecord>;
324
+ /**
325
+ * Register an already-present executable as a search plugin. Validates by spawning
326
+ * it (handshake + capability check) and runs its `setup` best-effort.
327
+ */
328
+ declare function registerPlugin(name: string, command: string[]): Promise<PluginRecord>;
329
+ /** Uninstall a managed plugin's package and deregister it. */
330
+ declare function removePlugin(name: string): Promise<void>;
331
+ /** List every registered plugin, marking whether it currently spawns + handshakes. */
332
+ declare function listPlugins(): Promise<Array<PluginRecord & {
333
+ spawnable: boolean;
334
+ }>>;
335
+
336
+ /**
337
+ * Fire a lifecycle hook at the active search plugin -- best-effort and
338
+ * non-blocking. Callers invoke this as `void emitPluginEvent(...)` and never
339
+ * await it, so a slow or absent plugin can neither stall nor fail a content
340
+ * mutation. Every error is swallowed; the lazy mtime-staleness reindex on
341
+ * `search` remains the correctness guarantee, so a dropped hook only costs a
342
+ * later lazy rebuild, never wrong results.
343
+ */
344
+ declare function emitPluginEvent(event: PluginEvent, collection: string, relPath?: string): Promise<void>;
345
+
346
+ export { type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, type CollectionEntry, CollectionExistsError, CollectionNotFoundError, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type InitOptions, InvalidTargetError, NATIVE_ENGINE, NoDefaultCollectionError, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderedArticle, type RepoConfig, type SearchMode, type SearchProvider, type SerializableArticle, type TocItem, addTags, buildArticleTree, collectionDir, collectionsDir, configPath, createArticle, createCollection, deregisterPlugin, doctor, emitPluginEvent, ensurePluginRoot, findCollection, findEnabledPlugin, getConfigValue, initRepoWiki, installPlugin, listArticleTree, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, resolveDefaultSearchMode, resolveHome, serializeArticle, setConfigValue, setTags, slugify, spawnPluginClient, unregisterCollection, updateArticleBody, upsertPlugin, writeConfig, writePluginRegistry, writeRegistry };