diffwiki-core 0.4.0 → 0.5.0-rc.202607230329.fb70592
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/bm25.cjs +85 -0
- package/dist/bm25.d.cts +48 -0
- package/dist/bm25.d.ts +48 -0
- package/dist/bm25.js +8 -0
- package/dist/chunk-EEETAETA.js +145 -0
- package/dist/chunk-FTUF6IXS.js +60 -0
- package/dist/chunk-GVSRD5WB.js +500 -0
- package/dist/events-XSIUMRBG.js +6 -0
- package/dist/index.cjs +1490 -223
- package/dist/index.d.cts +534 -21
- package/dist/index.d.ts +534 -21
- package/dist/index.js +857 -239
- package/dist/plugin.cjs +175 -0
- package/dist/plugin.d.cts +229 -0
- package/dist/plugin.d.ts +229 -0
- package/dist/plugin.js +14 -0
- package/package.json +12 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { SearchDoc } from './bm25.cjs';
|
|
2
|
+
export { RankableFields, rankDocs, tokenize } from './bm25.cjs';
|
|
3
|
+
import { LogLevel, Logger } from '@logtape/logtape';
|
|
4
|
+
|
|
5
|
+
type CollectionType = 'global' | 'repo' | 'external';
|
|
2
6
|
interface CollectionEntry {
|
|
3
7
|
name: string;
|
|
4
8
|
type: CollectionType;
|
|
5
|
-
/** Absolute path to the collection's directory. */
|
|
9
|
+
/** Absolute path to the collection's content directory. */
|
|
6
10
|
path: string;
|
|
7
11
|
createdAt: string;
|
|
12
|
+
/** External only: stable git repo id (see `repoId`). Identity + dedupe key. */
|
|
13
|
+
id?: string;
|
|
14
|
+
/** External only: absolute repo root (branch/behind + open-at-root). */
|
|
15
|
+
repoRoot?: string;
|
|
16
|
+
/** External only: content dir relative to `repoRoot` (default "docs"). */
|
|
17
|
+
docsDir?: string;
|
|
8
18
|
}
|
|
9
19
|
interface Registry {
|
|
10
20
|
version: number;
|
|
@@ -12,6 +22,7 @@ interface Registry {
|
|
|
12
22
|
}
|
|
13
23
|
interface Config {
|
|
14
24
|
defaultCollection?: string;
|
|
25
|
+
defaultSearch?: string;
|
|
15
26
|
}
|
|
16
27
|
interface Article {
|
|
17
28
|
title: string;
|
|
@@ -50,6 +61,142 @@ interface ArticleContent {
|
|
|
50
61
|
/** Absolute path to the file on disk. */
|
|
51
62
|
path: string;
|
|
52
63
|
}
|
|
64
|
+
type PluginKind = 'search';
|
|
65
|
+
declare const NATIVE_ENGINE = "native";
|
|
66
|
+
interface QueryOptions {
|
|
67
|
+
engine?: string;
|
|
68
|
+
collection?: string;
|
|
69
|
+
type?: string;
|
|
70
|
+
limit?: number;
|
|
71
|
+
}
|
|
72
|
+
interface QueryHit {
|
|
73
|
+
collection: string;
|
|
74
|
+
path: string;
|
|
75
|
+
title: string;
|
|
76
|
+
tags: string[];
|
|
77
|
+
snippet?: string;
|
|
78
|
+
score?: number;
|
|
79
|
+
docid?: string;
|
|
80
|
+
}
|
|
81
|
+
type DiagnosticLevel = 'ok' | 'warn' | 'error';
|
|
82
|
+
interface Diagnostic {
|
|
83
|
+
level: DiagnosticLevel;
|
|
84
|
+
message: string;
|
|
85
|
+
}
|
|
86
|
+
interface PluginRecord {
|
|
87
|
+
name: string;
|
|
88
|
+
kind: PluginKind;
|
|
89
|
+
command: string[];
|
|
90
|
+
version?: string;
|
|
91
|
+
/** Capabilities cached at install/register time via the `describe` op. */
|
|
92
|
+
capabilities?: PluginCapabilities;
|
|
93
|
+
enabled: boolean;
|
|
94
|
+
managed?: boolean;
|
|
95
|
+
}
|
|
96
|
+
interface PluginRegistryFile {
|
|
97
|
+
version: number;
|
|
98
|
+
plugins: PluginRecord[];
|
|
99
|
+
}
|
|
100
|
+
/** Capabilities a plugin advertises via the `describe` op, cached in the registry. */
|
|
101
|
+
interface PluginCapabilities {
|
|
102
|
+
/** Search types the plugin supports (e.g. `['keyword', 'semantic', 'hybrid']`). */
|
|
103
|
+
searchTypes: string[];
|
|
104
|
+
/** Whether the plugin supports filtering results by collection. */
|
|
105
|
+
collectionFilter: boolean;
|
|
106
|
+
/** Whether the plugin handles lifecycle events (article/collection mutations). */
|
|
107
|
+
events: boolean;
|
|
108
|
+
}
|
|
109
|
+
interface PluginReadiness {
|
|
110
|
+
toolInstalled: boolean;
|
|
111
|
+
modelsPresent: boolean;
|
|
112
|
+
indexBuilt: boolean;
|
|
113
|
+
docsIndexed: number;
|
|
114
|
+
embeddingsPending: number;
|
|
115
|
+
firstSearchCost: 'instant' | 'model-load' | 'model-download';
|
|
116
|
+
}
|
|
117
|
+
interface PluginHealth {
|
|
118
|
+
ready: boolean;
|
|
119
|
+
readiness?: PluginReadiness;
|
|
120
|
+
diagnostics: Diagnostic[];
|
|
121
|
+
}
|
|
122
|
+
interface SearchMode {
|
|
123
|
+
plugin: string;
|
|
124
|
+
type: string;
|
|
125
|
+
label: string;
|
|
126
|
+
}
|
|
127
|
+
type PluginEvent = 'collection-added' | 'collection-removed' | 'article-created' | 'article-updated' | 'article-removed';
|
|
128
|
+
/**
|
|
129
|
+
* Host-side facade for a search plugin. Each method is backed by a one-shot
|
|
130
|
+
* subprocess invocation -- there is no persistent process and no `close()`.
|
|
131
|
+
*/
|
|
132
|
+
interface SearchProvider {
|
|
133
|
+
/** Plugin name. */
|
|
134
|
+
name: string;
|
|
135
|
+
/** Plugin capabilities (cached from the `describe` op). */
|
|
136
|
+
capabilities: PluginCapabilities;
|
|
137
|
+
/** Run a search query and return hits with absolute paths. */
|
|
138
|
+
handleSearch(term: string, collections: CollectionEntry[], opts?: QueryOptions): Promise<QueryHit[]>;
|
|
139
|
+
/** Rebuild the search index. Returns the number of documents indexed. */
|
|
140
|
+
reindex(collections: CollectionEntry[], opts?: {
|
|
141
|
+
embed?: boolean;
|
|
142
|
+
}): Promise<number>;
|
|
143
|
+
/** Emit a lifecycle event to the plugin (best-effort). */
|
|
144
|
+
emitEvent(event: PluginEvent, collection: string, relPath: string | undefined, collections: CollectionEntry[]): Promise<void>;
|
|
145
|
+
/** Check plugin health and readiness. */
|
|
146
|
+
health(collections: CollectionEntry[]): Promise<PluginHealth>;
|
|
147
|
+
}
|
|
148
|
+
interface NodeMeta {
|
|
149
|
+
branch?: string;
|
|
150
|
+
behind?: number;
|
|
151
|
+
}
|
|
152
|
+
interface TreeNode {
|
|
153
|
+
name: string;
|
|
154
|
+
title?: string;
|
|
155
|
+
path?: string;
|
|
156
|
+
children?: TreeNode[];
|
|
157
|
+
/** Set only on top-level collection rows: git branch/behind badge data. */
|
|
158
|
+
meta?: NodeMeta;
|
|
159
|
+
}
|
|
160
|
+
interface CollectionInfo {
|
|
161
|
+
name: string;
|
|
162
|
+
type: CollectionType;
|
|
163
|
+
path: string;
|
|
164
|
+
branch?: string;
|
|
165
|
+
behind?: number;
|
|
166
|
+
}
|
|
167
|
+
interface CollectionTree {
|
|
168
|
+
collection: CollectionInfo;
|
|
169
|
+
nodes: TreeNode[];
|
|
170
|
+
}
|
|
171
|
+
interface TocItem {
|
|
172
|
+
slug: string;
|
|
173
|
+
text: string;
|
|
174
|
+
depth: number;
|
|
175
|
+
}
|
|
176
|
+
interface DocData {
|
|
177
|
+
coll: string;
|
|
178
|
+
slug: string;
|
|
179
|
+
title: string;
|
|
180
|
+
tags: string[];
|
|
181
|
+
html: string;
|
|
182
|
+
toc: TocItem[];
|
|
183
|
+
path: string;
|
|
184
|
+
}
|
|
185
|
+
interface ListingEntry {
|
|
186
|
+
name: string;
|
|
187
|
+
title: string;
|
|
188
|
+
path: string;
|
|
189
|
+
isDir: boolean;
|
|
190
|
+
}
|
|
191
|
+
type PageData = ({
|
|
192
|
+
kind: 'doc';
|
|
193
|
+
} & DocData) | {
|
|
194
|
+
kind: 'listing';
|
|
195
|
+
coll: string;
|
|
196
|
+
slug: string;
|
|
197
|
+
title: string;
|
|
198
|
+
entries: ListingEntry[];
|
|
199
|
+
};
|
|
53
200
|
|
|
54
201
|
/** Base class for all expected, user-facing diffwiki failures. */
|
|
55
202
|
declare class DiffwikiError extends Error {
|
|
@@ -70,6 +217,14 @@ declare class ArticleNotFoundError extends DiffwikiError {
|
|
|
70
217
|
declare class InvalidTargetError extends DiffwikiError {
|
|
71
218
|
constructor(target: string, expected: string);
|
|
72
219
|
}
|
|
220
|
+
declare class PluginError extends DiffwikiError {
|
|
221
|
+
}
|
|
222
|
+
declare class WorktreeNotAllowedError extends DiffwikiError {
|
|
223
|
+
constructor(root: string);
|
|
224
|
+
}
|
|
225
|
+
declare class CollectionAlreadyAddedError extends DiffwikiError {
|
|
226
|
+
constructor(name: string);
|
|
227
|
+
}
|
|
73
228
|
|
|
74
229
|
/** Root of all global diffwiki state. Overridable via DIFFWIKI_HOME (used by tests). */
|
|
75
230
|
declare function resolveHome(): string;
|
|
@@ -77,6 +232,8 @@ declare function registryPath(): string;
|
|
|
77
232
|
declare function configPath(): string;
|
|
78
233
|
declare function collectionsDir(): string;
|
|
79
234
|
declare function collectionDir(name: string): string;
|
|
235
|
+
declare function pluginsDir(): string;
|
|
236
|
+
declare function pluginsRegistryPath(): string;
|
|
80
237
|
|
|
81
238
|
declare function readConfig(): Promise<Config>;
|
|
82
239
|
declare function writeConfig(config: Config): Promise<void>;
|
|
@@ -110,6 +267,7 @@ declare function serializeArticle(article: SerializableArticle): string;
|
|
|
110
267
|
|
|
111
268
|
declare function listCollections(): Promise<CollectionEntry[]>;
|
|
112
269
|
declare function findCollection(name: string): Promise<CollectionEntry | undefined>;
|
|
270
|
+
declare function findCollectionById(id: string): Promise<CollectionEntry | undefined>;
|
|
113
271
|
declare function createCollection(name: string): Promise<CollectionEntry>;
|
|
114
272
|
/**
|
|
115
273
|
* Remove a collection from the registry. For `global` collections we own the
|
|
@@ -147,26 +305,225 @@ interface InitOptions {
|
|
|
147
305
|
wikiPath?: string;
|
|
148
306
|
}
|
|
149
307
|
declare function initRepoWiki(opts: InitOptions): Promise<CollectionEntry>;
|
|
308
|
+
interface AddExternalOptions {
|
|
309
|
+
cwd: string;
|
|
310
|
+
/** Content dir relative to the repo root. Default "docs". */
|
|
311
|
+
docsDir?: string;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Register the git repo containing `cwd` as an `external` collection: title from
|
|
315
|
+
* the git remote (fallback repo dir), content dir `docs/` by default, git-only
|
|
316
|
+
* (auto `git init`), unique by repo id, and refusing linked worktrees.
|
|
317
|
+
*/
|
|
318
|
+
declare function addExternalCollection(opts: AddExternalOptions): Promise<CollectionEntry>;
|
|
319
|
+
/**
|
|
320
|
+
* The registered collection the current directory belongs to: an external
|
|
321
|
+
* collection whose repo id matches, else any collection whose content path (or
|
|
322
|
+
* repoRoot) contains `cwd`. Undefined when cwd is outside every known collection.
|
|
323
|
+
*/
|
|
324
|
+
declare function resolveCwdCollection(cwd: string): Promise<CollectionEntry | undefined>;
|
|
325
|
+
interface CollectionGitStatus {
|
|
326
|
+
branch?: string;
|
|
327
|
+
ahead: number;
|
|
328
|
+
behind: number;
|
|
329
|
+
hasUpstream: boolean;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Branch + upstream status for an external collection, from its repo root.
|
|
333
|
+
* Undefined for non-external collections or when git is unavailable (e.g. during
|
|
334
|
+
* static generation) — callers warn + omit.
|
|
335
|
+
*/
|
|
336
|
+
declare function collectionGitStatus(entry: CollectionEntry): Promise<CollectionGitStatus | undefined>;
|
|
150
337
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
338
|
+
/** Absolute working-tree root, or undefined when `cwd` is not inside a git repo. */
|
|
339
|
+
declare function gitToplevel(cwd: string): Promise<string | undefined>;
|
|
340
|
+
/**
|
|
341
|
+
* True when `cwd` sits inside a *linked* worktree (not the primary working tree).
|
|
342
|
+
* In the primary tree `--absolute-git-dir` and `--git-common-dir` resolve to the
|
|
343
|
+
* same directory; in a linked worktree the git-dir is `<common>/worktrees/<name>`.
|
|
344
|
+
*/
|
|
345
|
+
declare function isLinkedWorktree(cwd: string): Promise<boolean>;
|
|
346
|
+
/** Short repo name from the `origin` remote URL (basename, `.git` stripped), or undefined. */
|
|
347
|
+
declare function repoRemoteName(cwd: string): Promise<string | undefined>;
|
|
348
|
+
/**
|
|
349
|
+
* A stable identifier for the repo, independent of checkout location. Prefers the
|
|
350
|
+
* root (first) commit hash — identical across clones/worktrees; falls back to the
|
|
351
|
+
* normalised origin URL when there is no commit yet. Undefined when neither exists.
|
|
352
|
+
*/
|
|
353
|
+
declare function repoId(cwd: string): Promise<string | undefined>;
|
|
354
|
+
/** Current short branch name, or undefined (detached HEAD / not a repo). */
|
|
355
|
+
declare function currentBranch(cwd: string): Promise<string | undefined>;
|
|
356
|
+
interface UpstreamStatus {
|
|
357
|
+
branch?: string;
|
|
358
|
+
ahead: number;
|
|
359
|
+
behind: number;
|
|
360
|
+
hasUpstream: boolean;
|
|
156
361
|
}
|
|
157
362
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
363
|
+
* Ahead/behind counts of the current branch versus its upstream. `hasUpstream` is
|
|
364
|
+
* false when no upstream is configured (then ahead/behind are 0).
|
|
160
365
|
*/
|
|
161
|
-
declare function
|
|
366
|
+
declare function upstreamStatus(cwd: string): Promise<UpstreamStatus>;
|
|
367
|
+
/** Ensure `cwd` is inside a git repo, running `git init` if not. Returns the toplevel. */
|
|
368
|
+
declare function ensureGitRepo(cwd: string): Promise<string>;
|
|
162
369
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
370
|
+
/** Map core article nodes to UI nav nodes, assigning each its route path. */
|
|
371
|
+
declare function toTreeNodes(coll: string, nodes: ArticleNode[]): TreeNode[];
|
|
372
|
+
declare function buildCollections(): Promise<CollectionInfo[]>;
|
|
373
|
+
declare function buildNavTree(): Promise<CollectionTree[]>;
|
|
374
|
+
/** Resolve a route to a rendered doc or a generated directory listing (or null). */
|
|
375
|
+
declare function resolvePage(coll: string, slug: string): Promise<PageData | null>;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Search query routing.
|
|
379
|
+
*
|
|
380
|
+
* `query()` is the primary search entry point. It resolves the search engine
|
|
381
|
+
* (explicit opt, config default, first enabled plugin, or native BM25), runs
|
|
382
|
+
* the search, and falls back to native on plugin failure. The built-in native
|
|
383
|
+
* search is a zero-dependency BM25-ranked full-text search across title, body,
|
|
384
|
+
* tags, and collection name.
|
|
385
|
+
*
|
|
386
|
+
* @module
|
|
387
|
+
*/
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Built-in JS-native search: BM25-ranked full-text search over an article's
|
|
391
|
+
* title, body, tags, and collection name -- across every registered collection's
|
|
392
|
+
* ABSOLUTE registry path (so in-repo wikis outside ~/.diffwiki are searched too).
|
|
393
|
+
* No `find`/OS shelling -- cross-platform and dependency-free.
|
|
394
|
+
*
|
|
395
|
+
* When `term` is empty, returns all articles unranked (no score) to preserve
|
|
396
|
+
* the "browse all" behavior.
|
|
397
|
+
*/
|
|
398
|
+
declare function nativeSearch(term: string, collections: CollectionEntry[], collection?: string): Promise<QueryHit[]>;
|
|
399
|
+
/**
|
|
400
|
+
* Resolve the engine + type that `query()` would use when the caller passes no
|
|
401
|
+
* explicit engine/type. Reads cached capabilities from the registry -- no spawn.
|
|
402
|
+
*/
|
|
403
|
+
declare function resolveDefaultSearchMode(): Promise<{
|
|
404
|
+
engine: string;
|
|
405
|
+
type: string;
|
|
406
|
+
}>;
|
|
407
|
+
/**
|
|
408
|
+
* The result of a {@link search} call: the hits plus the metadata a host UI
|
|
409
|
+
* needs to tell the user WHICH engine ran and whether it silently degraded.
|
|
410
|
+
*
|
|
411
|
+
* `fellBackToNative` is true whenever the resolved engine could not serve the
|
|
412
|
+
* search (no provider, or `handleSearch` threw) and native BM25 produced these
|
|
413
|
+
* hits instead. `error`, when set, is the captured message from the failing
|
|
414
|
+
* engine — a failing engine is thus distinguishable from an engine that
|
|
415
|
+
* legitimately found zero results.
|
|
416
|
+
*/
|
|
417
|
+
interface SearchOutcome {
|
|
418
|
+
/** The hits to display (from the engine, or from native on fallback). */
|
|
419
|
+
hits: QueryHit[];
|
|
420
|
+
/** The engine that was resolved and attempted (may differ from what produced `hits`). */
|
|
421
|
+
engine: string;
|
|
422
|
+
/** True when native BM25 produced `hits` because the engine failed/was unavailable. */
|
|
423
|
+
fellBackToNative: boolean;
|
|
424
|
+
/** The captured failure message from the engine, when it errored. */
|
|
425
|
+
error?: string;
|
|
167
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Search articles via the resolved search engine, surfacing engine failures.
|
|
429
|
+
*
|
|
430
|
+
* Routing precedence: explicit `opts.engine` -> `config.defaultSearch` ->
|
|
431
|
+
* first enabled plugin -> native BM25.
|
|
432
|
+
*
|
|
433
|
+
* On ANY engine failure (no provider, or `handleSearch` throws) the error
|
|
434
|
+
* message is CAPTURED, native BM25 is run as a fallback, and the outcome
|
|
435
|
+
* reports `{ fellBackToNative: true, error }`. On success it reports
|
|
436
|
+
* `{ fellBackToNative: false }` with the engine's hits. This lets callers
|
|
437
|
+
* distinguish "the engine broke" from "the engine found nothing".
|
|
438
|
+
*/
|
|
439
|
+
declare function search(term: string, opts?: QueryOptions): Promise<SearchOutcome>;
|
|
440
|
+
/**
|
|
441
|
+
* Search articles via the resolved search engine (native fallback on plugin
|
|
442
|
+
* failure). A thin wrapper over {@link search} that returns just the hits;
|
|
443
|
+
* engine errors are swallowed. Use {@link search} to surface those errors.
|
|
444
|
+
*
|
|
445
|
+
* Routing precedence: explicit `opts.engine` -> `config.defaultSearch` ->
|
|
446
|
+
* first enabled plugin -> native BM25.
|
|
447
|
+
*/
|
|
448
|
+
declare function query(term: string, opts?: QueryOptions): Promise<QueryHit[]>;
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Build the search index: one {@link SearchDoc} per article, carrying routing
|
|
452
|
+
* metadata (collection/slug/title/tags) AND the pre-tokenized fields BM25 ranks.
|
|
453
|
+
* The static exporter serialises this to `search-index.json`; the browser ranks it
|
|
454
|
+
* with the shared `rankDocs` (same algorithm as the server's native search), so
|
|
455
|
+
* keyword search works fully offline on the static site.
|
|
456
|
+
*/
|
|
457
|
+
declare function buildSearchIndex(collection?: string): Promise<SearchDoc[]>;
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Diagnostics and health checks.
|
|
461
|
+
*
|
|
462
|
+
* `doctor()` inspects the diffwiki home directory, registry, collections,
|
|
463
|
+
* config, and every registered plugin to produce a list of human-readable
|
|
464
|
+
* diagnostics at ok/warn/error severity levels.
|
|
465
|
+
*
|
|
466
|
+
* @module
|
|
467
|
+
*/
|
|
468
|
+
|
|
469
|
+
/** Run all diagnostics and return the results. */
|
|
168
470
|
declare function doctor(): Promise<Diagnostic[]>;
|
|
169
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Centralized logging for diffwiki host code, built on LogTape.
|
|
474
|
+
*
|
|
475
|
+
* diffwiki-core (and the plugin SDK) are LIBRARIES: they obtain loggers via
|
|
476
|
+
* {@link getLogger} and emit records, but they NEVER call LogTape's
|
|
477
|
+
* `configure()`/`configureSync()`. Per the LogTape library-vs-app pattern, a
|
|
478
|
+
* library stays silent until the surrounding APPLICATION (the CLI or the web
|
|
479
|
+
* app) installs sinks. `getLogger` called before any `configure` is a safe
|
|
480
|
+
* no-op, so importing this module has no side effects.
|
|
481
|
+
*
|
|
482
|
+
* All diffwiki loggers live under the `['diffwiki', ...]` category so an app can
|
|
483
|
+
* route the whole tree with a single logger config entry. Plugin diagnostics
|
|
484
|
+
* are forwarded under `['diffwiki', 'plugin', <name>]` (see `plugins/host.ts`).
|
|
485
|
+
*
|
|
486
|
+
* [DEVIATION] The task brief lists the level name `warn`, but the installed
|
|
487
|
+
* `@logtape/logtape` (2.2.4) names that level `warning` (levels are
|
|
488
|
+
* `trace|debug|info|warning|error|fatal`). We follow the installed types:
|
|
489
|
+
* {@link DiffwikiLogLevel} uses `warning`, and {@link resolveLogLevel} defaults
|
|
490
|
+
* to `warning`. Note LogTape's Logger METHOD for that level is still `.warn()`.
|
|
491
|
+
*
|
|
492
|
+
* @module
|
|
493
|
+
*/
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* A diffwiki log level. Identical to LogTape's `LogLevel`
|
|
497
|
+
* (`trace|debug|info|warning|error|fatal`); re-exported so host and app code can
|
|
498
|
+
* refer to it without importing LogTape directly.
|
|
499
|
+
*/
|
|
500
|
+
type DiffwikiLogLevel = LogLevel;
|
|
501
|
+
/**
|
|
502
|
+
* Get a diffwiki logger under the shared `['diffwiki', ...]` category root.
|
|
503
|
+
*
|
|
504
|
+
* This is a thin wrapper over LogTape's `getLogger`. It does NOT configure
|
|
505
|
+
* LogTape — libraries stay silent until an app calls `configure()`. Passing a
|
|
506
|
+
* string scope is sugar for a single-element category array.
|
|
507
|
+
*
|
|
508
|
+
* @param scope - A scope name (e.g. `'query'`) or a nested category path
|
|
509
|
+
* (e.g. `['plugin', 'diffwiki-qmd']`).
|
|
510
|
+
* @returns A LogTape {@link Logger} under `['diffwiki', ...scope]`.
|
|
511
|
+
*/
|
|
512
|
+
declare function getLogger(scope: string | string[]): Logger;
|
|
513
|
+
/**
|
|
514
|
+
* Resolve the diffwiki log level from the environment.
|
|
515
|
+
*
|
|
516
|
+
* Reads `DIFFWIKI_LOG` (one of `trace|debug|info|warning|error|fatal`, case
|
|
517
|
+
* insensitive) and returns it, falling back to the documented default
|
|
518
|
+
* (`warning`) when unset or unrecognized. Apps use this to pick the app-level
|
|
519
|
+
* sink's `lowestLevel` at startup.
|
|
520
|
+
*
|
|
521
|
+
* @param env - The environment to read (defaults to `process.env`). Injectable
|
|
522
|
+
* for tests.
|
|
523
|
+
* @returns The resolved {@link DiffwikiLogLevel}.
|
|
524
|
+
*/
|
|
525
|
+
declare function resolveLogLevel(env?: NodeJS.ProcessEnv): DiffwikiLogLevel;
|
|
526
|
+
|
|
170
527
|
/**
|
|
171
528
|
* Build a nested ArticleNode[] from a flat list of relative file paths.
|
|
172
529
|
* Pure — no I/O. Directories become branches; files become leaves whose `slug`
|
|
@@ -186,12 +543,6 @@ declare function listArticleTree(coll: string): Promise<ArticleNode[]>;
|
|
|
186
543
|
*/
|
|
187
544
|
declare function readArticle(coll: string, slug: string): Promise<ArticleContent>;
|
|
188
545
|
|
|
189
|
-
/** A heading extracted for the table of contents (h2/h3). */
|
|
190
|
-
interface TocItem {
|
|
191
|
-
slug: string;
|
|
192
|
-
text: string;
|
|
193
|
-
depth: number;
|
|
194
|
-
}
|
|
195
546
|
/** The result of rendering an article body to HTML. */
|
|
196
547
|
interface RenderedArticle {
|
|
197
548
|
/** Fully-rendered, Shiki-highlighted HTML — mounted directly on the client (no eval). */
|
|
@@ -208,4 +559,166 @@ interface RenderedArticle {
|
|
|
208
559
|
*/
|
|
209
560
|
declare function renderArticle(body: string): Promise<RenderedArticle>;
|
|
210
561
|
|
|
211
|
-
|
|
562
|
+
/**
|
|
563
|
+
* Host-side plugin invocation and search-provider loading.
|
|
564
|
+
*
|
|
565
|
+
* `invokePlugin()` spawns a plugin subprocess, writes a single JSON request to
|
|
566
|
+
* stdin (then closes it), reads the JSON response from stdout, and lets the
|
|
567
|
+
* process exit. Progress/diagnostic logs arrive on STDERR as structured
|
|
568
|
+
* `{log:{level,message}}` JSON lines; the host forwards each to a LogTape logger
|
|
569
|
+
* scoped by plugin name and keeps the raw stderr for on-error context.
|
|
570
|
+
*
|
|
571
|
+
* `loadSearchProvider()` returns a `SearchProvider` backed by one-shot
|
|
572
|
+
* invocations — each method call spawns the plugin fresh. `listSearchModes()`
|
|
573
|
+
* reads cached capabilities from the registry rather than spawning each plugin.
|
|
574
|
+
*
|
|
575
|
+
* @module
|
|
576
|
+
*/
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Load a search provider backed by one-shot subprocess invocations. Each method
|
|
580
|
+
* on the returned `SearchProvider` spawns the plugin binary, sends one JSON
|
|
581
|
+
* request, and reads the response. Returns `null` if no matching enabled plugin
|
|
582
|
+
* is found or the plugin fails its `describe` handshake.
|
|
583
|
+
*
|
|
584
|
+
* @param name - Optional plugin name. If omitted, the first enabled search plugin is used.
|
|
585
|
+
*/
|
|
586
|
+
declare function loadSearchProvider(name?: string): Promise<SearchProvider | null>;
|
|
587
|
+
/**
|
|
588
|
+
* List all available search modes, starting with native BM25. Reads cached
|
|
589
|
+
* capabilities from the plugin registry rather than spawning each plugin.
|
|
590
|
+
* Plugins without cached capabilities (registered before the SDK refactor) are
|
|
591
|
+
* skipped -- re-register to populate them.
|
|
592
|
+
*/
|
|
593
|
+
declare function listSearchModes(): Promise<SearchMode[]>;
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Plugin lifecycle management.
|
|
597
|
+
*
|
|
598
|
+
* Handles installing (from npm), registering (from an existing binary),
|
|
599
|
+
* removing, and listing plugins. Installation and registration both run
|
|
600
|
+
* a `describe` op to validate the plugin and cache its capabilities in
|
|
601
|
+
* the registry, then a best-effort `setup` op to provision the plugin.
|
|
602
|
+
*
|
|
603
|
+
* @module
|
|
604
|
+
*/
|
|
605
|
+
|
|
606
|
+
/** Create the managed plugin root (an isolated npm project) and return its path. */
|
|
607
|
+
declare function ensurePluginRoot(): Promise<string>;
|
|
608
|
+
/** One provisioning step reported by a plugin's `setup` op. */
|
|
609
|
+
interface SetupStep {
|
|
610
|
+
step: string;
|
|
611
|
+
ok: boolean;
|
|
612
|
+
message?: string;
|
|
613
|
+
}
|
|
614
|
+
/** The captured result of a plugin's `setup` op. */
|
|
615
|
+
interface SetupOutcome {
|
|
616
|
+
/** Whether the plugin provisioned to a ready-to-search state. */
|
|
617
|
+
ready: boolean;
|
|
618
|
+
/** Per-step results (e.g. verify-qmd, add-collections, index). */
|
|
619
|
+
steps: SetupStep[];
|
|
620
|
+
}
|
|
621
|
+
/** The result of {@link installPlugin}: the registered record + the setup outcome (if setup ran). */
|
|
622
|
+
interface InstallOutcome {
|
|
623
|
+
record: PluginRecord;
|
|
624
|
+
/** The setup result, or undefined when setup was skipped (`opts.setup === false`). */
|
|
625
|
+
setup?: SetupOutcome;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Install an npm spec into the managed root, register the discovered search bin,
|
|
629
|
+
* run a `describe` to validate and cache capabilities, then (unless `opts.setup
|
|
630
|
+
* === false`) run the plugin's `setup` op so the user lands on a working search.
|
|
631
|
+
*/
|
|
632
|
+
declare function installPlugin(spec: string, opts?: {
|
|
633
|
+
setup?: boolean;
|
|
634
|
+
prefetchModels?: boolean;
|
|
635
|
+
}): Promise<InstallOutcome>;
|
|
636
|
+
/**
|
|
637
|
+
* Register an existing executable as a search plugin. Validates by running
|
|
638
|
+
* `describe`, caches capabilities, then runs `setup` best-effort.
|
|
639
|
+
*/
|
|
640
|
+
declare function registerPlugin(name: string, command: string[]): Promise<PluginRecord>;
|
|
641
|
+
/** Uninstall a managed plugin's package and deregister it. */
|
|
642
|
+
declare function removePlugin(name: string): Promise<void>;
|
|
643
|
+
/** List every registered plugin, marking whether it currently responds to `describe`. */
|
|
644
|
+
declare function listPlugins(): Promise<Array<PluginRecord & {
|
|
645
|
+
spawnable: boolean;
|
|
646
|
+
}>>;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Plugin lifecycle event emission.
|
|
650
|
+
*
|
|
651
|
+
* Fires a lifecycle event at the active search plugin -- best-effort and
|
|
652
|
+
* non-blocking. diffwiki emits events on every content mutation (article
|
|
653
|
+
* create/update/remove, collection add/remove). A plugin's `onEvent`
|
|
654
|
+
* handler can self-manage in response (e.g. incremental reindex).
|
|
655
|
+
*
|
|
656
|
+
* Events are an optimisation, not the correctness path: the mtime-staleness
|
|
657
|
+
* lazy reindex on `search` remains the guarantee, so a dropped event only
|
|
658
|
+
* costs a later lazy rebuild, never wrong results.
|
|
659
|
+
*
|
|
660
|
+
* @module
|
|
661
|
+
*/
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Emit a lifecycle event to the active search plugin.
|
|
665
|
+
*
|
|
666
|
+
* This function is designed to be called fire-and-forget: `void emitPluginEvent(...)`.
|
|
667
|
+
* It only invokes the plugin when the plugin has `capabilities.events === true`.
|
|
668
|
+
* Every error is swallowed -- a slow or absent plugin can neither stall nor
|
|
669
|
+
* fail a content mutation.
|
|
670
|
+
*/
|
|
671
|
+
declare function emitPluginEvent(event: PluginEvent, collection: string, relPath?: string): Promise<void>;
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Known plugin catalog.
|
|
675
|
+
*
|
|
676
|
+
* A built-in directory of official diffwiki plugins that users can install.
|
|
677
|
+
* This is discovery only -- installing goes through `installPlugin(spec)`.
|
|
678
|
+
*
|
|
679
|
+
* @module
|
|
680
|
+
*/
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* A plugin diffwiki knows about and can recommend — the "app store" catalog.
|
|
684
|
+
* Discovery only; installing still goes through `installPlugin(spec)`.
|
|
685
|
+
*/
|
|
686
|
+
interface KnownPlugin {
|
|
687
|
+
name: string;
|
|
688
|
+
kind: PluginKind;
|
|
689
|
+
/** The argument for `diffwiki plugin install <spec>` (the npm package name). */
|
|
690
|
+
spec: string;
|
|
691
|
+
description: string;
|
|
692
|
+
/** External tool the plugin drives, shown as a prerequisite hint. */
|
|
693
|
+
requires?: string;
|
|
694
|
+
}
|
|
695
|
+
/** Return a copy of the known/official plugins available to install. */
|
|
696
|
+
declare function listAvailablePlugins(): KnownPlugin[];
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Plugin registry persistence.
|
|
700
|
+
*
|
|
701
|
+
* Manages the `~/.diffwiki/plugins.json` file that stores plugin records.
|
|
702
|
+
* Each record includes the plugin's name, kind, command argv, version,
|
|
703
|
+
* cached capabilities (populated at install/register time via `describe`),
|
|
704
|
+
* and enabled/managed flags.
|
|
705
|
+
*
|
|
706
|
+
* @module
|
|
707
|
+
*/
|
|
708
|
+
|
|
709
|
+
/** Read the plugin registry from disk. Returns an empty registry if the file does not exist. */
|
|
710
|
+
declare function readPluginRegistry(): Promise<PluginRegistryFile>;
|
|
711
|
+
/** Write the plugin registry to disk, creating the home directory if needed. */
|
|
712
|
+
declare function writePluginRegistry(registry: PluginRegistryFile): Promise<void>;
|
|
713
|
+
/** Insert or replace the plugin record with the same name. */
|
|
714
|
+
declare function upsertPlugin(record: PluginRecord): Promise<void>;
|
|
715
|
+
/** Remove a plugin record by name. */
|
|
716
|
+
declare function deregisterPlugin(name: string): Promise<void>;
|
|
717
|
+
/** List all enabled plugins of a given kind. */
|
|
718
|
+
declare function listEnabledPlugins(kind: PluginKind): Promise<PluginRecord[]>;
|
|
719
|
+
/** Find a single enabled plugin by kind and optionally by name. Returns the first if name is omitted. */
|
|
720
|
+
declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<PluginRecord | undefined>;
|
|
721
|
+
/** Find a plugin record by name (regardless of enabled state). */
|
|
722
|
+
declare function findPluginRecord(name: string): Promise<PluginRecord | undefined>;
|
|
723
|
+
|
|
724
|
+
export { type AddExternalOptions, type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, CollectionAlreadyAddedError, type CollectionEntry, CollectionExistsError, type CollectionGitStatus, type CollectionInfo, CollectionNotFoundError, type CollectionTree, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type DiffwikiLogLevel, type DocData, type InitOptions, type InstallOutcome, InvalidTargetError, type KnownPlugin, type ListingEntry, NATIVE_ENGINE, NoDefaultCollectionError, type NodeMeta, type PageData, 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, SearchDoc, type SearchMode, type SearchOutcome, type SearchProvider, type SerializableArticle, type SetupOutcome, type SetupStep, type TocItem, type TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildNavTree, buildSearchIndex, collectionDir, collectionGitStatus, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, isLinkedWorktree, listArticleTree, listAvailablePlugins, 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, repoId, repoRemoteName, resolveCwdCollection, resolveDefaultSearchMode, resolveHome, resolveLogLevel, resolvePage, search, serializeArticle, setConfigValue, setTags, slugify, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
|