@mintlify/common 1.0.1262 → 1.0.1264
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mdx/plugins/rehype/rehypeMdxExtractEndpoint/index.js +3 -1
- package/dist/mintExportAssets.d.ts +3 -0
- package/dist/mintExportAssets.js +58 -0
- package/dist/mintExportAssets.test.d.ts +1 -0
- package/dist/mintExportAssets.test.js +43 -0
- package/dist/server-only/ssrf.d.ts +8 -0
- package/dist/server-only/ssrf.js +108 -0
- package/dist/snippetPresets.d.ts +26 -0
- package/dist/snippetPresets.js +149 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +7 -3
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -80,7 +80,9 @@ export const rehypeMdxExtractEndpoint = (metadata, config, mdxExtracts) => {
|
|
|
80
80
|
});
|
|
81
81
|
const content = endpoint.request.body[requestContentType];
|
|
82
82
|
if (content) {
|
|
83
|
-
content.examples['example'] = {
|
|
83
|
+
content.examples['example'] = {
|
|
84
|
+
value: generateExampleFromSchema(content.schemaArray[0], 0, 'request'),
|
|
85
|
+
};
|
|
84
86
|
}
|
|
85
87
|
const response = (_a = endpoint.response['200']) === null || _a === void 0 ? void 0 : _a['application/json'];
|
|
86
88
|
if (response) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const MINT_EXPORT_ASSETS_PREFIX = '/_mintlify/export-assets';
|
|
2
|
+
const QUERY_SEGMENT = '_q';
|
|
3
|
+
const HTTP_SEGMENT = '_http';
|
|
4
|
+
const PORT_SEPARATOR = '~';
|
|
5
|
+
const toBase64Url = (value) => btoa(String.fromCharCode(...new TextEncoder().encode(value)))
|
|
6
|
+
.replace(/\+/g, '-')
|
|
7
|
+
.replace(/\//g, '_')
|
|
8
|
+
.replace(/=+$/, '');
|
|
9
|
+
const fromBase64Url = (value) => {
|
|
10
|
+
const base64 = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
11
|
+
const bytes = Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
|
|
12
|
+
return new TextDecoder().decode(bytes);
|
|
13
|
+
};
|
|
14
|
+
export const toMintExportAssetPath = (url, extension) => {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = new URL(url);
|
|
19
|
+
}
|
|
20
|
+
catch (_c) {
|
|
21
|
+
return url;
|
|
22
|
+
}
|
|
23
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')
|
|
24
|
+
return url;
|
|
25
|
+
const scheme = parsed.protocol === 'http:' ? `${HTTP_SEGMENT}/` : '';
|
|
26
|
+
const host = parsed.port ? `${parsed.hostname}${PORT_SEPARATOR}${parsed.port}` : parsed.hostname;
|
|
27
|
+
const base = `${MINT_EXPORT_ASSETS_PREFIX}/${scheme}${host}${parsed.pathname}`;
|
|
28
|
+
if (!parsed.search)
|
|
29
|
+
return base;
|
|
30
|
+
const resolvedExtension = extension !== null && extension !== void 0 ? extension : (_b = (_a = /\.([a-z0-9]+)$/i.exec(parsed.pathname)) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
|
31
|
+
const suffix = resolvedExtension ? `.${resolvedExtension}` : '';
|
|
32
|
+
return `${base.replace(/\/$/, '')}/${QUERY_SEGMENT}/${toBase64Url(parsed.search.slice(1))}${suffix}`;
|
|
33
|
+
};
|
|
34
|
+
export const fromMintExportAssetPath = (assetPath) => {
|
|
35
|
+
if (!assetPath.startsWith(`${MINT_EXPORT_ASSETS_PREFIX}/`))
|
|
36
|
+
return undefined;
|
|
37
|
+
const unprefixed = assetPath.slice(MINT_EXPORT_ASSETS_PREFIX.length + 1);
|
|
38
|
+
const isHttp = unprefixed.startsWith(`${HTTP_SEGMENT}/`);
|
|
39
|
+
const protocol = isHttp ? 'http' : 'https';
|
|
40
|
+
const rest = isHttp ? unprefixed.slice(HTTP_SEGMENT.length + 1) : unprefixed;
|
|
41
|
+
const slashIndex = rest.indexOf('/');
|
|
42
|
+
const encodedHost = slashIndex === -1 ? rest : rest.slice(0, slashIndex);
|
|
43
|
+
const pathname = slashIndex === -1 ? '/' : rest.slice(slashIndex);
|
|
44
|
+
if (!encodedHost || encodedHost.startsWith('.') || encodedHost.includes('..'))
|
|
45
|
+
return undefined;
|
|
46
|
+
const host = encodedHost.replace(PORT_SEPARATOR, ':');
|
|
47
|
+
const queryMarker = `/${QUERY_SEGMENT}/`;
|
|
48
|
+
const queryIndex = pathname.lastIndexOf(queryMarker);
|
|
49
|
+
if (queryIndex === -1)
|
|
50
|
+
return `${protocol}://${host}${pathname}`;
|
|
51
|
+
const encodedQuery = pathname.slice(queryIndex + queryMarker.length).replace(/\.[a-z0-9]+$/i, '');
|
|
52
|
+
try {
|
|
53
|
+
return `${protocol}://${host}${pathname.slice(0, queryIndex)}?${fromBase64Url(encodedQuery)}`;
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { fromMintExportAssetPath, toMintExportAssetPath } from './mintExportAssets.js';
|
|
3
|
+
describe('toMintExportAssetPath', () => {
|
|
4
|
+
it('maps an absolute url to a local asset path', () => {
|
|
5
|
+
expect(toMintExportAssetPath('https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/house.svg')).toBe('/_mintlify/export-assets/d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/house.svg');
|
|
6
|
+
});
|
|
7
|
+
it('encodes the query string into the path with the given extension', () => {
|
|
8
|
+
const assetPath = toMintExportAssetPath('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;1,700&display=swap', 'css');
|
|
9
|
+
expect(assetPath).toMatch(/^\/_mintlify\/export-assets\/fonts\.googleapis\.com\/css2\/_q\/[\w-]+\.css$/);
|
|
10
|
+
});
|
|
11
|
+
it('keeps the source extension for urls with a query', () => {
|
|
12
|
+
expect(toMintExportAssetPath('https://cdn.example.com/style.css?v=1')).toMatch(/^\/_mintlify\/export-assets\/cdn\.example\.com\/style\.css\/_q\/[\w-]+\.css$/);
|
|
13
|
+
});
|
|
14
|
+
it('encodes ports without a colon so the path is valid on every file system', () => {
|
|
15
|
+
expect(toMintExportAssetPath('https://fonts.example.com:8443/custom.woff2')).toBe('/_mintlify/export-assets/fonts.example.com~8443/custom.woff2');
|
|
16
|
+
});
|
|
17
|
+
it('keeps plain http assets distinct from https ones', () => {
|
|
18
|
+
expect(toMintExportAssetPath('http://example.com/a.woff2')).toBe('/_mintlify/export-assets/_http/example.com/a.woff2');
|
|
19
|
+
});
|
|
20
|
+
it('leaves relative and non-http urls untouched', () => {
|
|
21
|
+
expect(toMintExportAssetPath('/images/logo.svg')).toBe('/images/logo.svg');
|
|
22
|
+
expect(toMintExportAssetPath('data:image/svg+xml,abc')).toBe('data:image/svg+xml,abc');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('fromMintExportAssetPath', () => {
|
|
26
|
+
it.each([
|
|
27
|
+
'https://d3gk2c5xim1je2.cloudfront.net/flags/US.svg',
|
|
28
|
+
'https://cdn.jsdelivr.net/npm/@fontsource/inter@5/files/inter-latin-400-normal.woff2',
|
|
29
|
+
'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;1,700&display=swap',
|
|
30
|
+
'https://example.com/font.woff2?v=1&name=%C3%BC',
|
|
31
|
+
'http://fonts.example.com/inter.woff2',
|
|
32
|
+
'http://example.com/style.css?v=2',
|
|
33
|
+
'https://example.com/font.WOFF2?v=1',
|
|
34
|
+
'https://fonts.example.com:8443/custom.woff2',
|
|
35
|
+
'http://localhost:8080/a.css?v=1',
|
|
36
|
+
])('round-trips %s', (url) => {
|
|
37
|
+
expect(fromMintExportAssetPath(toMintExportAssetPath(url, 'css'))).toBe(url);
|
|
38
|
+
});
|
|
39
|
+
it('rejects paths outside the asset prefix or with traversal hosts', () => {
|
|
40
|
+
expect(fromMintExportAssetPath('/images/logo.svg')).toBeUndefined();
|
|
41
|
+
expect(fromMintExportAssetPath('/_mintlify/export-assets/../etc/passwd')).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import dns from 'dns';
|
|
2
|
+
export declare class PrivateIPError extends Error {
|
|
3
|
+
readonly hostname: string;
|
|
4
|
+
readonly ip: string;
|
|
5
|
+
constructor(hostname: string, ip: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function isPrivateIP(ip: string): boolean;
|
|
8
|
+
export declare function safeLookup(hostname: string, options: dns.LookupOptions, callback: (err: NodeJS.ErrnoException | null, address: string | dns.LookupAddress[], family?: number) => void): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import dns from 'dns';
|
|
2
|
+
import { BlockList, isIP } from 'net';
|
|
3
|
+
export class PrivateIPError extends Error {
|
|
4
|
+
constructor(hostname, ip) {
|
|
5
|
+
super(`Blocked: ${hostname} resolves to private IP ${ip}`);
|
|
6
|
+
this.hostname = hostname;
|
|
7
|
+
this.ip = ip;
|
|
8
|
+
this.name = 'PrivateIPError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const privateIPBlockList = new BlockList();
|
|
12
|
+
for (const [network, prefix] of [
|
|
13
|
+
['0.0.0.0', 8],
|
|
14
|
+
['10.0.0.0', 8],
|
|
15
|
+
['100.64.0.0', 10],
|
|
16
|
+
['127.0.0.0', 8],
|
|
17
|
+
['169.254.0.0', 16],
|
|
18
|
+
['172.16.0.0', 12],
|
|
19
|
+
['192.0.0.0', 24],
|
|
20
|
+
['192.0.2.0', 24],
|
|
21
|
+
['192.88.99.0', 24],
|
|
22
|
+
['192.168.0.0', 16],
|
|
23
|
+
['198.18.0.0', 15],
|
|
24
|
+
['198.51.100.0', 24],
|
|
25
|
+
['203.0.113.0', 24],
|
|
26
|
+
['224.0.0.0', 4],
|
|
27
|
+
['240.0.0.0', 4],
|
|
28
|
+
]) {
|
|
29
|
+
privateIPBlockList.addSubnet(network, prefix, 'ipv4');
|
|
30
|
+
}
|
|
31
|
+
// ::/96 covers ::, ::1 and the deprecated IPv4-compatible ::a.b.c.d form; the NAT64 and
|
|
32
|
+
// 6to4 prefixes embed an arbitrary IPv4 address, so they are blocked wholesale rather
|
|
33
|
+
// than unwrapped - they are transition mechanisms, never a legitimate API destination
|
|
34
|
+
for (const [network, prefix] of [
|
|
35
|
+
['::', 96],
|
|
36
|
+
['64:ff9b::', 96],
|
|
37
|
+
['64:ff9b:1::', 48],
|
|
38
|
+
['100::', 64],
|
|
39
|
+
['2001::', 32],
|
|
40
|
+
['2001:2::', 48],
|
|
41
|
+
['2001:10::', 28],
|
|
42
|
+
['2001:20::', 28],
|
|
43
|
+
['2001:db8::', 32],
|
|
44
|
+
['2002::', 16],
|
|
45
|
+
['3fff::', 20],
|
|
46
|
+
['5f00::', 16],
|
|
47
|
+
['fc00::', 7],
|
|
48
|
+
['fe80::', 10],
|
|
49
|
+
['fec0::', 10],
|
|
50
|
+
['ff00::', 8],
|
|
51
|
+
]) {
|
|
52
|
+
privateIPBlockList.addSubnet(network, prefix, 'ipv6');
|
|
53
|
+
}
|
|
54
|
+
export function isPrivateIP(ip) {
|
|
55
|
+
const ipType = isIP(ip);
|
|
56
|
+
if (ipType === 0)
|
|
57
|
+
return true;
|
|
58
|
+
// handle IPv4-mapped IPv6 (::ffff:x.x.x.x) - extract IPv4 and check that
|
|
59
|
+
const lower = ip.toLowerCase();
|
|
60
|
+
if (lower.startsWith('::ffff:') && ip.includes('.')) {
|
|
61
|
+
const ipv4 = ip.slice(7);
|
|
62
|
+
if (isIP(ipv4) === 4) {
|
|
63
|
+
return privateIPBlockList.check(ipv4, 'ipv4');
|
|
64
|
+
}
|
|
65
|
+
return true; // malformed IPv4 part = block
|
|
66
|
+
}
|
|
67
|
+
return privateIPBlockList.check(ip, ipType === 4 ? 'ipv4' : 'ipv6');
|
|
68
|
+
}
|
|
69
|
+
// custom lookup that validates resolved IPs before returning them
|
|
70
|
+
// the IP returned here is the EXACT IP the socket will connect to (no TOCTOU)
|
|
71
|
+
// resolves all addresses so dual-stack fallback keeps working; rejects if any is private
|
|
72
|
+
export function safeLookup(hostname, options, callback) {
|
|
73
|
+
dns.lookup(hostname, Object.assign(Object.assign({}, options), { all: true }), (err, addresses) => {
|
|
74
|
+
const fail = (e) => {
|
|
75
|
+
if (options.all) {
|
|
76
|
+
callback(e, []);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
callback(e, '', 0);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
if (err) {
|
|
83
|
+
fail(err);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(addresses)) {
|
|
87
|
+
fail(new Error('unexpected DNS lookup result'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const [first] = addresses;
|
|
91
|
+
if (!first) {
|
|
92
|
+
fail(new Error('unexpected DNS lookup result'));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
for (const { address } of addresses) {
|
|
96
|
+
if (typeof address !== 'string' || isPrivateIP(address)) {
|
|
97
|
+
fail(new PrivateIPError(hostname, address));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (options.all) {
|
|
102
|
+
callback(null, addresses);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
callback(null, first.address, first.family);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const MINTLIFY_ICONS_CDN_URL = "https://d3gk2c5xim1je2.cloudfront.net";
|
|
2
|
+
export type SnippetPreset = {
|
|
3
|
+
/** Canonical identifier (e.g., 'python', 'bash') */
|
|
4
|
+
key: string;
|
|
5
|
+
/** Alternative names that map to this preset (e.g., ['py'] for python) */
|
|
6
|
+
aliases?: string[];
|
|
7
|
+
/** Human-readable name (e.g., "Python", "cURL") */
|
|
8
|
+
displayName: string;
|
|
9
|
+
/** Devicon SVG name if different from key (e.g., 'dot-net' for '.NET') */
|
|
10
|
+
iconKey?: string;
|
|
11
|
+
/** Shiki language for syntax highlighting */
|
|
12
|
+
shikiLanguage: string;
|
|
13
|
+
/** httpsnippet config for code generation */
|
|
14
|
+
httpSnippet: {
|
|
15
|
+
target: string;
|
|
16
|
+
client?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Central config for API playground language options.
|
|
21
|
+
* Each preset defines: display info, icon, syntax highlighting, and code generation.
|
|
22
|
+
*/
|
|
23
|
+
export declare const SNIPPET_PRESETS: SnippetPreset[];
|
|
24
|
+
export declare const getPreset: (lang: string) => SnippetPreset | undefined;
|
|
25
|
+
export declare const getIconKey: (lang: string) => string | undefined;
|
|
26
|
+
export declare const getLanguageIconUrl: (language: string) => string | null;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
export const MINTLIFY_ICONS_CDN_URL = 'https://d3gk2c5xim1je2.cloudfront.net';
|
|
3
|
+
/**
|
|
4
|
+
* Central config for API playground language options.
|
|
5
|
+
* Each preset defines: display info, icon, syntax highlighting, and code generation.
|
|
6
|
+
*/
|
|
7
|
+
export const SNIPPET_PRESETS = [
|
|
8
|
+
{
|
|
9
|
+
key: 'bash',
|
|
10
|
+
aliases: ['curl', 'sh', 'shell'],
|
|
11
|
+
displayName: 'cURL',
|
|
12
|
+
shikiLanguage: 'bash',
|
|
13
|
+
httpSnippet: { target: 'shell' },
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
key: 'python',
|
|
17
|
+
aliases: ['py'],
|
|
18
|
+
displayName: 'Python',
|
|
19
|
+
shikiLanguage: 'python',
|
|
20
|
+
httpSnippet: { target: 'python', client: 'requests' },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'javascript',
|
|
24
|
+
aliases: ['js'],
|
|
25
|
+
displayName: 'JavaScript',
|
|
26
|
+
shikiLanguage: 'javascript',
|
|
27
|
+
httpSnippet: { target: 'javascript', client: 'fetch' },
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: 'node',
|
|
31
|
+
aliases: ['nodejs', 'node.js'],
|
|
32
|
+
displayName: 'Node.js',
|
|
33
|
+
iconKey: 'node',
|
|
34
|
+
shikiLanguage: 'javascript',
|
|
35
|
+
httpSnippet: { target: 'node', client: 'fetch' },
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: 'php',
|
|
39
|
+
displayName: 'PHP',
|
|
40
|
+
shikiLanguage: 'php',
|
|
41
|
+
httpSnippet: { target: 'php', client: 'curl' },
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: 'go',
|
|
45
|
+
aliases: ['golang'],
|
|
46
|
+
displayName: 'Go',
|
|
47
|
+
shikiLanguage: 'go',
|
|
48
|
+
httpSnippet: { target: 'go' },
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'java',
|
|
52
|
+
displayName: 'Java',
|
|
53
|
+
shikiLanguage: 'java',
|
|
54
|
+
httpSnippet: { target: 'java' },
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: 'ruby',
|
|
58
|
+
aliases: ['rb'],
|
|
59
|
+
displayName: 'Ruby',
|
|
60
|
+
shikiLanguage: 'ruby',
|
|
61
|
+
httpSnippet: { target: 'ruby' },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: 'powershell',
|
|
65
|
+
displayName: 'PowerShell',
|
|
66
|
+
shikiLanguage: 'bash', // shiki doesn't have powershell in our LANGS
|
|
67
|
+
httpSnippet: { target: 'powershell' },
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: 'swift',
|
|
71
|
+
displayName: 'Swift',
|
|
72
|
+
shikiLanguage: 'swift',
|
|
73
|
+
httpSnippet: { target: 'swift' },
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'csharp',
|
|
77
|
+
aliases: ['c#'],
|
|
78
|
+
displayName: 'C#',
|
|
79
|
+
shikiLanguage: 'csharp',
|
|
80
|
+
httpSnippet: { target: 'csharp', client: 'restsharp' },
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
key: 'dotnet',
|
|
84
|
+
aliases: ['.net', '.NET', 'dotnet', 'dot-net'],
|
|
85
|
+
displayName: '.NET',
|
|
86
|
+
iconKey: 'dot-net',
|
|
87
|
+
shikiLanguage: 'csharp',
|
|
88
|
+
httpSnippet: { target: 'csharp', client: 'restsharp' },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: 'typescript',
|
|
92
|
+
aliases: ['ts'],
|
|
93
|
+
displayName: 'TypeScript',
|
|
94
|
+
shikiLanguage: 'typescript',
|
|
95
|
+
httpSnippet: { target: 'javascript', client: 'fetch' },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: 'c',
|
|
99
|
+
displayName: 'C',
|
|
100
|
+
shikiLanguage: 'c',
|
|
101
|
+
httpSnippet: { target: 'c' },
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: 'c++',
|
|
105
|
+
aliases: ['cpp'],
|
|
106
|
+
displayName: 'C++',
|
|
107
|
+
shikiLanguage: 'c++',
|
|
108
|
+
iconKey: 'cplusplus',
|
|
109
|
+
httpSnippet: { target: 'c' },
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: 'kotlin',
|
|
113
|
+
aliases: ['kt'],
|
|
114
|
+
displayName: 'Kotlin',
|
|
115
|
+
shikiLanguage: 'kotlin',
|
|
116
|
+
httpSnippet: { target: 'kotlin' },
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
key: 'rust',
|
|
120
|
+
aliases: ['rs'],
|
|
121
|
+
displayName: 'Rust',
|
|
122
|
+
shikiLanguage: 'rust',
|
|
123
|
+
httpSnippet: { target: 'rust' },
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: 'dart',
|
|
127
|
+
aliases: ['flutter'],
|
|
128
|
+
displayName: 'Dart',
|
|
129
|
+
shikiLanguage: 'dart',
|
|
130
|
+
httpSnippet: { target: 'dart' },
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
const presetLookup = new Map();
|
|
134
|
+
for (const preset of SNIPPET_PRESETS) {
|
|
135
|
+
presetLookup.set(preset.key.toLowerCase(), preset);
|
|
136
|
+
for (const alias of (_a = preset.aliases) !== null && _a !== void 0 ? _a : []) {
|
|
137
|
+
presetLookup.set(alias.toLowerCase(), preset);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export const getPreset = (lang) => presetLookup.get(lang.toLowerCase());
|
|
141
|
+
export const getIconKey = (lang) => {
|
|
142
|
+
var _a;
|
|
143
|
+
const preset = getPreset(lang);
|
|
144
|
+
return preset ? ((_a = preset.iconKey) !== null && _a !== void 0 ? _a : preset.key) : lang.toLowerCase();
|
|
145
|
+
};
|
|
146
|
+
export const getLanguageIconUrl = (language) => {
|
|
147
|
+
const iconKey = getIconKey(language);
|
|
148
|
+
return iconKey ? `${MINTLIFY_ICONS_CDN_URL}/devicon/${iconKey}.svg` : null;
|
|
149
|
+
};
|