archicore 0.2.8 → 0.2.9
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/cli/commands/analyzers.js +8 -1
- package/dist/cli/commands/projects.js +25 -1
- package/dist/cli/utils/session.js +29 -12
- package/dist/cli/utils/upload-utils.js +45 -41
- package/dist/code-index/ast-parser.js +169 -1
- package/dist/server/index.js +4 -3
- package/dist/server/routes/api.js +248 -2
- package/dist/server/routes/auth.js +2 -4
- package/dist/server/routes/github.js +0 -17
- package/dist/server/services/project-service.d.ts +3 -1
- package/dist/server/services/project-service.js +17 -6
- package/dist/server/services/task-queue.d.ts +98 -0
- package/dist/server/services/task-queue.js +240 -0
- package/dist/types/user.d.ts +0 -1
- package/dist/types/user.js +0 -5
- package/dist/utils/file-utils.js +122 -36
- package/package.json +1 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Queue Service
|
|
3
|
+
*
|
|
4
|
+
* Асинхронная очередь задач для параллельного выполнения анализа
|
|
5
|
+
* Позволяет множеству пользователей запускать анализ одновременно
|
|
6
|
+
*/
|
|
7
|
+
import { Logger } from '../../utils/logger.js';
|
|
8
|
+
import { EventEmitter } from 'events';
|
|
9
|
+
class TaskQueueService extends EventEmitter {
|
|
10
|
+
tasks = new Map();
|
|
11
|
+
executors = new Map();
|
|
12
|
+
runningTasks = new Set();
|
|
13
|
+
maxConcurrentTasks = 10; // Максимум параллельных задач
|
|
14
|
+
taskTimeout = 10 * 60 * 1000; // 10 минут таймаут
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
// Очистка старых задач каждые 30 минут
|
|
18
|
+
setInterval(() => this.cleanupOldTasks(), 30 * 60 * 1000);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Регистрация исполнителя задач
|
|
22
|
+
*/
|
|
23
|
+
registerExecutor(taskType, executor) {
|
|
24
|
+
this.executors.set(taskType, executor);
|
|
25
|
+
Logger.info(`Task executor registered: ${taskType}`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Создание новой задачи
|
|
29
|
+
*/
|
|
30
|
+
createTask(type, projectId, userId) {
|
|
31
|
+
const id = `task_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
|
|
32
|
+
const task = {
|
|
33
|
+
id,
|
|
34
|
+
type,
|
|
35
|
+
projectId,
|
|
36
|
+
userId,
|
|
37
|
+
status: 'pending',
|
|
38
|
+
progress: {
|
|
39
|
+
phase: 'queued',
|
|
40
|
+
current: 0,
|
|
41
|
+
total: 100,
|
|
42
|
+
message: 'Task queued for execution',
|
|
43
|
+
},
|
|
44
|
+
createdAt: Date.now(),
|
|
45
|
+
};
|
|
46
|
+
this.tasks.set(id, task);
|
|
47
|
+
Logger.info(`Task created: ${id} (type: ${type}, project: ${projectId})`);
|
|
48
|
+
// Запускаем обработку очереди
|
|
49
|
+
this.processQueue();
|
|
50
|
+
return task;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Получение задачи по ID
|
|
54
|
+
*/
|
|
55
|
+
getTask(taskId) {
|
|
56
|
+
return this.tasks.get(taskId);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Получение задач пользователя
|
|
60
|
+
*/
|
|
61
|
+
getUserTasks(userId, limit = 10) {
|
|
62
|
+
const userTasks = [];
|
|
63
|
+
for (const task of this.tasks.values()) {
|
|
64
|
+
if (task.userId === userId) {
|
|
65
|
+
userTasks.push(task);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return userTasks
|
|
69
|
+
.sort((a, b) => b.createdAt - a.createdAt)
|
|
70
|
+
.slice(0, limit);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Получение задач проекта
|
|
74
|
+
*/
|
|
75
|
+
getProjectTasks(projectId, limit = 10) {
|
|
76
|
+
const projectTasks = [];
|
|
77
|
+
for (const task of this.tasks.values()) {
|
|
78
|
+
if (task.projectId === projectId) {
|
|
79
|
+
projectTasks.push(task);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return projectTasks
|
|
83
|
+
.sort((a, b) => b.createdAt - a.createdAt)
|
|
84
|
+
.slice(0, limit);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Обработка очереди задач
|
|
88
|
+
*/
|
|
89
|
+
async processQueue() {
|
|
90
|
+
// Находим pending задачи
|
|
91
|
+
const pendingTasks = Array.from(this.tasks.values())
|
|
92
|
+
.filter(t => t.status === 'pending')
|
|
93
|
+
.sort((a, b) => a.createdAt - b.createdAt);
|
|
94
|
+
for (const task of pendingTasks) {
|
|
95
|
+
// Проверяем лимит параллельных задач
|
|
96
|
+
if (this.runningTasks.size >= this.maxConcurrentTasks) {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
// Запускаем задачу
|
|
100
|
+
this.executeTask(task);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Выполнение задачи
|
|
105
|
+
*/
|
|
106
|
+
async executeTask(task) {
|
|
107
|
+
const executor = this.executors.get(task.type);
|
|
108
|
+
if (!executor) {
|
|
109
|
+
this.failTask(task.id, `Unknown task type: ${task.type}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Помечаем как running
|
|
113
|
+
task.status = 'running';
|
|
114
|
+
task.startedAt = Date.now();
|
|
115
|
+
task.progress = {
|
|
116
|
+
phase: 'starting',
|
|
117
|
+
current: 0,
|
|
118
|
+
total: 100,
|
|
119
|
+
message: 'Task started',
|
|
120
|
+
};
|
|
121
|
+
this.runningTasks.add(task.id);
|
|
122
|
+
this.emit('taskStarted', task);
|
|
123
|
+
Logger.info(`Task started: ${task.id}`);
|
|
124
|
+
// Устанавливаем таймаут
|
|
125
|
+
const timeoutId = setTimeout(() => {
|
|
126
|
+
if (task.status === 'running') {
|
|
127
|
+
this.failTask(task.id, 'Task timeout exceeded');
|
|
128
|
+
}
|
|
129
|
+
}, this.taskTimeout);
|
|
130
|
+
try {
|
|
131
|
+
// Выполняем задачу
|
|
132
|
+
const result = await executor(task, (progress) => {
|
|
133
|
+
this.updateTaskProgress(task.id, progress);
|
|
134
|
+
});
|
|
135
|
+
clearTimeout(timeoutId);
|
|
136
|
+
if (result.success) {
|
|
137
|
+
this.completeTask(task.id, result.data);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
this.failTask(task.id, result.error || 'Task failed');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
clearTimeout(timeoutId);
|
|
145
|
+
this.failTask(task.id, error instanceof Error ? error.message : String(error));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Обновление прогресса задачи
|
|
150
|
+
*/
|
|
151
|
+
updateTaskProgress(taskId, progress) {
|
|
152
|
+
const task = this.tasks.get(taskId);
|
|
153
|
+
if (!task)
|
|
154
|
+
return;
|
|
155
|
+
task.progress = { ...task.progress, ...progress };
|
|
156
|
+
this.emit('taskProgress', task);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Завершение задачи успешно
|
|
160
|
+
*/
|
|
161
|
+
completeTask(taskId, result) {
|
|
162
|
+
const task = this.tasks.get(taskId);
|
|
163
|
+
if (!task)
|
|
164
|
+
return;
|
|
165
|
+
task.status = 'completed';
|
|
166
|
+
task.completedAt = Date.now();
|
|
167
|
+
task.result = result;
|
|
168
|
+
task.progress = {
|
|
169
|
+
phase: 'completed',
|
|
170
|
+
current: 100,
|
|
171
|
+
total: 100,
|
|
172
|
+
message: 'Task completed successfully',
|
|
173
|
+
};
|
|
174
|
+
this.runningTasks.delete(taskId);
|
|
175
|
+
this.emit('taskCompleted', task);
|
|
176
|
+
const duration = task.completedAt - (task.startedAt || task.createdAt);
|
|
177
|
+
Logger.success(`Task completed: ${taskId} (${duration}ms)`);
|
|
178
|
+
// Запускаем следующие задачи из очереди
|
|
179
|
+
this.processQueue();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Завершение задачи с ошибкой
|
|
183
|
+
*/
|
|
184
|
+
failTask(taskId, error) {
|
|
185
|
+
const task = this.tasks.get(taskId);
|
|
186
|
+
if (!task)
|
|
187
|
+
return;
|
|
188
|
+
task.status = 'failed';
|
|
189
|
+
task.completedAt = Date.now();
|
|
190
|
+
task.error = error;
|
|
191
|
+
task.progress = {
|
|
192
|
+
phase: 'failed',
|
|
193
|
+
current: 0,
|
|
194
|
+
total: 100,
|
|
195
|
+
message: error,
|
|
196
|
+
};
|
|
197
|
+
this.runningTasks.delete(taskId);
|
|
198
|
+
this.emit('taskFailed', task);
|
|
199
|
+
Logger.error(`Task failed: ${taskId} - ${error}`);
|
|
200
|
+
// Запускаем следующие задачи из очереди
|
|
201
|
+
this.processQueue();
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Очистка старых задач (старше 1 часа)
|
|
205
|
+
*/
|
|
206
|
+
cleanupOldTasks() {
|
|
207
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
208
|
+
let cleaned = 0;
|
|
209
|
+
for (const [id, task] of this.tasks.entries()) {
|
|
210
|
+
// Удаляем завершённые задачи старше 1 часа
|
|
211
|
+
if ((task.status === 'completed' || task.status === 'failed') &&
|
|
212
|
+
(task.completedAt || task.createdAt) < oneHourAgo) {
|
|
213
|
+
this.tasks.delete(id);
|
|
214
|
+
cleaned++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (cleaned > 0) {
|
|
218
|
+
Logger.info(`Cleaned up ${cleaned} old tasks`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Статистика очереди
|
|
223
|
+
*/
|
|
224
|
+
getStats() {
|
|
225
|
+
const stats = {
|
|
226
|
+
total: this.tasks.size,
|
|
227
|
+
pending: 0,
|
|
228
|
+
running: 0,
|
|
229
|
+
completed: 0,
|
|
230
|
+
failed: 0,
|
|
231
|
+
};
|
|
232
|
+
for (const task of this.tasks.values()) {
|
|
233
|
+
stats[task.status]++;
|
|
234
|
+
}
|
|
235
|
+
return stats;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Singleton instance
|
|
239
|
+
export const taskQueue = new TaskQueueService();
|
|
240
|
+
//# sourceMappingURL=task-queue.js.map
|
package/dist/types/user.d.ts
CHANGED
package/dist/types/user.js
CHANGED
|
@@ -6,7 +6,6 @@ export const TIER_LIMITS = {
|
|
|
6
6
|
requestsPerDay: 10,
|
|
7
7
|
fullAnalysisPerDay: 1,
|
|
8
8
|
projectsPerDay: 3,
|
|
9
|
-
maxProjectSizeMB: 30,
|
|
10
9
|
retentionDays: 7,
|
|
11
10
|
price: 0,
|
|
12
11
|
priceYearly: 0
|
|
@@ -15,7 +14,6 @@ export const TIER_LIMITS = {
|
|
|
15
14
|
requestsPerDay: 50,
|
|
16
15
|
fullAnalysisPerDay: 5,
|
|
17
16
|
projectsPerDay: 10,
|
|
18
|
-
maxProjectSizeMB: 50,
|
|
19
17
|
retentionDays: 14,
|
|
20
18
|
price: 39,
|
|
21
19
|
priceYearly: 390 // ~2 months free
|
|
@@ -24,7 +22,6 @@ export const TIER_LIMITS = {
|
|
|
24
22
|
requestsPerDay: 150,
|
|
25
23
|
fullAnalysisPerDay: 15,
|
|
26
24
|
projectsPerDay: 20,
|
|
27
|
-
maxProjectSizeMB: 80,
|
|
28
25
|
retentionDays: 30,
|
|
29
26
|
price: 99,
|
|
30
27
|
priceYearly: 990 // ~2 months free
|
|
@@ -33,7 +30,6 @@ export const TIER_LIMITS = {
|
|
|
33
30
|
requestsPerDay: Infinity,
|
|
34
31
|
fullAnalysisPerDay: Infinity,
|
|
35
32
|
projectsPerDay: Infinity,
|
|
36
|
-
maxProjectSizeMB: 500,
|
|
37
33
|
retentionDays: 365,
|
|
38
34
|
price: -1, // Custom pricing
|
|
39
35
|
priceYearly: -1
|
|
@@ -42,7 +38,6 @@ export const TIER_LIMITS = {
|
|
|
42
38
|
requestsPerDay: Infinity,
|
|
43
39
|
fullAnalysisPerDay: Infinity,
|
|
44
40
|
projectsPerDay: Infinity,
|
|
45
|
-
maxProjectSizeMB: 1000,
|
|
46
41
|
retentionDays: 365,
|
|
47
42
|
price: 0,
|
|
48
43
|
priceYearly: 0
|
package/dist/utils/file-utils.js
CHANGED
|
@@ -12,24 +12,69 @@ export class FileUtils {
|
|
|
12
12
|
return stats.size;
|
|
13
13
|
}
|
|
14
14
|
static async getAllFiles(rootDir, patterns = [
|
|
15
|
-
|
|
16
|
-
'**/*.
|
|
17
|
-
'**/*.
|
|
15
|
+
// JavaScript/TypeScript ecosystem
|
|
16
|
+
'**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx', '**/*.mjs', '**/*.cjs',
|
|
17
|
+
'**/*.vue', '**/*.svelte', '**/*.astro',
|
|
18
|
+
// Python
|
|
19
|
+
'**/*.py', '**/*.pyw', '**/*.pyi',
|
|
20
|
+
// Systems languages
|
|
18
21
|
'**/*.go', // Go
|
|
19
22
|
'**/*.rs', // Rust
|
|
23
|
+
'**/*.zig', // Zig
|
|
24
|
+
'**/*.nim', // Nim
|
|
25
|
+
'**/*.c', '**/*.h', '**/*.cpp', '**/*.hpp', '**/*.cc', '**/*.cxx', '**/*.hh', // C/C++
|
|
26
|
+
// JVM languages
|
|
20
27
|
'**/*.java', // Java
|
|
21
|
-
'**/*.
|
|
22
|
-
'**/*.
|
|
28
|
+
'**/*.kt', '**/*.kts', // Kotlin
|
|
29
|
+
'**/*.scala', '**/*.sc', // Scala
|
|
30
|
+
'**/*.groovy', '**/*.gradle', // Groovy
|
|
31
|
+
'**/*.clj', '**/*.cljs', '**/*.cljc', // Clojure
|
|
32
|
+
// .NET languages
|
|
23
33
|
'**/*.cs', // C#
|
|
24
|
-
'**/*.
|
|
34
|
+
'**/*.fs', '**/*.fsx', // F#
|
|
35
|
+
'**/*.vb', // Visual Basic
|
|
36
|
+
// Web/scripting
|
|
37
|
+
'**/*.php', // PHP
|
|
38
|
+
'**/*.rb', '**/*.erb', '**/*.rake', // Ruby
|
|
39
|
+
'**/*.pl', '**/*.pm', // Perl
|
|
40
|
+
'**/*.lua', // Lua
|
|
41
|
+
// Mobile
|
|
42
|
+
'**/*.swift', // Swift
|
|
43
|
+
'**/*.dart', // Dart/Flutter
|
|
44
|
+
'**/*.m', '**/*.mm', // Objective-C
|
|
45
|
+
// Functional languages
|
|
46
|
+
'**/*.hs', '**/*.lhs', // Haskell
|
|
47
|
+
'**/*.ml', '**/*.mli', // OCaml
|
|
48
|
+
'**/*.erl', '**/*.hrl', // Erlang
|
|
49
|
+
'**/*.ex', '**/*.exs', // Elixir
|
|
50
|
+
'**/*.jl', // Julia
|
|
51
|
+
'**/*.r', '**/*.R', // R
|
|
52
|
+
// Other compiled
|
|
53
|
+
'**/*.cr', // Crystal
|
|
54
|
+
// Markup/styles
|
|
25
55
|
'**/*.html', '**/*.htm', // HTML
|
|
26
|
-
'**/*.css', '**/*.scss', '**/*.sass', '**/*.less', // CSS
|
|
56
|
+
'**/*.css', '**/*.scss', '**/*.sass', '**/*.less', '**/*.styl', // CSS
|
|
57
|
+
'**/*.xml', '**/*.xsl', '**/*.xslt', // XML
|
|
58
|
+
// Data/config
|
|
27
59
|
'**/*.json', // JSON
|
|
28
60
|
'**/*.yaml', '**/*.yml', // YAML
|
|
29
|
-
'**/*.
|
|
30
|
-
'**/*.
|
|
31
|
-
|
|
32
|
-
'**/*.
|
|
61
|
+
'**/*.toml', // TOML
|
|
62
|
+
'**/*.ini', '**/*.cfg', '**/*.conf', // INI/Config
|
|
63
|
+
// Database/query
|
|
64
|
+
'**/*.sql', '**/*.prisma', // SQL/Prisma
|
|
65
|
+
'**/*.graphql', '**/*.gql', // GraphQL
|
|
66
|
+
// Infrastructure
|
|
67
|
+
'**/*.tf', '**/*.tfvars', // Terraform
|
|
68
|
+
'**/*.proto', // Protobuf
|
|
69
|
+
'**/Dockerfile', '**/*.dockerfile', // Docker
|
|
70
|
+
'**/Makefile', '**/*.mk', // Make
|
|
71
|
+
'**/CMakeLists.txt', '**/*.cmake', // CMake
|
|
72
|
+
// Shell/scripting
|
|
73
|
+
'**/*.sh', '**/*.bash', '**/*.zsh', // Unix shell
|
|
74
|
+
'**/*.ps1', '**/*.psm1', '**/*.psd1', // PowerShell
|
|
75
|
+
'**/*.bat', '**/*.cmd', // Windows batch
|
|
76
|
+
// Documentation
|
|
77
|
+
'**/*.md', '**/*.markdown', '**/*.mdx', '**/*.rst' // Markdown/RST
|
|
33
78
|
]) {
|
|
34
79
|
// Resolve to absolute path
|
|
35
80
|
const resolvedPath = resolve(rootDir);
|
|
@@ -173,38 +218,79 @@ export class FileUtils {
|
|
|
173
218
|
}
|
|
174
219
|
static getLanguageFromExtension(filePath) {
|
|
175
220
|
const ext = extname(filePath).toLowerCase();
|
|
221
|
+
const filename = filePath.split(/[/\\]/).pop()?.toLowerCase() || '';
|
|
222
|
+
// Special filenames
|
|
223
|
+
if (filename === 'dockerfile' || filename.endsWith('.dockerfile'))
|
|
224
|
+
return 'dockerfile';
|
|
225
|
+
if (filename === 'makefile' || filename.endsWith('.mk'))
|
|
226
|
+
return 'make';
|
|
227
|
+
if (filename === 'cmakelists.txt' || filename.endsWith('.cmake'))
|
|
228
|
+
return 'cmake';
|
|
176
229
|
const map = {
|
|
177
|
-
|
|
178
|
-
'.tsx': 'typescript',
|
|
179
|
-
'.js': 'javascript',
|
|
180
|
-
'.
|
|
181
|
-
|
|
182
|
-
'.py': 'python',
|
|
230
|
+
// JavaScript/TypeScript
|
|
231
|
+
'.ts': 'typescript', '.tsx': 'typescript', '.mts': 'typescript', '.cts': 'typescript',
|
|
232
|
+
'.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
233
|
+
'.vue': 'vue', '.svelte': 'svelte', '.astro': 'astro',
|
|
234
|
+
// Python
|
|
235
|
+
'.py': 'python', '.pyw': 'python', '.pyi': 'python',
|
|
236
|
+
// Systems languages
|
|
183
237
|
'.go': 'go',
|
|
184
238
|
'.rs': 'rust',
|
|
239
|
+
'.zig': 'zig',
|
|
240
|
+
'.nim': 'nim',
|
|
241
|
+
'.c': 'c', '.h': 'c',
|
|
242
|
+
'.cpp': 'cpp', '.cc': 'cpp', '.cxx': 'cpp', '.hpp': 'cpp', '.hh': 'cpp', '.hxx': 'cpp',
|
|
243
|
+
// JVM languages
|
|
185
244
|
'.java': 'java',
|
|
186
|
-
'.
|
|
187
|
-
'.
|
|
245
|
+
'.kt': 'kotlin', '.kts': 'kotlin',
|
|
246
|
+
'.scala': 'scala', '.sc': 'scala',
|
|
247
|
+
'.groovy': 'groovy', '.gradle': 'groovy',
|
|
248
|
+
'.clj': 'clojure', '.cljs': 'clojure', '.cljc': 'clojure',
|
|
249
|
+
// .NET languages
|
|
188
250
|
'.cs': 'csharp',
|
|
189
|
-
'.
|
|
190
|
-
'.
|
|
191
|
-
|
|
192
|
-
'.
|
|
193
|
-
'.
|
|
194
|
-
'.
|
|
195
|
-
'.
|
|
196
|
-
|
|
197
|
-
'.
|
|
198
|
-
'.
|
|
251
|
+
'.fs': 'fsharp', '.fsx': 'fsharp', '.fsi': 'fsharp',
|
|
252
|
+
'.vb': 'visualbasic',
|
|
253
|
+
// Web/scripting
|
|
254
|
+
'.php': 'php', '.phtml': 'php',
|
|
255
|
+
'.rb': 'ruby', '.erb': 'ruby', '.rake': 'ruby',
|
|
256
|
+
'.pl': 'perl', '.pm': 'perl',
|
|
257
|
+
'.lua': 'lua',
|
|
258
|
+
// Mobile
|
|
259
|
+
'.swift': 'swift',
|
|
260
|
+
'.dart': 'dart',
|
|
261
|
+
'.m': 'objectivec', '.mm': 'objectivec',
|
|
262
|
+
// Functional languages
|
|
263
|
+
'.hs': 'haskell', '.lhs': 'haskell',
|
|
264
|
+
'.ml': 'ocaml', '.mli': 'ocaml',
|
|
265
|
+
'.erl': 'erlang', '.hrl': 'erlang',
|
|
266
|
+
'.ex': 'elixir', '.exs': 'elixir',
|
|
267
|
+
'.jl': 'julia',
|
|
268
|
+
'.r': 'r', '.R': 'r',
|
|
269
|
+
// Other compiled
|
|
270
|
+
'.cr': 'crystal',
|
|
271
|
+
// Markup/styles
|
|
272
|
+
'.html': 'html', '.htm': 'html',
|
|
273
|
+
'.css': 'css', '.scss': 'scss', '.sass': 'sass', '.less': 'less', '.styl': 'stylus',
|
|
274
|
+
'.xml': 'xml', '.xsl': 'xml', '.xslt': 'xml',
|
|
275
|
+
// Data/config
|
|
199
276
|
'.json': 'json',
|
|
200
|
-
'.yaml': 'yaml',
|
|
201
|
-
'.
|
|
202
|
-
'.
|
|
277
|
+
'.yaml': 'yaml', '.yml': 'yaml',
|
|
278
|
+
'.toml': 'toml',
|
|
279
|
+
'.ini': 'ini', '.cfg': 'ini', '.conf': 'ini',
|
|
280
|
+
// Database/query
|
|
203
281
|
'.sql': 'sql',
|
|
204
|
-
'.
|
|
205
|
-
'.
|
|
206
|
-
|
|
207
|
-
'.
|
|
282
|
+
'.prisma': 'prisma',
|
|
283
|
+
'.graphql': 'graphql', '.gql': 'graphql',
|
|
284
|
+
// Infrastructure
|
|
285
|
+
'.tf': 'terraform', '.tfvars': 'terraform',
|
|
286
|
+
'.proto': 'protobuf',
|
|
287
|
+
// Shell/scripting
|
|
288
|
+
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell',
|
|
289
|
+
'.ps1': 'powershell', '.psm1': 'powershell', '.psd1': 'powershell',
|
|
290
|
+
'.bat': 'batch', '.cmd': 'batch',
|
|
291
|
+
// Documentation
|
|
292
|
+
'.md': 'markdown', '.markdown': 'markdown', '.mdx': 'markdown',
|
|
293
|
+
'.rst': 'restructuredtext'
|
|
208
294
|
};
|
|
209
295
|
return map[ext] || 'unknown';
|
|
210
296
|
}
|