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.
@@ -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
@@ -6,7 +6,6 @@ export interface TierLimits {
6
6
  requestsPerDay: number;
7
7
  fullAnalysisPerDay: number;
8
8
  projectsPerDay: number;
9
- maxProjectSizeMB: number;
10
9
  retentionDays: number;
11
10
  price: number;
12
11
  priceYearly: number;
@@ -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
@@ -12,24 +12,69 @@ export class FileUtils {
12
12
  return stats.size;
13
13
  }
14
14
  static async getAllFiles(rootDir, patterns = [
15
- '**/*.ts', '**/*.js', '**/*.tsx', '**/*.jsx', // JS/TS
16
- '**/*.vue', // Vue
17
- '**/*.py', // Python
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
- '**/*.php', // PHP
22
- '**/*.rb', // Ruby
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
- '**/*.cpp', '**/*.c', '**/*.h', '**/*.hpp', // C/C++
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
- '**/*.xml', // XML
30
- '**/*.sql', // SQL
31
- '**/*.sh', '**/*.bash', // Shell
32
- '**/*.md', '**/*.markdown' // Markdown (for documentation)
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
- '.ts': 'typescript',
178
- '.tsx': 'typescript',
179
- '.js': 'javascript',
180
- '.jsx': 'javascript',
181
- '.vue': 'vue',
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
- '.php': 'php',
187
- '.rb': 'ruby',
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
- '.cpp': 'cpp',
190
- '.c': 'c',
191
- '.h': 'c',
192
- '.hpp': 'cpp',
193
- '.html': 'html',
194
- '.htm': 'html',
195
- '.css': 'css',
196
- '.scss': 'scss',
197
- '.sass': 'sass',
198
- '.less': 'less',
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
- '.yml': 'yaml',
202
- '.xml': 'xml',
277
+ '.yaml': 'yaml', '.yml': 'yaml',
278
+ '.toml': 'toml',
279
+ '.ini': 'ini', '.cfg': 'ini', '.conf': 'ini',
280
+ // Database/query
203
281
  '.sql': 'sql',
204
- '.sh': 'shell',
205
- '.bash': 'shell',
206
- '.md': 'markdown',
207
- '.markdown': 'markdown'
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archicore",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "AI Software Architect - code analysis, impact prediction, semantic search",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",