@webspire/mcp 0.9.0 → 0.10.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/README.md +66 -53
- package/css/webspire-components.css +48 -0
- package/data/registry.json +3982 -298
- package/dist/registry.d.ts +9 -3
- package/dist/registry.js +67 -24
- package/package.json +1 -1
package/dist/registry.d.ts
CHANGED
|
@@ -7,9 +7,15 @@ export interface RegistryOptions {
|
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Load the registry with fallback chain:
|
|
10
|
-
* 1. Explicit --registry-file path
|
|
11
|
-
* 2.
|
|
12
|
-
* 3. Remote fetch from
|
|
10
|
+
* 1. Explicit --registry-file path (dev / CI override)
|
|
11
|
+
* 2. In-memory cache (5-min TTL, cleared when MCP process restarts)
|
|
12
|
+
* 3. Remote fetch from webspire.de — if newer than disk cache, writes to disk
|
|
13
|
+
* 4. Disk cache (~/.cache/webspire/registry.json) — persists across restarts,
|
|
14
|
+
* always holds the last successfully fetched remote version
|
|
15
|
+
* 5. Bundled data/registry.json — frozen at npm publish time, last resort
|
|
16
|
+
*
|
|
17
|
+
* The date comparison (registry.generated) ensures the disk cache is only
|
|
18
|
+
* overwritten when the remote actually has newer content.
|
|
13
19
|
*/
|
|
14
20
|
export declare function loadRegistry(options?: RegistryOptions): Promise<Registry>;
|
|
15
21
|
/**
|
package/dist/registry.js
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
3
4
|
import { dirname, resolve } from 'node:path';
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { searchSnippets as searchEntries } from './search.js';
|
|
6
7
|
const DEFAULT_REGISTRY_URL = 'https://webspire.de/registry.json';
|
|
7
8
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Resolve the path to the bundled registry.json shipped with this package.
|
|
11
|
-
* Works both from src/ (development) and dist/ (published).
|
|
12
|
-
*/
|
|
9
|
+
const DISK_CACHE_PATH = resolve(homedir(), '.cache', 'webspire', 'registry.json');
|
|
10
|
+
let memCache = null;
|
|
13
11
|
function getBundledRegistryPath() {
|
|
14
12
|
try {
|
|
15
13
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
// From dist/ → ../data/registry.json, from src/ → ../data/registry.json
|
|
17
14
|
const candidate = resolve(thisDir, '..', 'data', 'registry.json');
|
|
18
15
|
return existsSync(candidate) ? candidate : undefined;
|
|
19
16
|
}
|
|
@@ -21,11 +18,39 @@ function getBundledRegistryPath() {
|
|
|
21
18
|
return undefined;
|
|
22
19
|
}
|
|
23
20
|
}
|
|
21
|
+
async function readDiskCache() {
|
|
22
|
+
try {
|
|
23
|
+
const raw = await readFile(DISK_CACHE_PATH, 'utf-8');
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function writeDiskCache(registry) {
|
|
31
|
+
try {
|
|
32
|
+
await mkdir(dirname(DISK_CACHE_PATH), { recursive: true });
|
|
33
|
+
await writeFile(DISK_CACHE_PATH, JSON.stringify(registry), 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Best-effort — disk write failures are non-fatal
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Returns true if registry a was generated after registry b. */
|
|
40
|
+
function isNewer(a, b) {
|
|
41
|
+
return new Date(a.generated ?? 0).getTime() > new Date(b.generated ?? 0).getTime();
|
|
42
|
+
}
|
|
24
43
|
/**
|
|
25
44
|
* Load the registry with fallback chain:
|
|
26
|
-
* 1. Explicit --registry-file path
|
|
27
|
-
* 2.
|
|
28
|
-
* 3. Remote fetch from
|
|
45
|
+
* 1. Explicit --registry-file path (dev / CI override)
|
|
46
|
+
* 2. In-memory cache (5-min TTL, cleared when MCP process restarts)
|
|
47
|
+
* 3. Remote fetch from webspire.de — if newer than disk cache, writes to disk
|
|
48
|
+
* 4. Disk cache (~/.cache/webspire/registry.json) — persists across restarts,
|
|
49
|
+
* always holds the last successfully fetched remote version
|
|
50
|
+
* 5. Bundled data/registry.json — frozen at npm publish time, last resort
|
|
51
|
+
*
|
|
52
|
+
* The date comparison (registry.generated) ensures the disk cache is only
|
|
53
|
+
* overwritten when the remote actually has newer content.
|
|
29
54
|
*/
|
|
30
55
|
export async function loadRegistry(options) {
|
|
31
56
|
// 1. Explicit file path
|
|
@@ -33,24 +58,42 @@ export async function loadRegistry(options) {
|
|
|
33
58
|
const raw = await readFile(options.filePath, 'utf-8');
|
|
34
59
|
return JSON.parse(raw);
|
|
35
60
|
}
|
|
36
|
-
// 2.
|
|
61
|
+
// 2. In-memory cache (avoids re-fetching within the same session)
|
|
62
|
+
if (memCache && Date.now() - memCache.loadedAt < CACHE_TTL_MS) {
|
|
63
|
+
return memCache.registry;
|
|
64
|
+
}
|
|
65
|
+
// 3. Remote fetch — compare with disk cache, write if newer
|
|
66
|
+
try {
|
|
67
|
+
const url = options?.url ?? DEFAULT_REGISTRY_URL;
|
|
68
|
+
const response = await fetch(url);
|
|
69
|
+
if (response.ok) {
|
|
70
|
+
const remote = (await response.json());
|
|
71
|
+
memCache = { registry: remote, loadedAt: Date.now() };
|
|
72
|
+
// Write to disk only when remote is actually newer than what's cached
|
|
73
|
+
const disk = await readDiskCache();
|
|
74
|
+
if (!disk || isNewer(remote, disk)) {
|
|
75
|
+
await writeDiskCache(remote);
|
|
76
|
+
}
|
|
77
|
+
return remote;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Network unavailable — fall through to disk cache
|
|
82
|
+
}
|
|
83
|
+
// 4. Disk cache — last successfully fetched version, survives process restarts
|
|
84
|
+
const disk = await readDiskCache();
|
|
85
|
+
if (disk) {
|
|
86
|
+
memCache = { registry: disk, loadedAt: Date.now() };
|
|
87
|
+
return disk;
|
|
88
|
+
}
|
|
89
|
+
// 5. Bundled registry — frozen at npm publish time
|
|
37
90
|
const bundledPath = getBundledRegistryPath();
|
|
38
|
-
if (bundledPath
|
|
91
|
+
if (bundledPath) {
|
|
39
92
|
const raw = await readFile(bundledPath, 'utf-8');
|
|
40
93
|
return JSON.parse(raw);
|
|
41
94
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return cache.registry;
|
|
45
|
-
}
|
|
46
|
-
const url = options?.url ?? DEFAULT_REGISTRY_URL;
|
|
47
|
-
const response = await fetch(url);
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
throw new Error(`Failed to fetch registry from ${url}: ${response.statusText}`);
|
|
50
|
-
}
|
|
51
|
-
const registry = (await response.json());
|
|
52
|
-
cache = { registry, loadedAt: Date.now() };
|
|
53
|
-
return registry;
|
|
95
|
+
throw new Error('Registry unavailable: network unreachable and no local cache found. ' +
|
|
96
|
+
'Try reinstalling @webspire/mcp or check your internet connection.');
|
|
54
97
|
}
|
|
55
98
|
/**
|
|
56
99
|
* Load the bundled fonts.json shipped with this package.
|
package/package.json
CHANGED