swift-patterns-mcp 1.0.2
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/LICENSE +21 -0
- package/README.md +377 -0
- package/build/cli/auth.d.ts +3 -0
- package/build/cli/auth.d.ts.map +1 -0
- package/build/cli/auth.js +43 -0
- package/build/cli/auth.js.map +1 -0
- package/build/cli/setup.d.ts +2 -0
- package/build/cli/setup.d.ts.map +1 -0
- package/build/cli/setup.js +149 -0
- package/build/cli/setup.js.map +1 -0
- package/build/cli/source-manager.d.ts +3 -0
- package/build/cli/source-manager.d.ts.map +1 -0
- package/build/cli/source-manager.js +117 -0
- package/build/cli/source-manager.js.map +1 -0
- package/build/config/creators.d.ts +11 -0
- package/build/config/creators.d.ts.map +1 -0
- package/build/config/creators.js +32 -0
- package/build/config/creators.js.map +1 -0
- package/build/config/sources.d.ts +65 -0
- package/build/config/sources.d.ts.map +1 -0
- package/build/config/sources.js +184 -0
- package/build/config/sources.js.map +1 -0
- package/build/config/sources.test.d.ts +2 -0
- package/build/config/sources.test.d.ts.map +1 -0
- package/build/config/sources.test.js +195 -0
- package/build/config/sources.test.js.map +1 -0
- package/build/config/swift-keywords.d.ts +21 -0
- package/build/config/swift-keywords.d.ts.map +1 -0
- package/build/config/swift-keywords.js +67 -0
- package/build/config/swift-keywords.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +295 -0
- package/build/index.js.map +1 -0
- package/build/integration/mcp-client.test.d.ts +2 -0
- package/build/integration/mcp-client.test.d.ts.map +1 -0
- package/build/integration/mcp-client.test.js +82 -0
- package/build/integration/mcp-client.test.js.map +1 -0
- package/build/integration/response-quality.test.d.ts +2 -0
- package/build/integration/response-quality.test.d.ts.map +1 -0
- package/build/integration/response-quality.test.js +139 -0
- package/build/integration/response-quality.test.js.map +1 -0
- package/build/integration/test-client.d.ts +25 -0
- package/build/integration/test-client.d.ts.map +1 -0
- package/build/integration/test-client.js +93 -0
- package/build/integration/test-client.js.map +1 -0
- package/build/sources/free/rssPatternSource.d.ts +41 -0
- package/build/sources/free/rssPatternSource.d.ts.map +1 -0
- package/build/sources/free/rssPatternSource.js +122 -0
- package/build/sources/free/rssPatternSource.js.map +1 -0
- package/build/sources/free/rssPatternSource.test.d.ts +2 -0
- package/build/sources/free/rssPatternSource.test.d.ts.map +1 -0
- package/build/sources/free/rssPatternSource.test.js +92 -0
- package/build/sources/free/rssPatternSource.test.js.map +1 -0
- package/build/sources/free/sundell.d.ts +9 -0
- package/build/sources/free/sundell.d.ts.map +1 -0
- package/build/sources/free/sundell.js +31 -0
- package/build/sources/free/sundell.js.map +1 -0
- package/build/sources/free/sundell.test.d.ts +2 -0
- package/build/sources/free/sundell.test.d.ts.map +1 -0
- package/build/sources/free/sundell.test.js +63 -0
- package/build/sources/free/sundell.test.js.map +1 -0
- package/build/sources/free/vanderlee.d.ts +9 -0
- package/build/sources/free/vanderlee.d.ts.map +1 -0
- package/build/sources/free/vanderlee.js +71 -0
- package/build/sources/free/vanderlee.js.map +1 -0
- package/build/sources/free/vanderlee.test.d.ts +2 -0
- package/build/sources/free/vanderlee.test.d.ts.map +1 -0
- package/build/sources/free/vanderlee.test.js +73 -0
- package/build/sources/free/vanderlee.test.js.map +1 -0
- package/build/sources/premium/patreon-dl.d.ts +45 -0
- package/build/sources/premium/patreon-dl.d.ts.map +1 -0
- package/build/sources/premium/patreon-dl.js +189 -0
- package/build/sources/premium/patreon-dl.js.map +1 -0
- package/build/sources/premium/patreon-fetch.d.ts +3 -0
- package/build/sources/premium/patreon-fetch.d.ts.map +1 -0
- package/build/sources/premium/patreon-fetch.js +18 -0
- package/build/sources/premium/patreon-fetch.js.map +1 -0
- package/build/sources/premium/patreon-oauth.d.ts +24 -0
- package/build/sources/premium/patreon-oauth.d.ts.map +1 -0
- package/build/sources/premium/patreon-oauth.js +207 -0
- package/build/sources/premium/patreon-oauth.js.map +1 -0
- package/build/sources/premium/patreon-zip.d.ts +17 -0
- package/build/sources/premium/patreon-zip.d.ts.map +1 -0
- package/build/sources/premium/patreon-zip.js +128 -0
- package/build/sources/premium/patreon-zip.js.map +1 -0
- package/build/sources/premium/patreon.d.ts +48 -0
- package/build/sources/premium/patreon.d.ts.map +1 -0
- package/build/sources/premium/patreon.js +248 -0
- package/build/sources/premium/patreon.js.map +1 -0
- package/build/sources/premium/youtube.d.ts +14 -0
- package/build/sources/premium/youtube.d.ts.map +1 -0
- package/build/sources/premium/youtube.js +92 -0
- package/build/sources/premium/youtube.js.map +1 -0
- package/build/tools/extract-cookie.d.ts +2 -0
- package/build/tools/extract-cookie.d.ts.map +1 -0
- package/build/tools/extract-cookie.js +40 -0
- package/build/tools/extract-cookie.js.map +1 -0
- package/build/tools/handlers/enableSource.d.ts +3 -0
- package/build/tools/handlers/enableSource.d.ts.map +1 -0
- package/build/tools/handlers/enableSource.js +39 -0
- package/build/tools/handlers/enableSource.js.map +1 -0
- package/build/tools/handlers/getSwiftPattern.d.ts +3 -0
- package/build/tools/handlers/getSwiftPattern.d.ts.map +1 -0
- package/build/tools/handlers/getSwiftPattern.js +78 -0
- package/build/tools/handlers/getSwiftPattern.js.map +1 -0
- package/build/tools/handlers/handlers.test.d.ts +2 -0
- package/build/tools/handlers/handlers.test.d.ts.map +1 -0
- package/build/tools/handlers/handlers.test.js +189 -0
- package/build/tools/handlers/handlers.test.js.map +1 -0
- package/build/tools/handlers/listContentSources.d.ts +3 -0
- package/build/tools/handlers/listContentSources.d.ts.map +1 -0
- package/build/tools/handlers/listContentSources.js +38 -0
- package/build/tools/handlers/listContentSources.js.map +1 -0
- package/build/tools/handlers/searchSwiftContent.d.ts +3 -0
- package/build/tools/handlers/searchSwiftContent.d.ts.map +1 -0
- package/build/tools/handlers/searchSwiftContent.js +44 -0
- package/build/tools/handlers/searchSwiftContent.js.map +1 -0
- package/build/tools/index.d.ts +3 -0
- package/build/tools/index.d.ts.map +1 -0
- package/build/tools/index.js +14 -0
- package/build/tools/index.js.map +1 -0
- package/build/tools/registry.d.ts +14 -0
- package/build/tools/registry.d.ts.map +1 -0
- package/build/tools/registry.js +21 -0
- package/build/tools/registry.js.map +1 -0
- package/build/tools/registry.test.d.ts +2 -0
- package/build/tools/registry.test.d.ts.map +1 -0
- package/build/tools/registry.test.js +54 -0
- package/build/tools/registry.test.js.map +1 -0
- package/build/tools/types.d.ts +25 -0
- package/build/tools/types.d.ts.map +1 -0
- package/build/tools/types.js +3 -0
- package/build/tools/types.js.map +1 -0
- package/build/utils/cache.d.ts +17 -0
- package/build/utils/cache.d.ts.map +1 -0
- package/build/utils/cache.js +144 -0
- package/build/utils/cache.js.map +1 -0
- package/build/utils/errors.d.ts +19 -0
- package/build/utils/errors.d.ts.map +1 -0
- package/build/utils/errors.js +34 -0
- package/build/utils/errors.js.map +1 -0
- package/build/utils/paths.d.ts +27 -0
- package/build/utils/paths.d.ts.map +1 -0
- package/build/utils/paths.js +43 -0
- package/build/utils/paths.js.map +1 -0
- package/build/utils/search.d.ts +29 -0
- package/build/utils/search.d.ts.map +1 -0
- package/build/utils/search.js +165 -0
- package/build/utils/search.js.map +1 -0
- package/build/utils/search.test.d.ts +2 -0
- package/build/utils/search.test.d.ts.map +1 -0
- package/build/utils/search.test.js +199 -0
- package/build/utils/search.test.js.map +1 -0
- package/build/utils/swift-analysis.d.ts +27 -0
- package/build/utils/swift-analysis.d.ts.map +1 -0
- package/build/utils/swift-analysis.js +78 -0
- package/build/utils/swift-analysis.js.map +1 -0
- package/build/utils/swift-analysis.test.d.ts +2 -0
- package/build/utils/swift-analysis.test.d.ts.map +1 -0
- package/build/utils/swift-analysis.test.js +215 -0
- package/build/utils/swift-analysis.test.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// src/utils/cache.ts
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { createHash } from 'crypto';
|
|
5
|
+
import { getCacheDir } from './paths.js';
|
|
6
|
+
const DEFAULT_TTL = 86400; // 24 hours in seconds
|
|
7
|
+
export class FileCache {
|
|
8
|
+
cacheDir;
|
|
9
|
+
memoryCache = new Map();
|
|
10
|
+
constructor(namespace = 'default') {
|
|
11
|
+
this.cacheDir = getCacheDir(namespace);
|
|
12
|
+
this.ensureCacheDir();
|
|
13
|
+
}
|
|
14
|
+
ensureCacheDir() {
|
|
15
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
16
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
getCacheKey(key) {
|
|
20
|
+
// Hash long keys to avoid filesystem issues
|
|
21
|
+
if (key.length > 100) {
|
|
22
|
+
return createHash('md5').update(key).digest('hex');
|
|
23
|
+
}
|
|
24
|
+
// Sanitize key for filesystem
|
|
25
|
+
return key.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
26
|
+
}
|
|
27
|
+
getCachePath(key) {
|
|
28
|
+
return path.join(this.cacheDir, `${this.getCacheKey(key)}.json`);
|
|
29
|
+
}
|
|
30
|
+
async get(key) {
|
|
31
|
+
// Check memory cache first
|
|
32
|
+
const memEntry = this.memoryCache.get(key);
|
|
33
|
+
if (memEntry && !this.isExpired(memEntry)) {
|
|
34
|
+
return memEntry.data;
|
|
35
|
+
}
|
|
36
|
+
// Check file cache
|
|
37
|
+
const cachePath = this.getCachePath(key);
|
|
38
|
+
try {
|
|
39
|
+
if (fs.existsSync(cachePath)) {
|
|
40
|
+
const content = fs.readFileSync(cachePath, 'utf-8');
|
|
41
|
+
const entry = JSON.parse(content);
|
|
42
|
+
if (!this.isExpired(entry)) {
|
|
43
|
+
// Populate memory cache
|
|
44
|
+
this.memoryCache.set(key, entry);
|
|
45
|
+
return entry.data;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Clean up expired entry
|
|
49
|
+
fs.unlinkSync(cachePath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Cache read failed, return null
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
async set(key, data, ttl = DEFAULT_TTL) {
|
|
59
|
+
const entry = {
|
|
60
|
+
data,
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
ttl,
|
|
63
|
+
};
|
|
64
|
+
// Set in memory cache
|
|
65
|
+
this.memoryCache.set(key, entry);
|
|
66
|
+
// Set in file cache
|
|
67
|
+
const cachePath = this.getCachePath(key);
|
|
68
|
+
try {
|
|
69
|
+
fs.writeFileSync(cachePath, JSON.stringify(entry));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Cache write failed, continue without caching
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async getOrFetch(key, fetcher, ttl = DEFAULT_TTL) {
|
|
76
|
+
const cached = await this.get(key);
|
|
77
|
+
if (cached !== null) {
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
const data = await fetcher();
|
|
81
|
+
await this.set(key, data, ttl);
|
|
82
|
+
return data;
|
|
83
|
+
}
|
|
84
|
+
isExpired(entry) {
|
|
85
|
+
const age = (Date.now() - entry.timestamp) / 1000; // age in seconds
|
|
86
|
+
return age > entry.ttl;
|
|
87
|
+
}
|
|
88
|
+
clear() {
|
|
89
|
+
// Clear memory cache
|
|
90
|
+
this.memoryCache.clear();
|
|
91
|
+
// Clear file cache
|
|
92
|
+
try {
|
|
93
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
94
|
+
const files = fs.readdirSync(this.cacheDir);
|
|
95
|
+
for (const file of files) {
|
|
96
|
+
fs.unlinkSync(path.join(this.cacheDir, file));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore errors during clear
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
clearExpired() {
|
|
105
|
+
let cleared = 0;
|
|
106
|
+
// Clear expired from memory
|
|
107
|
+
for (const [key, entry] of this.memoryCache.entries()) {
|
|
108
|
+
if (this.isExpired(entry)) {
|
|
109
|
+
this.memoryCache.delete(key);
|
|
110
|
+
cleared++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Clear expired from files
|
|
114
|
+
try {
|
|
115
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
116
|
+
const files = fs.readdirSync(this.cacheDir);
|
|
117
|
+
for (const file of files) {
|
|
118
|
+
const filePath = path.join(this.cacheDir, file);
|
|
119
|
+
try {
|
|
120
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
121
|
+
const entry = JSON.parse(content);
|
|
122
|
+
if (this.isExpired(entry)) {
|
|
123
|
+
fs.unlinkSync(filePath);
|
|
124
|
+
cleared++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Remove corrupted cache files
|
|
129
|
+
fs.unlinkSync(filePath);
|
|
130
|
+
cleared++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Ignore errors during cleanup
|
|
137
|
+
}
|
|
138
|
+
return cleared;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Shared cache instances
|
|
142
|
+
export const rssCache = new FileCache('rss');
|
|
143
|
+
export const articleCache = new FileCache('articles');
|
|
144
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for consistent error logging across the codebase.
|
|
3
|
+
* Uses console.error with structured context - no external dependencies.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a value is an Error instance.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isError(value: unknown): value is Error;
|
|
9
|
+
/**
|
|
10
|
+
* Safely extracts an error message from any thrown value.
|
|
11
|
+
* Returns error.message if Error, String(error) otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export declare function toErrorMessage(error: unknown): string;
|
|
14
|
+
/**
|
|
15
|
+
* Logs an error with context prefix and optional details.
|
|
16
|
+
* Format: [context] message { details }
|
|
17
|
+
*/
|
|
18
|
+
export declare function logError(context: string, error: unknown, details?: Record<string, unknown>): void;
|
|
19
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAEtD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKrD;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAON"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for consistent error logging across the codebase.
|
|
3
|
+
* Uses console.error with structured context - no external dependencies.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a value is an Error instance.
|
|
7
|
+
*/
|
|
8
|
+
export function isError(value) {
|
|
9
|
+
return value instanceof Error;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Safely extracts an error message from any thrown value.
|
|
13
|
+
* Returns error.message if Error, String(error) otherwise.
|
|
14
|
+
*/
|
|
15
|
+
export function toErrorMessage(error) {
|
|
16
|
+
if (isError(error)) {
|
|
17
|
+
return error.message;
|
|
18
|
+
}
|
|
19
|
+
return String(error);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Logs an error with context prefix and optional details.
|
|
23
|
+
* Format: [context] message { details }
|
|
24
|
+
*/
|
|
25
|
+
export function logError(context, error, details) {
|
|
26
|
+
const message = toErrorMessage(error);
|
|
27
|
+
if (details) {
|
|
28
|
+
console.error(`[${context}]`, message, details);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.error(`[${context}]`, message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,KAAK,YAAY,KAAK,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,OAAe,EACf,KAAc,EACd,OAAiC;IAEjC,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the base directory for swift-mcp configuration and data
|
|
3
|
+
* @returns The absolute path to ~/.swift-mcp directory
|
|
4
|
+
*/
|
|
5
|
+
export declare function getSwiftMcpDir(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get the configuration file path
|
|
8
|
+
* @returns The absolute path to the config.json file
|
|
9
|
+
*/
|
|
10
|
+
export declare function getConfigPath(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Get the cache directory path
|
|
13
|
+
* @param namespace Optional namespace for cache isolation
|
|
14
|
+
* @returns The absolute path to the cache directory
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCacheDir(namespace?: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Get the Patreon content download directory
|
|
19
|
+
* @returns The absolute path to the patreon-content directory
|
|
20
|
+
*/
|
|
21
|
+
export declare function getPatreonContentDir(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Get the Patreon creators configuration path
|
|
24
|
+
* @returns The absolute path to the patreon-creators.json file
|
|
25
|
+
*/
|
|
26
|
+
export declare function getPatreonCreatorsPath(): string;
|
|
27
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/utils/paths.ts
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Get the base directory for swift-mcp configuration and data
|
|
5
|
+
* @returns The absolute path to ~/.swift-mcp directory
|
|
6
|
+
*/
|
|
7
|
+
export function getSwiftMcpDir() {
|
|
8
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
9
|
+
return path.join(home, '.swift-mcp');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get the configuration file path
|
|
13
|
+
* @returns The absolute path to the config.json file
|
|
14
|
+
*/
|
|
15
|
+
export function getConfigPath() {
|
|
16
|
+
return path.join(getSwiftMcpDir(), 'config.json');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the cache directory path
|
|
20
|
+
* @param namespace Optional namespace for cache isolation
|
|
21
|
+
* @returns The absolute path to the cache directory
|
|
22
|
+
*/
|
|
23
|
+
export function getCacheDir(namespace) {
|
|
24
|
+
if (namespace) {
|
|
25
|
+
return path.join(getSwiftMcpDir(), 'cache', namespace);
|
|
26
|
+
}
|
|
27
|
+
return path.join(getSwiftMcpDir(), 'cache');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the Patreon content download directory
|
|
31
|
+
* @returns The absolute path to the patreon-content directory
|
|
32
|
+
*/
|
|
33
|
+
export function getPatreonContentDir() {
|
|
34
|
+
return path.join(getSwiftMcpDir(), 'patreon-content');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the Patreon creators configuration path
|
|
38
|
+
* @returns The absolute path to the patreon-creators.json file
|
|
39
|
+
*/
|
|
40
|
+
export function getPatreonCreatorsPath() {
|
|
41
|
+
return path.join(getSwiftMcpDir(), 'patreon-creators.json');
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,qBAAqB;AAErB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,SAAkB;IAC5C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,iBAAiB,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,uBAAuB,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface SearchableDocument {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
content: string;
|
|
5
|
+
topics: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface SearchResult<T> {
|
|
8
|
+
item: T;
|
|
9
|
+
score: number;
|
|
10
|
+
matches: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface SearchOptions {
|
|
13
|
+
fuzzy?: number;
|
|
14
|
+
boost?: Record<string, number>;
|
|
15
|
+
minScore?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class SearchIndex<T extends SearchableDocument> {
|
|
18
|
+
private miniSearch;
|
|
19
|
+
private documents;
|
|
20
|
+
constructor(fields?: string[]);
|
|
21
|
+
addDocuments(docs: T[]): void;
|
|
22
|
+
search(query: string, options?: SearchOptions): SearchResult<T>[];
|
|
23
|
+
private findMatches;
|
|
24
|
+
}
|
|
25
|
+
export declare function fuzzySearch<T extends SearchableDocument>(documents: T[], query: string, options?: SearchOptions): SearchResult<T>[];
|
|
26
|
+
export declare function combineScores(searchScore: number, staticRelevance: number, searchWeight?: number): number;
|
|
27
|
+
export declare function suggestSimilar(query: string, knownTerms: string[], maxSuggestions?: number): string[];
|
|
28
|
+
export default SearchIndex;
|
|
29
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/utils/search.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2CD,qBAAa,WAAW,CAAC,CAAC,SAAS,kBAAkB;IACnD,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,SAAS,CAA6B;gBAElC,MAAM,GAAE,MAAM,EAAmC;IAc7D,YAAY,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI;IAmB7B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE;IAqCrE,OAAO,CAAC,WAAW;CAapB;AAGD,wBAAgB,WAAW,CAAC,CAAC,SAAS,kBAAkB,EACtD,SAAS,EAAE,CAAC,EAAE,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAAC,CAAC,CAAC,EAAE,CAInB;AAGD,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,YAAY,GAAE,MAAY,GACzB,MAAM,CASR;AAGD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,GAAE,MAAU,GACzB,MAAM,EAAE,CAeV;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/utils/search.ts
|
|
2
|
+
// Advanced search utilities with fuzzy matching and smart stemming
|
|
3
|
+
import MiniSearch from 'minisearch';
|
|
4
|
+
import natural from 'natural';
|
|
5
|
+
// Porter Stemmer for English
|
|
6
|
+
const stemmer = natural.PorterStemmer;
|
|
7
|
+
// Common stopwords to filter out
|
|
8
|
+
const STOPWORDS = new Set([
|
|
9
|
+
'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
|
|
10
|
+
'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'been',
|
|
11
|
+
'be', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
|
|
12
|
+
'should', 'may', 'might', 'must', 'shall', 'can', 'this', 'that', 'these',
|
|
13
|
+
'those', 'it', 'its', 'they', 'them', 'their', 'we', 'our', 'you', 'your',
|
|
14
|
+
'i', 'my', 'me', 'he', 'she', 'him', 'her', 'his', 'who', 'what', 'which',
|
|
15
|
+
'when', 'where', 'why', 'how', 'all', 'each', 'every', 'both', 'few',
|
|
16
|
+
'more', 'most', 'other', 'some', 'such', 'no', 'not', 'only', 'same',
|
|
17
|
+
'so', 'than', 'too', 'very', 'just', 'also', 'now', 'here', 'there'
|
|
18
|
+
]);
|
|
19
|
+
// Swift-specific terms that shouldn't be stemmed (preserve technical accuracy)
|
|
20
|
+
// NOTE: If you add hyphenated terms here (e.g. "objective-c"), the tokenizer will now respect them.
|
|
21
|
+
const PRESERVE_TERMS = new Set([
|
|
22
|
+
'swift', 'swiftui', 'uikit', 'combine', 'async', 'await', 'actor',
|
|
23
|
+
'struct', 'class', 'enum', 'protocol', 'extension', 'func', 'var', 'let',
|
|
24
|
+
'mvvm', 'viper', 'mvc', 'tca', 'xctest', 'xcode', 'ios', 'macos',
|
|
25
|
+
'watchos', 'tvos', 'ipados', 'appkit', 'foundation', 'coredata',
|
|
26
|
+
'cloudkit', 'urlsession', 'codable', 'observable', 'published',
|
|
27
|
+
'stateobject', 'observedobject', 'environmentobject', 'binding', 'state'
|
|
28
|
+
]);
|
|
29
|
+
// Custom tokenizer with smart hyphen handling
|
|
30
|
+
function tokenize(text) {
|
|
31
|
+
// 1. Clean text but keep hyphens temporarily
|
|
32
|
+
const rawTokens = text
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^\w\s-]/g, ' ')
|
|
35
|
+
.split(/\s+/)
|
|
36
|
+
.filter(t => t.length > 0);
|
|
37
|
+
const finalTokens = [];
|
|
38
|
+
for (const token of rawTokens) {
|
|
39
|
+
// 2. Check if the full token is a preserved term (e.g., if you added "objective-c")
|
|
40
|
+
if (PRESERVE_TERMS.has(token)) {
|
|
41
|
+
finalTokens.push(token);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// 3. If not preserved, split on hyphens to separate words like "async-await" -> "async", "await"
|
|
45
|
+
const subTokens = token.split('-');
|
|
46
|
+
for (const sub of subTokens) {
|
|
47
|
+
if (sub.length <= 1 || STOPWORDS.has(sub))
|
|
48
|
+
continue;
|
|
49
|
+
// 4. Check sub-tokens against preserved terms or stem them
|
|
50
|
+
if (PRESERVE_TERMS.has(sub)) {
|
|
51
|
+
finalTokens.push(sub);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
finalTokens.push(stemmer.stem(sub));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return finalTokens;
|
|
59
|
+
}
|
|
60
|
+
// Process query with same tokenization for consistent matching
|
|
61
|
+
function processQuery(query) {
|
|
62
|
+
return tokenize(query);
|
|
63
|
+
}
|
|
64
|
+
export class SearchIndex {
|
|
65
|
+
miniSearch;
|
|
66
|
+
documents = new Map();
|
|
67
|
+
constructor(fields = ['title', 'content', 'topics']) {
|
|
68
|
+
this.miniSearch = new MiniSearch({
|
|
69
|
+
fields,
|
|
70
|
+
storeFields: ['id'],
|
|
71
|
+
tokenize, // Uses our new smart tokenizer
|
|
72
|
+
processTerm: (term) => term, // Already processed by tokenize
|
|
73
|
+
searchOptions: {
|
|
74
|
+
boost: { title: 2, topics: 1.5, content: 1 },
|
|
75
|
+
fuzzy: 0.2,
|
|
76
|
+
prefix: true,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
addDocuments(docs) {
|
|
81
|
+
// Clear existing documents
|
|
82
|
+
this.miniSearch.removeAll();
|
|
83
|
+
this.documents.clear();
|
|
84
|
+
// Preprocess documents for indexing
|
|
85
|
+
const processedDocs = docs.map(doc => ({
|
|
86
|
+
...doc,
|
|
87
|
+
// Join topics array for indexing
|
|
88
|
+
topics: Array.isArray(doc.topics) ? doc.topics.join(' ') : doc.topics,
|
|
89
|
+
}));
|
|
90
|
+
// Add to MiniSearch
|
|
91
|
+
this.miniSearch.addAll(processedDocs);
|
|
92
|
+
// Store original documents for retrieval
|
|
93
|
+
docs.forEach(doc => this.documents.set(doc.id, doc));
|
|
94
|
+
}
|
|
95
|
+
search(query, options = {}) {
|
|
96
|
+
const { fuzzy = 0.2, boost = { title: 2, topics: 1.5, content: 1 }, minScore = 0, } = options;
|
|
97
|
+
// Get stemmed query terms for match highlighting
|
|
98
|
+
const queryTerms = processQuery(query);
|
|
99
|
+
// Search with MiniSearch
|
|
100
|
+
const results = this.miniSearch.search(query, {
|
|
101
|
+
fuzzy,
|
|
102
|
+
prefix: true,
|
|
103
|
+
boost,
|
|
104
|
+
combineWith: 'OR',
|
|
105
|
+
});
|
|
106
|
+
// Map results to original documents with scores
|
|
107
|
+
return results
|
|
108
|
+
.filter(result => result.score >= minScore)
|
|
109
|
+
.map(result => {
|
|
110
|
+
const doc = this.documents.get(result.id);
|
|
111
|
+
if (!doc)
|
|
112
|
+
return null;
|
|
113
|
+
// Find which terms matched
|
|
114
|
+
const matches = this.findMatches(doc, queryTerms);
|
|
115
|
+
return {
|
|
116
|
+
item: doc,
|
|
117
|
+
score: result.score,
|
|
118
|
+
matches,
|
|
119
|
+
};
|
|
120
|
+
})
|
|
121
|
+
.filter((r) => r !== null);
|
|
122
|
+
}
|
|
123
|
+
findMatches(doc, queryTerms) {
|
|
124
|
+
const matches = [];
|
|
125
|
+
const searchText = `${doc.title} ${doc.content} ${doc.topics.join(' ')}`.toLowerCase();
|
|
126
|
+
for (const term of queryTerms) {
|
|
127
|
+
// Check both stemmed and original
|
|
128
|
+
if (searchText.includes(term)) {
|
|
129
|
+
matches.push(term);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return [...new Set(matches)];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Standalone search function for simple use cases
|
|
136
|
+
export function fuzzySearch(documents, query, options = {}) {
|
|
137
|
+
const index = new SearchIndex();
|
|
138
|
+
index.addDocuments(documents);
|
|
139
|
+
return index.search(query, options);
|
|
140
|
+
}
|
|
141
|
+
// Get search relevance score (combines MiniSearch score with static relevance)
|
|
142
|
+
export function combineScores(searchScore, staticRelevance, searchWeight = 0.6) {
|
|
143
|
+
// Normalize search score (MiniSearch scores can vary widely)
|
|
144
|
+
const normalizedSearch = Math.min(searchScore / 10, 1) * 100;
|
|
145
|
+
// Weighted combination
|
|
146
|
+
return Math.round(normalizedSearch * searchWeight +
|
|
147
|
+
staticRelevance * (1 - searchWeight));
|
|
148
|
+
}
|
|
149
|
+
// Suggest similar terms (for "did you mean?" functionality)
|
|
150
|
+
export function suggestSimilar(query, knownTerms, maxSuggestions = 3) {
|
|
151
|
+
const queryLower = query.toLowerCase();
|
|
152
|
+
// Calculate Levenshtein distance
|
|
153
|
+
const suggestions = knownTerms
|
|
154
|
+
.map(term => ({
|
|
155
|
+
term,
|
|
156
|
+
distance: natural.LevenshteinDistance(queryLower, term.toLowerCase()),
|
|
157
|
+
}))
|
|
158
|
+
.filter(({ distance }) => distance <= 3 && distance > 0)
|
|
159
|
+
.sort((a, b) => a.distance - b.distance)
|
|
160
|
+
.slice(0, maxSuggestions)
|
|
161
|
+
.map(({ term }) => term);
|
|
162
|
+
return suggestions;
|
|
163
|
+
}
|
|
164
|
+
export default SearchIndex;
|
|
165
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/utils/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../src/utils/search.test.ts"],"names":[],"mappings":""}
|