@j0hanz/superfetch 1.2.5 → 2.0.1
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 +131 -156
- package/dist/config/auth-config.d.ts +16 -0
- package/dist/config/auth-config.js +53 -0
- package/dist/config/constants.d.ts +11 -13
- package/dist/config/constants.js +1 -3
- package/dist/config/env-parsers.d.ts +7 -0
- package/dist/config/env-parsers.js +84 -0
- package/dist/config/formatting.d.ts +2 -2
- package/dist/config/index.d.ts +47 -53
- package/dist/config/index.js +35 -64
- package/dist/config/types/content.d.ts +1 -49
- package/dist/config/types/runtime.d.ts +8 -16
- package/dist/config/types/tools.d.ts +2 -28
- package/dist/http/accept-policy.d.ts +3 -0
- package/dist/http/accept-policy.js +45 -0
- package/dist/http/async-handler.d.ts +2 -0
- package/dist/http/async-handler.js +5 -0
- package/dist/http/auth-introspection.d.ts +2 -0
- package/dist/http/auth-introspection.js +141 -0
- package/dist/http/auth-static.d.ts +2 -0
- package/dist/http/auth-static.js +23 -0
- package/dist/http/auth.d.ts +3 -2
- package/dist/http/auth.js +254 -23
- package/dist/http/cors.d.ts +6 -6
- package/dist/http/cors.js +7 -42
- package/dist/http/download-routes.d.ts +0 -12
- package/dist/http/download-routes.js +21 -58
- package/dist/http/host-allowlist.d.ts +3 -0
- package/dist/http/host-allowlist.js +117 -0
- package/dist/http/jsonrpc-http.d.ts +2 -0
- package/dist/http/jsonrpc-http.js +10 -0
- package/dist/http/mcp-routes.d.ts +8 -3
- package/dist/http/mcp-routes.js +137 -31
- package/dist/http/mcp-session-eviction.d.ts +3 -0
- package/dist/http/mcp-session-eviction.js +24 -0
- package/dist/http/mcp-session-helpers.d.ts +0 -1
- package/dist/http/mcp-session-helpers.js +1 -1
- package/dist/http/mcp-session-init.d.ts +7 -0
- package/dist/http/mcp-session-init.js +94 -0
- package/dist/http/mcp-session-slots.d.ts +17 -0
- package/dist/http/mcp-session-slots.js +55 -0
- package/dist/http/mcp-session-transport-init.d.ts +7 -0
- package/dist/http/mcp-session-transport-init.js +41 -0
- package/dist/http/mcp-session-transport.d.ts +7 -0
- package/dist/http/mcp-session-transport.js +57 -0
- package/dist/http/mcp-session-types.d.ts +5 -0
- package/dist/http/mcp-session-types.js +1 -0
- package/dist/http/mcp-session.d.ts +9 -9
- package/dist/http/mcp-session.js +15 -137
- package/dist/http/mcp-sessions.d.ts +43 -0
- package/dist/http/mcp-sessions.js +392 -0
- package/dist/http/mcp-validation.d.ts +1 -0
- package/dist/http/mcp-validation.js +11 -10
- package/dist/http/protocol-policy.d.ts +2 -0
- package/dist/http/protocol-policy.js +31 -0
- package/dist/http/rate-limit.js +7 -4
- package/dist/http/server-config.d.ts +1 -0
- package/dist/http/server-config.js +40 -0
- package/dist/http/server-middleware.d.ts +7 -9
- package/dist/http/server-middleware.js +9 -70
- package/dist/http/server-shutdown.d.ts +4 -0
- package/dist/http/server-shutdown.js +43 -0
- package/dist/http/server.d.ts +10 -0
- package/dist/http/server.js +546 -61
- package/dist/http/session-cleanup.js +8 -5
- package/dist/middleware/error-handler.d.ts +1 -1
- package/dist/middleware/error-handler.js +32 -33
- package/dist/resources/cached-content-params.d.ts +5 -0
- package/dist/resources/cached-content-params.js +36 -0
- package/dist/resources/cached-content.js +67 -125
- package/dist/resources/index.js +0 -82
- package/dist/server.js +50 -29
- package/dist/services/cache-events.d.ts +8 -0
- package/dist/services/cache-events.js +19 -0
- package/dist/services/cache-keys.d.ts +7 -0
- package/dist/services/cache-keys.js +57 -0
- package/dist/services/cache.d.ts +4 -9
- package/dist/services/cache.js +77 -139
- package/dist/services/context.d.ts +0 -1
- package/dist/services/context.js +0 -7
- package/dist/services/extractor.js +55 -116
- package/dist/services/fetcher/agents.d.ts +2 -2
- package/dist/services/fetcher/agents.js +35 -96
- package/dist/services/fetcher/dns-selection.d.ts +2 -0
- package/dist/services/fetcher/dns-selection.js +72 -0
- package/dist/services/fetcher/interceptors.d.ts +0 -22
- package/dist/services/fetcher/interceptors.js +18 -32
- package/dist/services/fetcher/redirects.js +16 -7
- package/dist/services/fetcher/response.js +79 -34
- package/dist/services/fetcher.d.ts +22 -3
- package/dist/services/fetcher.js +544 -44
- package/dist/services/fifo-queue.d.ts +8 -0
- package/dist/services/fifo-queue.js +25 -0
- package/dist/services/logger.js +2 -2
- package/dist/services/metadata-collector.d.ts +1 -9
- package/dist/services/metadata-collector.js +71 -2
- package/dist/services/transform-worker-pool.d.ts +4 -14
- package/dist/services/transform-worker-pool.js +177 -129
- package/dist/services/transform-worker-types.d.ts +32 -0
- package/dist/services/transform-worker-types.js +14 -0
- package/dist/tools/handlers/fetch-markdown.tool.d.ts +3 -4
- package/dist/tools/handlers/fetch-markdown.tool.js +20 -72
- package/dist/tools/handlers/fetch-single.shared.d.ts +11 -22
- package/dist/tools/handlers/fetch-single.shared.js +175 -89
- package/dist/tools/handlers/fetch-url.tool.d.ts +7 -1
- package/dist/tools/handlers/fetch-url.tool.js +84 -119
- package/dist/tools/index.js +21 -40
- package/dist/tools/schemas.d.ts +1 -51
- package/dist/tools/schemas.js +1 -107
- package/dist/tools/utils/cached-markdown.d.ts +5 -0
- package/dist/tools/utils/cached-markdown.js +46 -0
- package/dist/tools/utils/content-shaping.d.ts +4 -0
- package/dist/tools/utils/content-shaping.js +67 -0
- package/dist/tools/utils/content-transform.d.ts +5 -17
- package/dist/tools/utils/content-transform.js +134 -114
- package/dist/tools/utils/fetch-pipeline.d.ts +0 -8
- package/dist/tools/utils/fetch-pipeline.js +57 -63
- package/dist/tools/utils/frontmatter.d.ts +3 -0
- package/dist/tools/utils/frontmatter.js +73 -0
- package/dist/tools/utils/inline-content.d.ts +1 -2
- package/dist/tools/utils/inline-content.js +4 -7
- package/dist/tools/utils/markdown-heuristics.d.ts +1 -0
- package/dist/tools/utils/markdown-heuristics.js +19 -0
- package/dist/tools/utils/markdown-signals.d.ts +1 -0
- package/dist/tools/utils/markdown-signals.js +19 -0
- package/dist/tools/utils/raw-markdown-frontmatter.d.ts +3 -0
- package/dist/tools/utils/raw-markdown-frontmatter.js +73 -0
- package/dist/tools/utils/raw-markdown.d.ts +6 -0
- package/dist/tools/utils/raw-markdown.js +135 -0
- package/dist/transformers/markdown/fenced-code-rule.d.ts +2 -0
- package/dist/transformers/markdown/fenced-code-rule.js +38 -0
- package/dist/transformers/markdown/frontmatter.d.ts +2 -0
- package/dist/transformers/markdown/frontmatter.js +45 -0
- package/dist/transformers/markdown/noise-rule.d.ts +2 -0
- package/dist/transformers/markdown/noise-rule.js +80 -0
- package/dist/transformers/markdown/turndown-instance.d.ts +2 -0
- package/dist/transformers/markdown/turndown-instance.js +19 -0
- package/dist/transformers/markdown.d.ts +2 -0
- package/dist/transformers/markdown.js +185 -0
- package/dist/transformers/markdown.transformer.js +5 -117
- package/dist/utils/cached-payload.d.ts +7 -0
- package/dist/utils/cached-payload.js +36 -0
- package/dist/utils/code-language-bash.d.ts +1 -0
- package/dist/utils/code-language-bash.js +48 -0
- package/dist/utils/code-language-core.d.ts +2 -0
- package/dist/utils/code-language-core.js +13 -0
- package/dist/utils/code-language-detectors.d.ts +5 -0
- package/dist/utils/code-language-detectors.js +142 -0
- package/dist/utils/code-language-helpers.d.ts +5 -0
- package/dist/utils/code-language-helpers.js +62 -0
- package/dist/utils/code-language-parsing.d.ts +5 -0
- package/dist/utils/code-language-parsing.js +62 -0
- package/dist/utils/code-language.d.ts +9 -0
- package/dist/utils/code-language.js +250 -46
- package/dist/utils/error-details.d.ts +3 -0
- package/dist/utils/error-details.js +12 -0
- package/dist/utils/error-utils.js +1 -1
- package/dist/utils/filename-generator.js +34 -12
- package/dist/utils/guards.d.ts +1 -0
- package/dist/utils/guards.js +3 -0
- package/dist/utils/header-normalizer.d.ts +0 -3
- package/dist/utils/header-normalizer.js +3 -3
- package/dist/utils/ip-address.d.ts +4 -0
- package/dist/utils/ip-address.js +6 -0
- package/dist/utils/tool-error-handler.d.ts +2 -2
- package/dist/utils/tool-error-handler.js +14 -46
- package/dist/utils/url-transformer.d.ts +7 -0
- package/dist/utils/url-transformer.js +147 -0
- package/dist/utils/url-validator.d.ts +1 -2
- package/dist/utils/url-validator.js +53 -114
- package/dist/workers/content-transform.worker.d.ts +1 -0
- package/dist/workers/content-transform.worker.js +40 -0
- package/package.json +17 -18
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function containsJsxTag(code: string): boolean;
|
|
2
|
+
export declare function containsWord(source: string, word: string): boolean;
|
|
3
|
+
export declare function splitLines(content: string): string[];
|
|
4
|
+
export declare function extractLanguageFromClassName(className: string): string | undefined;
|
|
5
|
+
export declare function resolveLanguageFromDataAttribute(dataLang: string): string | undefined;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function containsJsxTag(code) {
|
|
2
|
+
for (let index = 0; index < code.length - 1; index += 1) {
|
|
3
|
+
if (code[index] !== '<')
|
|
4
|
+
continue;
|
|
5
|
+
const next = code[index + 1];
|
|
6
|
+
if (!next)
|
|
7
|
+
continue;
|
|
8
|
+
if (next >= 'A' && next <= 'Z')
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
export function containsWord(source, word) {
|
|
14
|
+
let startIndex = source.indexOf(word);
|
|
15
|
+
while (startIndex !== -1) {
|
|
16
|
+
const before = startIndex === 0 ? '' : source[startIndex - 1];
|
|
17
|
+
const afterIndex = startIndex + word.length;
|
|
18
|
+
const after = afterIndex >= source.length ? '' : source[afterIndex];
|
|
19
|
+
if (!isWordChar(before) && !isWordChar(after))
|
|
20
|
+
return true;
|
|
21
|
+
startIndex = source.indexOf(word, startIndex + word.length);
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
export function splitLines(content) {
|
|
26
|
+
return content.split('\n');
|
|
27
|
+
}
|
|
28
|
+
export function extractLanguageFromClassName(className) {
|
|
29
|
+
const tokens = className.match(/\S+/g);
|
|
30
|
+
if (!tokens)
|
|
31
|
+
return undefined;
|
|
32
|
+
for (const token of tokens) {
|
|
33
|
+
const lower = token.toLowerCase();
|
|
34
|
+
if (lower.startsWith('language-'))
|
|
35
|
+
return token.slice('language-'.length);
|
|
36
|
+
if (lower.startsWith('lang-'))
|
|
37
|
+
return token.slice('lang-'.length);
|
|
38
|
+
if (lower.startsWith('highlight-')) {
|
|
39
|
+
return token.slice('highlight-'.length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
export function resolveLanguageFromDataAttribute(dataLang) {
|
|
45
|
+
const trimmed = dataLang.trim();
|
|
46
|
+
if (!trimmed)
|
|
47
|
+
return undefined;
|
|
48
|
+
for (const char of trimmed) {
|
|
49
|
+
if (!isWordChar(char))
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return trimmed;
|
|
53
|
+
}
|
|
54
|
+
function isWordChar(char) {
|
|
55
|
+
if (!char)
|
|
56
|
+
return false;
|
|
57
|
+
const code = char.charCodeAt(0);
|
|
58
|
+
return ((code >= 48 && code <= 57) ||
|
|
59
|
+
(code >= 65 && code <= 90) ||
|
|
60
|
+
(code >= 97 && code <= 122) ||
|
|
61
|
+
char === '_');
|
|
62
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function containsJsxTag(code: string): boolean;
|
|
2
|
+
export declare function containsWord(source: string, word: string): boolean;
|
|
3
|
+
export declare function splitLines(content: string): string[];
|
|
4
|
+
export declare function extractLanguageFromClassName(className: string): string | undefined;
|
|
5
|
+
export declare function resolveLanguageFromDataAttribute(dataLang: string): string | undefined;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function containsJsxTag(code) {
|
|
2
|
+
for (let index = 0; index < code.length - 1; index += 1) {
|
|
3
|
+
if (code[index] !== '<')
|
|
4
|
+
continue;
|
|
5
|
+
const next = code[index + 1];
|
|
6
|
+
if (!next)
|
|
7
|
+
continue;
|
|
8
|
+
if (next >= 'A' && next <= 'Z')
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
export function containsWord(source, word) {
|
|
14
|
+
let startIndex = source.indexOf(word);
|
|
15
|
+
while (startIndex !== -1) {
|
|
16
|
+
const before = startIndex === 0 ? '' : source[startIndex - 1];
|
|
17
|
+
const afterIndex = startIndex + word.length;
|
|
18
|
+
const after = afterIndex >= source.length ? '' : source[afterIndex];
|
|
19
|
+
if (!isWordChar(before) && !isWordChar(after))
|
|
20
|
+
return true;
|
|
21
|
+
startIndex = source.indexOf(word, startIndex + word.length);
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
export function splitLines(content) {
|
|
26
|
+
return content.split('\n');
|
|
27
|
+
}
|
|
28
|
+
export function extractLanguageFromClassName(className) {
|
|
29
|
+
const tokens = className.match(/\S+/g);
|
|
30
|
+
if (!tokens)
|
|
31
|
+
return undefined;
|
|
32
|
+
for (const token of tokens) {
|
|
33
|
+
const lower = token.toLowerCase();
|
|
34
|
+
if (lower.startsWith('language-'))
|
|
35
|
+
return token.slice('language-'.length);
|
|
36
|
+
if (lower.startsWith('lang-'))
|
|
37
|
+
return token.slice('lang-'.length);
|
|
38
|
+
if (lower.startsWith('highlight-')) {
|
|
39
|
+
return token.slice('highlight-'.length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
export function resolveLanguageFromDataAttribute(dataLang) {
|
|
45
|
+
const trimmed = dataLang.trim();
|
|
46
|
+
if (!trimmed)
|
|
47
|
+
return undefined;
|
|
48
|
+
for (const char of trimmed) {
|
|
49
|
+
if (!isWordChar(char))
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return trimmed;
|
|
53
|
+
}
|
|
54
|
+
function isWordChar(char) {
|
|
55
|
+
if (!char)
|
|
56
|
+
return false;
|
|
57
|
+
const code = char.charCodeAt(0);
|
|
58
|
+
return ((code >= 48 && code <= 57) ||
|
|
59
|
+
(code >= 65 && code <= 90) ||
|
|
60
|
+
(code >= 97 && code <= 122) ||
|
|
61
|
+
char === '_');
|
|
62
|
+
}
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
+
export declare function containsJsxTag(code: string): boolean;
|
|
2
|
+
export declare function containsWord(source: string, word: string): boolean;
|
|
3
|
+
export declare function splitLines(content: string): string[];
|
|
4
|
+
export declare function extractLanguageFromClassName(className: string): string | undefined;
|
|
5
|
+
export declare function resolveLanguageFromDataAttribute(dataLang: string): string | undefined;
|
|
6
|
+
export interface CodeDetector {
|
|
7
|
+
language: string;
|
|
8
|
+
detect: (code: string) => boolean;
|
|
9
|
+
}
|
|
1
10
|
export declare function detectLanguageFromCode(code: string): string | undefined;
|
|
2
11
|
export declare function resolveLanguageFromAttributes(className: string, dataLang: string): string | undefined;
|
|
@@ -1,56 +1,260 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1
|
+
export function containsJsxTag(code) {
|
|
2
|
+
for (let index = 0; index < code.length - 1; index += 1) {
|
|
3
|
+
if (code[index] !== '<')
|
|
4
|
+
continue;
|
|
5
|
+
const next = code[index + 1];
|
|
6
|
+
if (!next)
|
|
7
|
+
continue;
|
|
8
|
+
if (next >= 'A' && next <= 'Z')
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
export function containsWord(source, word) {
|
|
14
|
+
let startIndex = source.indexOf(word);
|
|
15
|
+
while (startIndex !== -1) {
|
|
16
|
+
const before = startIndex === 0 ? '' : source[startIndex - 1];
|
|
17
|
+
const afterIndex = startIndex + word.length;
|
|
18
|
+
const after = afterIndex >= source.length ? '' : source[afterIndex];
|
|
19
|
+
if (!isWordChar(before) && !isWordChar(after))
|
|
20
|
+
return true;
|
|
21
|
+
startIndex = source.indexOf(word, startIndex + word.length);
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
export function splitLines(content) {
|
|
26
|
+
return content.split('\n');
|
|
27
|
+
}
|
|
28
|
+
export function extractLanguageFromClassName(className) {
|
|
29
|
+
const tokens = className.match(/\S+/g);
|
|
30
|
+
if (!tokens)
|
|
31
|
+
return undefined;
|
|
32
|
+
for (const token of tokens) {
|
|
33
|
+
const lower = token.toLowerCase();
|
|
34
|
+
if (lower.startsWith('language-'))
|
|
35
|
+
return token.slice('language-'.length);
|
|
36
|
+
if (lower.startsWith('lang-'))
|
|
37
|
+
return token.slice('lang-'.length);
|
|
38
|
+
if (lower.startsWith('highlight-')) {
|
|
39
|
+
return token.slice('highlight-'.length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
export function resolveLanguageFromDataAttribute(dataLang) {
|
|
45
|
+
const trimmed = dataLang.trim();
|
|
46
|
+
if (!trimmed)
|
|
47
|
+
return undefined;
|
|
48
|
+
for (const char of trimmed) {
|
|
49
|
+
if (!isWordChar(char))
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return trimmed;
|
|
53
|
+
}
|
|
54
|
+
function isWordChar(char) {
|
|
55
|
+
if (!char)
|
|
56
|
+
return false;
|
|
57
|
+
const code = char.charCodeAt(0);
|
|
58
|
+
return ((code >= 48 && code <= 57) ||
|
|
59
|
+
(code >= 65 && code <= 90) ||
|
|
60
|
+
(code >= 97 && code <= 122) ||
|
|
61
|
+
char === '_');
|
|
62
|
+
}
|
|
63
|
+
const BASH_PACKAGE_MANAGERS = [
|
|
64
|
+
'npm',
|
|
65
|
+
'yarn',
|
|
66
|
+
'pnpm',
|
|
67
|
+
'npx',
|
|
68
|
+
'brew',
|
|
69
|
+
'apt',
|
|
70
|
+
'pip',
|
|
71
|
+
'cargo',
|
|
72
|
+
'go',
|
|
73
|
+
];
|
|
74
|
+
const BASH_VERBS = ['install', 'add', 'run', 'build', 'start'];
|
|
75
|
+
const BASH_COMMANDS = ['sudo', 'chmod', 'mkdir', 'cd', 'ls', 'cat', 'echo'];
|
|
76
|
+
function detectBash(code) {
|
|
77
|
+
const lines = splitLines(code);
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
const trimmed = line.trimStart();
|
|
80
|
+
if (!trimmed)
|
|
81
|
+
continue;
|
|
82
|
+
if (isBashIndicator(trimmed))
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function startsWithCommand(line, commands) {
|
|
88
|
+
return commands.some((command) => line === command || line.startsWith(`${command} `));
|
|
89
|
+
}
|
|
90
|
+
function isBashIndicator(line) {
|
|
91
|
+
return (isShebang(line) ||
|
|
92
|
+
isPromptLine(line) ||
|
|
93
|
+
startsWithCommand(line, BASH_COMMANDS) ||
|
|
94
|
+
startsWithPackageManagerCommand(line));
|
|
95
|
+
}
|
|
96
|
+
function isShebang(line) {
|
|
97
|
+
return line.startsWith('#!');
|
|
98
|
+
}
|
|
99
|
+
function isPromptLine(line) {
|
|
100
|
+
return line.startsWith('$ ') || line.startsWith('# ');
|
|
101
|
+
}
|
|
102
|
+
function startsWithPackageManagerCommand(line) {
|
|
103
|
+
return BASH_PACKAGE_MANAGERS.some((manager) => {
|
|
104
|
+
if (!line.startsWith(`${manager} `))
|
|
105
|
+
return false;
|
|
106
|
+
const rest = line.slice(manager.length + 1);
|
|
107
|
+
return BASH_VERBS.some((verb) => rest === verb || rest.startsWith(`${verb} `));
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const TYPE_HINTS = [
|
|
111
|
+
'string',
|
|
112
|
+
'number',
|
|
113
|
+
'boolean',
|
|
114
|
+
'void',
|
|
115
|
+
'any',
|
|
116
|
+
'unknown',
|
|
117
|
+
'never',
|
|
118
|
+
];
|
|
119
|
+
const HTML_TAGS = [
|
|
120
|
+
'<!doctype',
|
|
121
|
+
'<html',
|
|
122
|
+
'<head',
|
|
123
|
+
'<body',
|
|
124
|
+
'<div',
|
|
125
|
+
'<span',
|
|
126
|
+
'<p',
|
|
127
|
+
'<a',
|
|
128
|
+
'<script',
|
|
129
|
+
'<style',
|
|
130
|
+
];
|
|
131
|
+
const SQL_KEYWORDS = [
|
|
132
|
+
'select',
|
|
133
|
+
'insert',
|
|
134
|
+
'update',
|
|
135
|
+
'delete',
|
|
136
|
+
'create',
|
|
137
|
+
'alter',
|
|
138
|
+
'drop',
|
|
27
139
|
];
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
140
|
+
const JS_WORD_REGEX = /\b(?:const|let|var|function|class|async|await|export|import)\b/;
|
|
141
|
+
const PYTHON_WORD_REGEX = /\b(?:def|class|import|from)\b/;
|
|
142
|
+
const RUST_WORD_REGEX = /\b(?:fn|impl|struct|enum)\b/;
|
|
143
|
+
const CSS_DIRECTIVE_REGEX = /@media|@import|@keyframes/;
|
|
144
|
+
const CODE_DETECTORS = [
|
|
145
|
+
{ language: 'jsx', detect: detectJsx },
|
|
146
|
+
{ language: 'typescript', detect: detectTypescript },
|
|
147
|
+
{ language: 'rust', detect: detectRust },
|
|
148
|
+
{ language: 'javascript', detect: detectJavascript },
|
|
149
|
+
{ language: 'python', detect: detectPython },
|
|
150
|
+
{ language: 'bash', detect: detectBash },
|
|
151
|
+
{ language: 'css', detect: detectCss },
|
|
152
|
+
{ language: 'html', detect: detectHtml },
|
|
153
|
+
{ language: 'json', detect: detectJson },
|
|
154
|
+
{ language: 'yaml', detect: detectYaml },
|
|
155
|
+
{ language: 'sql', detect: detectSql },
|
|
156
|
+
{ language: 'go', detect: detectGo },
|
|
32
157
|
];
|
|
158
|
+
function detectJsx(code) {
|
|
159
|
+
const lower = code.toLowerCase();
|
|
160
|
+
if (lower.includes('classname='))
|
|
161
|
+
return true;
|
|
162
|
+
if (lower.includes('jsx:'))
|
|
163
|
+
return true;
|
|
164
|
+
if (lower.includes("from 'react'") || lower.includes('from "react"')) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
return containsJsxTag(code);
|
|
168
|
+
}
|
|
169
|
+
function detectTypescript(code) {
|
|
170
|
+
const lower = code.toLowerCase();
|
|
171
|
+
if (containsWord(lower, 'interface'))
|
|
172
|
+
return true;
|
|
173
|
+
if (containsWord(lower, 'type'))
|
|
174
|
+
return true;
|
|
175
|
+
return TYPE_HINTS.some((hint) => lower.includes(`: ${hint}`) || lower.includes(`:${hint}`));
|
|
176
|
+
}
|
|
177
|
+
function detectRust(code) {
|
|
178
|
+
const lower = code.toLowerCase();
|
|
179
|
+
return (RUST_WORD_REGEX.test(lower) ||
|
|
180
|
+
lower.includes('let mut') ||
|
|
181
|
+
(lower.includes('use ') && lower.includes('::')));
|
|
182
|
+
}
|
|
183
|
+
function detectJavascript(code) {
|
|
184
|
+
const lower = code.toLowerCase();
|
|
185
|
+
return JS_WORD_REGEX.test(lower);
|
|
186
|
+
}
|
|
187
|
+
function detectPython(code) {
|
|
188
|
+
const lower = code.toLowerCase();
|
|
189
|
+
return (PYTHON_WORD_REGEX.test(lower) ||
|
|
190
|
+
lower.includes('print(') ||
|
|
191
|
+
lower.includes('__name__'));
|
|
192
|
+
}
|
|
193
|
+
function detectCss(code) {
|
|
194
|
+
const lower = code.toLowerCase();
|
|
195
|
+
if (CSS_DIRECTIVE_REGEX.test(lower))
|
|
196
|
+
return true;
|
|
197
|
+
const lines = splitLines(code);
|
|
198
|
+
for (const line of lines) {
|
|
199
|
+
const trimmed = line.trimStart();
|
|
200
|
+
if (!trimmed)
|
|
201
|
+
continue;
|
|
202
|
+
if (isCssSelectorLine(trimmed) || isCssPropertyLine(trimmed))
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
function detectHtml(code) {
|
|
208
|
+
const lower = code.toLowerCase();
|
|
209
|
+
return HTML_TAGS.some((tag) => lower.includes(tag));
|
|
210
|
+
}
|
|
211
|
+
function detectJson(code) {
|
|
212
|
+
const trimmed = code.trimStart();
|
|
213
|
+
if (!trimmed)
|
|
214
|
+
return false;
|
|
215
|
+
return trimmed.startsWith('{') || trimmed.startsWith('[');
|
|
216
|
+
}
|
|
217
|
+
function detectYaml(code) {
|
|
218
|
+
const lines = splitLines(code);
|
|
219
|
+
for (const line of lines) {
|
|
220
|
+
const trimmed = line.trim();
|
|
221
|
+
if (!trimmed)
|
|
222
|
+
continue;
|
|
223
|
+
const colonIndex = trimmed.indexOf(':');
|
|
224
|
+
if (colonIndex <= 0)
|
|
225
|
+
continue;
|
|
226
|
+
const after = trimmed[colonIndex + 1];
|
|
227
|
+
if (after === ' ' || after === '\t')
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
function detectSql(code) {
|
|
233
|
+
const lower = code.toLowerCase();
|
|
234
|
+
return SQL_KEYWORDS.some((keyword) => containsWord(lower, keyword));
|
|
235
|
+
}
|
|
236
|
+
function detectGo(code) {
|
|
237
|
+
const lower = code.toLowerCase();
|
|
238
|
+
return (containsWord(lower, 'package') ||
|
|
239
|
+
containsWord(lower, 'func') ||
|
|
240
|
+
lower.includes('import "'));
|
|
241
|
+
}
|
|
242
|
+
function isCssSelectorLine(line) {
|
|
243
|
+
if (!line.startsWith('.') && !line.startsWith('#'))
|
|
244
|
+
return false;
|
|
245
|
+
return line.includes('{');
|
|
246
|
+
}
|
|
247
|
+
function isCssPropertyLine(line) {
|
|
248
|
+
return line.includes(':') && line.includes(';');
|
|
249
|
+
}
|
|
33
250
|
export function detectLanguageFromCode(code) {
|
|
34
|
-
for (const
|
|
35
|
-
if (
|
|
251
|
+
for (const { language, detect } of CODE_DETECTORS) {
|
|
252
|
+
if (detect(code))
|
|
36
253
|
return language;
|
|
37
|
-
}
|
|
38
254
|
}
|
|
39
255
|
return undefined;
|
|
40
256
|
}
|
|
41
257
|
export function resolveLanguageFromAttributes(className, dataLang) {
|
|
42
|
-
const classMatch =
|
|
258
|
+
const classMatch = extractLanguageFromClassName(className);
|
|
43
259
|
return classMatch ?? resolveLanguageFromDataAttribute(dataLang);
|
|
44
260
|
}
|
|
45
|
-
function matchFirstCapture(value, patterns) {
|
|
46
|
-
for (const pattern of patterns) {
|
|
47
|
-
const match = pattern.exec(value);
|
|
48
|
-
if (match?.[1])
|
|
49
|
-
return match[1];
|
|
50
|
-
}
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
function resolveLanguageFromDataAttribute(dataLang) {
|
|
54
|
-
const match = /^(\w+)$/.exec(dataLang);
|
|
55
|
-
return match?.[1];
|
|
56
|
-
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function getErrorMessage(error) {
|
|
2
|
+
return error instanceof Error ? error.message : 'Unknown error';
|
|
3
|
+
}
|
|
4
|
+
export function createErrorWithCode(message, code) {
|
|
5
|
+
const error = new Error(message);
|
|
6
|
+
return Object.assign(error, { code });
|
|
7
|
+
}
|
|
8
|
+
export function isSystemError(error) {
|
|
9
|
+
return (error instanceof Error &&
|
|
10
|
+
'code' in error &&
|
|
11
|
+
typeof Reflect.get(error, 'code') === 'string');
|
|
12
|
+
}
|
|
@@ -2,6 +2,17 @@ const MAX_FILENAME_LENGTH = 200;
|
|
|
2
2
|
const UNSAFE_CHARS_REGEX = /[<>:"/\\|?*]|\p{C}/gu;
|
|
3
3
|
const WHITESPACE_REGEX = /\s+/g;
|
|
4
4
|
const DEFAULT_EXTENSION = '.md';
|
|
5
|
+
function trimHyphens(value) {
|
|
6
|
+
let start = 0;
|
|
7
|
+
let end = value.length;
|
|
8
|
+
while (start < end && value[start] === '-') {
|
|
9
|
+
start += 1;
|
|
10
|
+
}
|
|
11
|
+
while (end > start && value[end - 1] === '-') {
|
|
12
|
+
end -= 1;
|
|
13
|
+
}
|
|
14
|
+
return value.slice(start, end);
|
|
15
|
+
}
|
|
5
16
|
export function generateSafeFilename(url, title, hashFallback, extension = DEFAULT_EXTENSION) {
|
|
6
17
|
const fromUrl = extractFilenameFromUrl(url);
|
|
7
18
|
if (fromUrl)
|
|
@@ -16,20 +27,31 @@ export function generateSafeFilename(url, title, hashFallback, extension = DEFAU
|
|
|
16
27
|
}
|
|
17
28
|
return `download-${Date.now()}${extension}`;
|
|
18
29
|
}
|
|
30
|
+
function getLastPathSegment(url) {
|
|
31
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
32
|
+
if (segments.length === 0)
|
|
33
|
+
return null;
|
|
34
|
+
const lastSegment = segments[segments.length - 1];
|
|
35
|
+
return lastSegment ?? null;
|
|
36
|
+
}
|
|
37
|
+
function stripCommonPageExtension(segment) {
|
|
38
|
+
return segment.replace(/\.(html?|php|aspx?|jsp)$/i, '');
|
|
39
|
+
}
|
|
40
|
+
function normalizeUrlFilenameSegment(segment) {
|
|
41
|
+
const cleaned = stripCommonPageExtension(segment);
|
|
42
|
+
if (!cleaned)
|
|
43
|
+
return null;
|
|
44
|
+
if (cleaned === 'index')
|
|
45
|
+
return null;
|
|
46
|
+
return cleaned;
|
|
47
|
+
}
|
|
19
48
|
function extractFilenameFromUrl(url) {
|
|
20
49
|
try {
|
|
21
50
|
const urlObj = new URL(url);
|
|
22
|
-
const
|
|
23
|
-
const segments = pathname.split('/').filter(Boolean);
|
|
24
|
-
if (segments.length === 0)
|
|
25
|
-
return null;
|
|
26
|
-
const lastSegment = segments[segments.length - 1];
|
|
51
|
+
const lastSegment = getLastPathSegment(urlObj);
|
|
27
52
|
if (!lastSegment)
|
|
28
53
|
return null;
|
|
29
|
-
|
|
30
|
-
if (!cleaned || cleaned === 'index')
|
|
31
|
-
return null;
|
|
32
|
-
return cleaned;
|
|
54
|
+
return normalizeUrlFilenameSegment(lastSegment);
|
|
33
55
|
}
|
|
34
56
|
catch {
|
|
35
57
|
return null;
|
|
@@ -41,9 +63,9 @@ function slugifyTitle(title) {
|
|
|
41
63
|
.trim()
|
|
42
64
|
.replace(UNSAFE_CHARS_REGEX, '')
|
|
43
65
|
.replace(WHITESPACE_REGEX, '-')
|
|
44
|
-
.replace(/-+/g, '-')
|
|
45
|
-
|
|
46
|
-
return
|
|
66
|
+
.replace(/-+/g, '-');
|
|
67
|
+
const trimmed = trimHyphens(slug);
|
|
68
|
+
return trimmed || null;
|
|
47
69
|
}
|
|
48
70
|
function sanitizeFilename(name, extension) {
|
|
49
71
|
let sanitized = name
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
@@ -2,7 +2,4 @@ interface NormalizeOptions {
|
|
|
2
2
|
readonly trimValues?: boolean;
|
|
3
3
|
}
|
|
4
4
|
export declare function normalizeHeaderRecord(headers: Record<string, string> | undefined, blockedHeaders: Set<string>, options?: NormalizeOptions): Record<string, string> | undefined;
|
|
5
|
-
export declare function normalizeHeaderEntries(headers: Record<string, string>, blockedHeaders: Set<string>, options?: NormalizeOptions): Headers;
|
|
6
|
-
export declare function hasHeaderEntries(headers: Headers): boolean;
|
|
7
|
-
export declare function headersToRecord(headers: Headers): Record<string, string>;
|
|
8
5
|
export {};
|
|
@@ -6,7 +6,7 @@ export function normalizeHeaderRecord(headers, blockedHeaders, options = {}) {
|
|
|
6
6
|
return undefined;
|
|
7
7
|
return headersToRecord(normalized);
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
function normalizeHeaderEntries(headers, blockedHeaders, options = {}) {
|
|
10
10
|
const normalized = new Headers();
|
|
11
11
|
for (const [key, value] of Object.entries(headers)) {
|
|
12
12
|
if (blockedHeaders.has(key.toLowerCase()))
|
|
@@ -15,10 +15,10 @@ export function normalizeHeaderEntries(headers, blockedHeaders, options = {}) {
|
|
|
15
15
|
}
|
|
16
16
|
return normalized;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
function hasHeaderEntries(headers) {
|
|
19
19
|
return !headers.keys().next().done;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
function headersToRecord(headers) {
|
|
22
22
|
return Object.fromEntries(headers.entries());
|
|
23
23
|
}
|
|
24
24
|
function setHeaderValue(headers, key, value, trimValue) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ToolErrorResponse } from '../config/types/tools.js';
|
|
2
|
-
export declare function createToolErrorResponse(message: string, url: string
|
|
3
|
-
export declare function handleToolError(error: unknown, url: string, fallbackMessage?: string
|
|
2
|
+
export declare function createToolErrorResponse(message: string, url: string): ToolErrorResponse;
|
|
3
|
+
export declare function handleToolError(error: unknown, url: string, fallbackMessage?: string): ToolErrorResponse;
|