skrypt-ai 0.4.2 → 0.6.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/dist/auth/index.d.ts +13 -3
- package/dist/auth/index.js +101 -9
- package/dist/auth/keychain.d.ts +5 -0
- package/dist/auth/keychain.js +82 -0
- package/dist/auth/notices.d.ts +3 -0
- package/dist/auth/notices.js +42 -0
- package/dist/autofix/index.d.ts +0 -4
- package/dist/autofix/index.js +10 -24
- package/dist/capture/browser.d.ts +11 -0
- package/dist/capture/browser.js +173 -0
- package/dist/capture/diff.d.ts +23 -0
- package/dist/capture/diff.js +52 -0
- package/dist/capture/index.d.ts +23 -0
- package/dist/capture/index.js +210 -0
- package/dist/capture/naming.d.ts +17 -0
- package/dist/capture/naming.js +45 -0
- package/dist/capture/parser.d.ts +15 -0
- package/dist/capture/parser.js +80 -0
- package/dist/capture/types.d.ts +57 -0
- package/dist/capture/types.js +1 -0
- package/dist/cli.js +20 -3
- package/dist/commands/autofix.js +136 -120
- package/dist/commands/cron.js +58 -47
- package/dist/commands/deploy.js +123 -102
- package/dist/commands/generate.js +125 -7
- package/dist/commands/heal.d.ts +10 -0
- package/dist/commands/heal.js +201 -0
- package/dist/commands/i18n.js +146 -111
- package/dist/commands/import.d.ts +2 -0
- package/dist/commands/import.js +157 -0
- package/dist/commands/init.js +19 -7
- package/dist/commands/lint.js +50 -44
- package/dist/commands/llms-txt.js +59 -49
- package/dist/commands/login.js +63 -34
- package/dist/commands/mcp.js +6 -0
- package/dist/commands/monitor.js +13 -8
- package/dist/commands/qa.d.ts +2 -0
- package/dist/commands/qa.js +43 -0
- package/dist/commands/review-pr.js +108 -92
- package/dist/commands/sdk.js +128 -122
- package/dist/commands/security.d.ts +2 -0
- package/dist/commands/security.js +109 -0
- package/dist/commands/test.js +91 -92
- package/dist/commands/version.js +104 -75
- package/dist/commands/watch.js +130 -114
- package/dist/config/types.js +2 -2
- package/dist/context-hub/index.d.ts +23 -0
- package/dist/context-hub/index.js +179 -0
- package/dist/context-hub/mappings.d.ts +8 -0
- package/dist/context-hub/mappings.js +55 -0
- package/dist/context-hub/types.d.ts +33 -0
- package/dist/context-hub/types.js +1 -0
- package/dist/generator/generator.js +39 -6
- package/dist/generator/types.d.ts +7 -0
- package/dist/generator/writer.d.ts +3 -1
- package/dist/generator/writer.js +36 -7
- package/dist/importers/confluence.d.ts +5 -0
- package/dist/importers/confluence.js +137 -0
- package/dist/importers/detect.d.ts +20 -0
- package/dist/importers/detect.js +121 -0
- package/dist/importers/docusaurus.d.ts +5 -0
- package/dist/importers/docusaurus.js +279 -0
- package/dist/importers/gitbook.d.ts +5 -0
- package/dist/importers/gitbook.js +189 -0
- package/dist/importers/github.d.ts +8 -0
- package/dist/importers/github.js +99 -0
- package/dist/importers/index.d.ts +15 -0
- package/dist/importers/index.js +30 -0
- package/dist/importers/markdown.d.ts +6 -0
- package/dist/importers/markdown.js +105 -0
- package/dist/importers/mintlify.d.ts +5 -0
- package/dist/importers/mintlify.js +172 -0
- package/dist/importers/notion.d.ts +5 -0
- package/dist/importers/notion.js +174 -0
- package/dist/importers/readme.d.ts +5 -0
- package/dist/importers/readme.js +184 -0
- package/dist/importers/transform.d.ts +90 -0
- package/dist/importers/transform.js +457 -0
- package/dist/importers/types.d.ts +37 -0
- package/dist/importers/types.js +1 -0
- package/dist/llm/anthropic-client.d.ts +1 -0
- package/dist/llm/anthropic-client.js +3 -1
- package/dist/llm/index.d.ts +6 -4
- package/dist/llm/index.js +76 -261
- package/dist/llm/openai-client.d.ts +1 -0
- package/dist/llm/openai-client.js +7 -2
- package/dist/plugins/index.js +7 -0
- package/dist/qa/checks.d.ts +10 -0
- package/dist/qa/checks.js +492 -0
- package/dist/qa/fixes.d.ts +30 -0
- package/dist/qa/fixes.js +277 -0
- package/dist/qa/index.d.ts +29 -0
- package/dist/qa/index.js +187 -0
- package/dist/qa/types.d.ts +24 -0
- package/dist/qa/types.js +1 -0
- package/dist/scanner/csharp.d.ts +23 -0
- package/dist/scanner/csharp.js +421 -0
- package/dist/scanner/index.js +53 -26
- package/dist/scanner/java.d.ts +39 -0
- package/dist/scanner/java.js +318 -0
- package/dist/scanner/kotlin.d.ts +23 -0
- package/dist/scanner/kotlin.js +389 -0
- package/dist/scanner/php.d.ts +57 -0
- package/dist/scanner/php.js +351 -0
- package/dist/scanner/python.js +17 -0
- package/dist/scanner/ruby.d.ts +36 -0
- package/dist/scanner/ruby.js +431 -0
- package/dist/scanner/swift.d.ts +25 -0
- package/dist/scanner/swift.js +392 -0
- package/dist/scanner/types.d.ts +1 -1
- package/dist/template/content/docs/_navigation.json +46 -0
- package/dist/template/content/docs/_sidebars.json +684 -0
- package/dist/template/content/docs/core.md +4544 -0
- package/dist/template/content/docs/index.mdx +89 -0
- package/dist/template/content/docs/integrations.md +1158 -0
- package/dist/template/content/docs/llms-full.md +403 -0
- package/dist/template/content/docs/llms.txt +4588 -0
- package/dist/template/content/docs/other.md +10379 -0
- package/dist/template/content/docs/tools.md +746 -0
- package/dist/template/content/docs/types.md +531 -0
- package/dist/template/docs.json +13 -11
- package/dist/template/mdx-components.tsx +27 -2
- package/dist/template/package.json +6 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +149 -13
- package/dist/template/src/app/api/chat/route.ts +83 -128
- package/dist/template/src/app/docs/[...slug]/page.tsx +75 -20
- package/dist/template/src/app/docs/llms-full.md +151 -4
- package/dist/template/src/app/docs/llms.txt +2464 -847
- package/dist/template/src/app/docs/page.mdx +48 -38
- package/dist/template/src/app/layout.tsx +3 -1
- package/dist/template/src/app/page.tsx +22 -8
- package/dist/template/src/components/ai-chat.tsx +73 -64
- package/dist/template/src/components/breadcrumbs.tsx +21 -23
- package/dist/template/src/components/copy-button.tsx +13 -9
- package/dist/template/src/components/copy-page-button.tsx +54 -0
- package/dist/template/src/components/docs-layout.tsx +37 -25
- package/dist/template/src/components/header.tsx +51 -10
- package/dist/template/src/components/mdx/card.tsx +17 -3
- package/dist/template/src/components/mdx/code-block.tsx +13 -9
- package/dist/template/src/components/mdx/code-group.tsx +13 -8
- package/dist/template/src/components/mdx/heading.tsx +15 -2
- package/dist/template/src/components/mdx/highlighted-code.tsx +13 -8
- package/dist/template/src/components/mdx/index.tsx +2 -0
- package/dist/template/src/components/mdx/mermaid.tsx +110 -0
- package/dist/template/src/components/mdx/screenshot.tsx +150 -0
- package/dist/template/src/components/scroll-to-hash.tsx +48 -0
- package/dist/template/src/components/sidebar.tsx +12 -18
- package/dist/template/src/components/table-of-contents.tsx +9 -0
- package/dist/template/src/lib/highlight.ts +3 -88
- package/dist/template/src/lib/navigation.ts +159 -0
- package/dist/template/src/lib/search-types.ts +4 -1
- package/dist/template/src/lib/search.ts +30 -7
- package/dist/template/src/styles/globals.css +17 -6
- package/dist/utils/files.d.ts +9 -1
- package/dist/utils/files.js +59 -10
- package/dist/utils/validation.d.ts +0 -3
- package/dist/utils/validation.js +0 -26
- package/package.json +5 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { join, relative, basename, extname } from 'path';
|
|
3
|
+
import { findMdxFiles } from '../utils/files.js';
|
|
4
|
+
import { transformReadmeCallouts, transformReadmeCodeBlocks, normalizeFrontmatter, } from './transform.js';
|
|
5
|
+
/**
|
|
6
|
+
* Import ReadMe.io documentation export.
|
|
7
|
+
*/
|
|
8
|
+
export function importReadme(dir, name) {
|
|
9
|
+
const result = createEmptyResult(name);
|
|
10
|
+
const stats = { callouts: 0, tabs: 0, codeGroups: 0, steps: 0, accordions: 0, images: 0, other: 0 };
|
|
11
|
+
// ReadMe organizes by category folders with _order.yaml
|
|
12
|
+
const categories = discoverCategories(dir);
|
|
13
|
+
for (const category of categories) {
|
|
14
|
+
const pages = [];
|
|
15
|
+
for (const file of category.files) {
|
|
16
|
+
const page = processReadmePage(dir, file, stats, result);
|
|
17
|
+
if (page)
|
|
18
|
+
pages.push(page);
|
|
19
|
+
}
|
|
20
|
+
if (pages.length > 0) {
|
|
21
|
+
result.navigation.push({ group: category.label, pages });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Handle CDN images (files.readme.io)
|
|
25
|
+
flagCdnImages(result, stats);
|
|
26
|
+
result.stats = {
|
|
27
|
+
pages: result.files.size,
|
|
28
|
+
groups: result.navigation.length,
|
|
29
|
+
transforms: stats,
|
|
30
|
+
};
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function discoverCategories(dir) {
|
|
34
|
+
const categories = [];
|
|
35
|
+
// Check for category subdirectories
|
|
36
|
+
try {
|
|
37
|
+
const entries = readdirSync(dir);
|
|
38
|
+
const hasSubdirs = entries.some(e => {
|
|
39
|
+
try {
|
|
40
|
+
return statSync(join(dir, e)).isDirectory() && !e.startsWith('.');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
if (hasSubdirs) {
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
const fullPath = join(dir, entry);
|
|
49
|
+
try {
|
|
50
|
+
if (!statSync(fullPath).isDirectory() || entry.startsWith('.') || entry === 'node_modules')
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const files = findMdxFiles(fullPath);
|
|
57
|
+
if (files.length === 0)
|
|
58
|
+
continue;
|
|
59
|
+
// Check _order.yaml
|
|
60
|
+
const orderPath = join(fullPath, '_order.yaml');
|
|
61
|
+
let orderedFiles = files;
|
|
62
|
+
const position = Infinity;
|
|
63
|
+
if (existsSync(orderPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const orderContent = readFileSync(orderPath, 'utf-8');
|
|
66
|
+
const order = orderContent.split('\n').map(l => l.replace(/^-\s*/, '').trim()).filter(Boolean);
|
|
67
|
+
orderedFiles = orderFiles(files, order, fullPath);
|
|
68
|
+
}
|
|
69
|
+
catch { /* skip */ }
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
orderedFiles = sortByFrontmatterOrder(files);
|
|
73
|
+
}
|
|
74
|
+
const label = entry.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
75
|
+
categories.push({ label, files: orderedFiles, position });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Flat structure — all files in root
|
|
80
|
+
const files = findMdxFiles(dir);
|
|
81
|
+
if (files.length > 0) {
|
|
82
|
+
categories.push({ label: 'Documentation', files: sortByFrontmatterOrder(files), position: 0 });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* skip */ }
|
|
87
|
+
return categories.sort((a, b) => a.position - b.position);
|
|
88
|
+
}
|
|
89
|
+
function orderFiles(files, order, _dir) {
|
|
90
|
+
const ordered = [];
|
|
91
|
+
for (const slug of order) {
|
|
92
|
+
const match = files.find(f => {
|
|
93
|
+
const name = basename(f, extname(f));
|
|
94
|
+
return name === slug || name.toLowerCase() === slug.toLowerCase();
|
|
95
|
+
});
|
|
96
|
+
if (match)
|
|
97
|
+
ordered.push(match);
|
|
98
|
+
}
|
|
99
|
+
// Add remaining files not in order
|
|
100
|
+
for (const f of files) {
|
|
101
|
+
if (!ordered.includes(f))
|
|
102
|
+
ordered.push(f);
|
|
103
|
+
}
|
|
104
|
+
return ordered;
|
|
105
|
+
}
|
|
106
|
+
function sortByFrontmatterOrder(files) {
|
|
107
|
+
return [...files].sort((a, b) => {
|
|
108
|
+
try {
|
|
109
|
+
const aContent = readFileSync(a, 'utf-8');
|
|
110
|
+
const bContent = readFileSync(b, 'utf-8');
|
|
111
|
+
const aOrder = extractFmField(aContent, 'order');
|
|
112
|
+
const bOrder = extractFmField(bContent, 'order');
|
|
113
|
+
const aNum = aOrder !== null ? Number(aOrder) : Infinity;
|
|
114
|
+
const bNum = bOrder !== null ? Number(bOrder) : Infinity;
|
|
115
|
+
if (aNum !== bNum)
|
|
116
|
+
return aNum - bNum;
|
|
117
|
+
}
|
|
118
|
+
catch { /* skip */ }
|
|
119
|
+
return basename(a).localeCompare(basename(b));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function extractFmField(content, field) {
|
|
123
|
+
const match = content.match(new RegExp(`^---\\n[\\s\\S]*?${field}:\\s*([^\\n]+)\\n[\\s\\S]*?---`, 'm'));
|
|
124
|
+
return match ? match[1].trim() : null;
|
|
125
|
+
}
|
|
126
|
+
function processReadmePage(dir, filePath, stats, result) {
|
|
127
|
+
let content = readFileSync(filePath, 'utf-8');
|
|
128
|
+
const originalContent = content;
|
|
129
|
+
// Apply ReadMe transforms
|
|
130
|
+
content = transformReadmeCallouts(content);
|
|
131
|
+
content = transformReadmeCodeBlocks(content);
|
|
132
|
+
// Map ReadMe-specific frontmatter
|
|
133
|
+
content = content.replace(/^---\n([\s\S]*?)\n---/, (match, fmStr) => {
|
|
134
|
+
const mapped = fmStr.replace(/^excerpt:\s*/m, 'description: ');
|
|
135
|
+
return `---\n${mapped}\n---`;
|
|
136
|
+
});
|
|
137
|
+
content = normalizeFrontmatter(content);
|
|
138
|
+
stats.callouts += countNew(originalContent, content, '<Callout');
|
|
139
|
+
stats.codeGroups += countNew(originalContent, content, '<CodeGroup');
|
|
140
|
+
const title = extractTitle(content, filePath);
|
|
141
|
+
const rel = relative(dir, filePath);
|
|
142
|
+
const slug = rel.replace(/\.(md|mdx)$/, '').replace(/\\/g, '/');
|
|
143
|
+
const outputPath = `content/docs/${slug}.mdx`;
|
|
144
|
+
result.files.set(outputPath, content);
|
|
145
|
+
return { title, slug, sourcePath: rel, content };
|
|
146
|
+
}
|
|
147
|
+
function flagCdnImages(result, stats) {
|
|
148
|
+
for (const [, content] of result.files) {
|
|
149
|
+
const cdnMatches = content.match(/https?:\/\/files\.readme\.io\/[^\s)]+/g);
|
|
150
|
+
if (cdnMatches) {
|
|
151
|
+
for (const url of cdnMatches) {
|
|
152
|
+
result.warnings.push(`CDN image found (manual download needed): ${url}`);
|
|
153
|
+
stats.images++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function extractTitle(content, filePath) {
|
|
159
|
+
const fmMatch = content.match(/^---\n[\s\S]*?title:\s*["']?([^"'\n]+)["']?\s*\n[\s\S]*?---/m);
|
|
160
|
+
if (fmMatch)
|
|
161
|
+
return fmMatch[1].trim();
|
|
162
|
+
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
163
|
+
if (h1Match)
|
|
164
|
+
return h1Match[1].trim();
|
|
165
|
+
return basename(filePath, extname(filePath)).replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
166
|
+
}
|
|
167
|
+
function countNew(original, transformed, marker) {
|
|
168
|
+
const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
169
|
+
const origCount = (original.match(new RegExp(escaped, 'g')) || []).length;
|
|
170
|
+
const newCount = (transformed.match(new RegExp(escaped, 'g')) || []).length;
|
|
171
|
+
return Math.max(0, newCount - origCount);
|
|
172
|
+
}
|
|
173
|
+
function createEmptyResult(name) {
|
|
174
|
+
return {
|
|
175
|
+
navigation: [],
|
|
176
|
+
name: name || 'Documentation',
|
|
177
|
+
description: 'Imported from ReadMe',
|
|
178
|
+
files: new Map(),
|
|
179
|
+
assets: new Map(),
|
|
180
|
+
warnings: [],
|
|
181
|
+
stats: { pages: 0, groups: 0, transforms: { callouts: 0, tabs: 0, codeGroups: 0, steps: 0, accordions: 0, images: 0, other: 0 } },
|
|
182
|
+
sourceFormat: 'readme',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mintlify: <Note>content</Note>, <Warning>, <Tip>, <Info>, <Check>
|
|
3
|
+
*/
|
|
4
|
+
export declare function transformMintlifyCallouts(content: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Docusaurus: :::note[Title]\ncontent\n:::
|
|
7
|
+
*/
|
|
8
|
+
export declare function transformDocusaurusAdmonitions(content: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* GitBook: {% hint style="info" %}\ncontent\n{% endhint %}
|
|
11
|
+
*/
|
|
12
|
+
export declare function transformGitBookHints(content: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* ReadMe RDMD: > 📘 Title\n> content lines
|
|
15
|
+
*/
|
|
16
|
+
export declare function transformReadmeCallouts(content: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Notion: <aside>content</aside> and :::callout
|
|
19
|
+
*/
|
|
20
|
+
export declare function transformNotionCallouts(content: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Confluence: <ac:structured-macro ac:name="info">...<ac:rich-text-body>content</ac:rich-text-body>
|
|
23
|
+
*/
|
|
24
|
+
export declare function transformConfluenceCallouts(content: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Mintlify: <Tabs><Tab title="X">content</Tab></Tabs>
|
|
27
|
+
*/
|
|
28
|
+
export declare function transformMintlifyTabs(content: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Docusaurus: <Tabs><TabItem value="x" label="X">content</TabItem></Tabs>
|
|
31
|
+
*/
|
|
32
|
+
export declare function transformDocusaurusTabs(content: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* GitBook: {% tabs %}{% tab title="X" %}content{% endtab %}{% endtabs %}
|
|
35
|
+
*/
|
|
36
|
+
export declare function transformGitBookTabs(content: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* ReadMe [block:code] JSON → <CodeGroup>
|
|
39
|
+
*/
|
|
40
|
+
export declare function transformReadmeCodeBlocks(content: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* GitBook: {% stepper %}{% step %}...{% endstep %}{% endstepper %}
|
|
43
|
+
*/
|
|
44
|
+
export declare function transformGitBookSteps(content: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* GitBook: {% expandable title="X" %}content{% endexpandable %}
|
|
47
|
+
*/
|
|
48
|
+
export declare function transformGitBookExpandable(content: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* GitBook: {% content-ref url="path" %} → markdown link
|
|
51
|
+
*/
|
|
52
|
+
export declare function transformGitBookContentRef(content: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* GitBook: {% embed url="..." %} → plain URL
|
|
55
|
+
*/
|
|
56
|
+
export declare function transformGitBookEmbed(content: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Notion: <details><summary>Title</summary>content</details> → <Accordion>
|
|
59
|
+
*/
|
|
60
|
+
export declare function transformNotionToggles(content: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Confluence: Convert common HTML and AC macros to markdown
|
|
63
|
+
*/
|
|
64
|
+
export declare function transformConfluenceHtml(content: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Docusaurus: Strip import ... from '@theme/...' statements
|
|
67
|
+
*/
|
|
68
|
+
export declare function stripDocusaurusImports(content: string): string;
|
|
69
|
+
interface FrontmatterDefaults {
|
|
70
|
+
title?: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Normalize frontmatter fields to Skrypt format
|
|
75
|
+
*/
|
|
76
|
+
export declare function normalizeFrontmatter(content: string, defaults?: FrontmatterDefaults): string;
|
|
77
|
+
/**
|
|
78
|
+
* Rewrite image paths based on asset copy mapping
|
|
79
|
+
*/
|
|
80
|
+
export declare function rewriteImagePaths(content: string, mapping: Map<string, string>): string;
|
|
81
|
+
/**
|
|
82
|
+
* Strip 32-char hex UUID suffixes from Notion filenames
|
|
83
|
+
* e.g., "Getting Started a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4.md" → "Getting Started.md"
|
|
84
|
+
*/
|
|
85
|
+
export declare function stripNotionUUIDs(filename: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* Get sorting weight from frontmatter
|
|
88
|
+
*/
|
|
89
|
+
export declare function getSortWeight(content: string): number;
|
|
90
|
+
export {};
|