ultracode 5.3.0 → 5.5.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.
Files changed (57) hide show
  1. package/dist/chunks/analysis-tool-handlers-H2RXLDPX.js +817 -0
  2. package/dist/chunks/analysis-tool-handlers-RJZAR6VT.js +817 -0
  3. package/dist/chunks/analysis-tool-handlers-Z2RF24T7.js +13 -0
  4. package/dist/chunks/autodoc-tool-handlers-CV5JEQUA.js +1112 -0
  5. package/dist/chunks/autodoc-tool-handlers-EHTNCH6I.js +1112 -0
  6. package/dist/chunks/autodoc-tool-handlers-MECXQJ2K.js +138 -0
  7. package/dist/chunks/chaos-CO7TOBOJ.js +18 -0
  8. package/dist/chunks/chaos-VM2PXERO.js +1573 -0
  9. package/dist/chunks/chaos-W3XRVJ7K.js +1564 -0
  10. package/dist/chunks/chunk-6K37BWK5.js +439 -0
  11. package/dist/chunks/chunk-EALTCYHZ.js +10 -0
  12. package/dist/chunks/chunk-FTBE7VMY.js +316 -0
  13. package/dist/chunks/chunk-KBW6LRQP.js +322 -0
  14. package/dist/chunks/chunk-NKUHX4CU.js +5 -0
  15. package/dist/chunks/chunk-NZFF4DQ4.js +3179 -0
  16. package/dist/chunks/chunk-RGP5UVQ7.js +3179 -0
  17. package/dist/chunks/chunk-RMZXFGQZ.js +322 -0
  18. package/dist/chunks/chunk-UG44F23Y.js +316 -0
  19. package/dist/chunks/chunk-V2SCB5H5.js +4403 -0
  20. package/dist/chunks/chunk-V6JAQNM3.js +1 -0
  21. package/dist/chunks/chunk-XFGXM4CR.js +4403 -0
  22. package/dist/chunks/dev-agent-JVIGBMHQ.js +1 -0
  23. package/dist/chunks/dev-agent-TRVP5U6N.js +1624 -0
  24. package/dist/chunks/dev-agent-Y5G5WKQ4.js +1624 -0
  25. package/dist/chunks/graph-storage-factory-AYZ57YSL.js +13 -0
  26. package/dist/chunks/graph-storage-factory-GTAIJEI5.js +1 -0
  27. package/dist/chunks/graph-storage-factory-T2WO5QVG.js +13 -0
  28. package/dist/chunks/incremental-updater-KDIQGAUU.js +14 -0
  29. package/dist/chunks/incremental-updater-OJRSTO3Q.js +1 -0
  30. package/dist/chunks/incremental-updater-SBEBH7KF.js +14 -0
  31. package/dist/chunks/indexer-agent-H3QIEL3Z.js +21 -0
  32. package/dist/chunks/indexer-agent-KHF5JMV7.js +21 -0
  33. package/dist/chunks/indexer-agent-SHJD6Z77.js +1 -0
  34. package/dist/chunks/indexing-pipeline-J6Z4BHKF.js +1 -0
  35. package/dist/chunks/indexing-pipeline-OY3337QN.js +249 -0
  36. package/dist/chunks/indexing-pipeline-WCXIDMAP.js +249 -0
  37. package/dist/chunks/merge-agent-LSUBDJB2.js +2481 -0
  38. package/dist/chunks/merge-agent-MJEW3HWU.js +2481 -0
  39. package/dist/chunks/merge-agent-O45OXF33.js +11 -0
  40. package/dist/chunks/merge-tool-handlers-BDSVNQVZ.js +277 -0
  41. package/dist/chunks/merge-tool-handlers-HP7DRBXJ.js +1 -0
  42. package/dist/chunks/merge-tool-handlers-RUJAKE3D.js +277 -0
  43. package/dist/chunks/pattern-tool-handlers-L62W3CXR.js +1549 -0
  44. package/dist/chunks/pattern-tool-handlers-SAHX2CVW.js +13 -0
  45. package/dist/chunks/query-agent-3TWDFIMT.js +191 -0
  46. package/dist/chunks/query-agent-HXQ3BMMF.js +191 -0
  47. package/dist/chunks/query-agent-USMC2GNG.js +1 -0
  48. package/dist/chunks/semantic-agent-MQCAWIAB.js +6381 -0
  49. package/dist/chunks/semantic-agent-NDGR3NAK.js +6381 -0
  50. package/dist/chunks/semantic-agent-S4ZL6GZC.js +137 -0
  51. package/dist/index.js +17 -17
  52. package/dist/roslyn-addon/.build-hash +1 -1
  53. package/dist/roslyn-addon/ILGPU.Algorithms.dll +0 -0
  54. package/dist/roslyn-addon/ILGPU.dll +0 -0
  55. package/dist/roslyn-addon/UltraCode.CSharp.deps.json +35 -0
  56. package/dist/roslyn-addon/UltraCode.CSharp.dll +0 -0
  57. package/package.json +1 -1
@@ -0,0 +1,1564 @@
1
+ import './chunk-CTXFPNDA.js';
2
+ import './chunk-TJSOOFXA.js';
3
+ import { init_logging, log } from './chunk-VCCBEJQ5.js';
4
+ import './chunk-NAQKA54E.js';
5
+
6
+ // src/analysis/chaos/angular-patterns.ts
7
+ function isStateIdentifier(name) {
8
+ const lowerName = name.toLowerCase();
9
+ const stateKeywords = [
10
+ "state",
11
+ "config",
12
+ "context",
13
+ "data",
14
+ "token",
15
+ "user",
16
+ "auth",
17
+ "session",
18
+ "settings",
19
+ "cache",
20
+ "store",
21
+ "value",
22
+ "status",
23
+ "flag",
24
+ "id"
25
+ ];
26
+ if (stateKeywords.some((keyword) => lowerName.includes(keyword))) {
27
+ return true;
28
+ }
29
+ const patterns = [
30
+ /^_[a-z]+$/,
31
+ // _token
32
+ /^is[A-Z]/,
33
+ // isLoading
34
+ /^has[A-Z]/,
35
+ // hasToken
36
+ /[Ss]tate$/,
37
+ // userState
38
+ /[Cc]onfig$/,
39
+ // apiConfig
40
+ /Subject$/
41
+ // userSubject
42
+ ];
43
+ return patterns.some((pattern) => pattern.test(name));
44
+ }
45
+
46
+ // src/analysis/chaos/chaos-analyzer.ts
47
+ init_logging();
48
+
49
+ // src/analysis/chaos/csharp-patterns.ts
50
+ var CSHARP_ASYNC_APIS = [
51
+ "Task.Run",
52
+ "Task.Factory.StartNew",
53
+ "Task.WhenAll",
54
+ "Task.WhenAny",
55
+ "Task.Delay",
56
+ "Parallel.ForEach",
57
+ "Parallel.For",
58
+ "Parallel.ForEachAsync",
59
+ "ThreadPool.QueueUserWorkItem",
60
+ "Channel<",
61
+ "Channel.CreateBounded",
62
+ "Channel.CreateUnbounded",
63
+ "Timer",
64
+ "PeriodicTimer",
65
+ "BackgroundService",
66
+ "IHostedService"
67
+ ];
68
+ var CSHARP_LOCK_PATTERNS = [
69
+ /\block\s*\(/,
70
+ /SemaphoreSlim/,
71
+ /Monitor\.(Enter|Exit|TryEnter)/,
72
+ /Interlocked\./,
73
+ /ReaderWriterLockSlim/,
74
+ /SpinLock/,
75
+ /Volatile\.(Read|Write)/,
76
+ /Mutex/,
77
+ /ConcurrentDictionary/,
78
+ /ConcurrentBag/,
79
+ /ConcurrentQueue/,
80
+ /ConcurrentStack/,
81
+ /ImmutableArray/,
82
+ /ImmutableList/,
83
+ /ImmutableDictionary/
84
+ ];
85
+ function isCSharpStateIdentifier(entity) {
86
+ const name = entity.name.toLowerCase();
87
+ const modifiers = entity.metadata?.modifiers || [];
88
+ if (modifiers.includes("readonly")) {
89
+ return false;
90
+ }
91
+ const diServicePatterns = [
92
+ /^_log(ger)?$/i,
93
+ /^_(http)?client$/i,
94
+ /^_mediator$/i,
95
+ /^_mapper$/i,
96
+ /^_sender$/i,
97
+ /^_(message|event)?bus$/i,
98
+ /^_publisher$/i,
99
+ /^_inner$/i,
100
+ /^_next$/i,
101
+ /^_handler$/i,
102
+ /^_service$/i,
103
+ /^_provider$/i,
104
+ /^_factory$/i,
105
+ /^_repository$/i,
106
+ /^_config(uration)?$/i,
107
+ /^_options$/i,
108
+ /^_settings$/i,
109
+ /^_env(ironment)?$/i
110
+ ];
111
+ if (diServicePatterns.some((p) => p.test(entity.name))) {
112
+ return false;
113
+ }
114
+ if (modifiers.includes("static") && !modifiers.includes("const")) {
115
+ return true;
116
+ }
117
+ const stateKeywords = ["state", "cache", "connection", "session", "current", "singleton", "shared", "global"];
118
+ if (stateKeywords.some((kw) => name.includes(kw))) {
119
+ return true;
120
+ }
121
+ const weakStateKeywords = ["config", "configuration", "settings", "options", "context", "token", "instance"];
122
+ if (weakStateKeywords.some((kw) => name.includes(kw)) && !/^_[a-z]/.test(entity.name)) {
123
+ return true;
124
+ }
125
+ const typeStr = entity.type;
126
+ if (/^_[a-z]/.test(entity.name) && (entity.type === "variable" /* VARIABLE */ || entity.type === "constant" /* CONSTANT */ || typeStr === "field" || typeStr === "property")) {
127
+ return true;
128
+ }
129
+ if (/^(Is|Has|Can|Should|Current|Last|Previous)/.test(entity.name)) {
130
+ return true;
131
+ }
132
+ return false;
133
+ }
134
+ function detectCSharpChaosPatterns(entities) {
135
+ const patterns = [];
136
+ const classesByFile = /* @__PURE__ */ new Map();
137
+ for (const entity of entities) {
138
+ if (entity.language !== "csharp" && entity.metadata?.language !== "csharp") {
139
+ continue;
140
+ }
141
+ const modifiers = entity.metadata?.modifiers || [];
142
+ const returnType = entity.metadata?.returnType || "";
143
+ const params = entity.metadata?.parameters || [];
144
+ const line = entity.location?.start?.line || 0;
145
+ const typeStr = entity.type;
146
+ const isFieldLike = entity.type === "variable" /* VARIABLE */ || entity.type === "constant" /* CONSTANT */ || typeStr === "field" || typeStr === "property";
147
+ const isMethodLike = entity.type === "method" /* METHOD */ || entity.type === "function" /* FUNCTION */ || typeStr === "constructor";
148
+ if (isFieldLike && modifiers.includes("static") && !modifiers.includes("readonly") && !modifiers.includes("const")) {
149
+ patterns.push({
150
+ pattern: "mutable-static",
151
+ severity: "critical",
152
+ entityId: entity.id,
153
+ entityName: entity.name,
154
+ filePath: entity.filePath,
155
+ line,
156
+ description: `Mutable static field '${entity.name}' \u2014 shared across all threads without protection`,
157
+ suggestion: "Use ConcurrentDictionary, add readonly, or move state to DI-managed service"
158
+ });
159
+ }
160
+ if (isMethodLike && modifiers.includes("async") && (returnType === "void" || returnType === "Void")) {
161
+ patterns.push({
162
+ pattern: "async-void",
163
+ severity: "high",
164
+ entityId: entity.id,
165
+ entityName: entity.name,
166
+ filePath: entity.filePath,
167
+ line,
168
+ description: `async void method '${entity.name}' \u2014 exceptions will crash the process`,
169
+ suggestion: "Replace 'async void' with 'async Task'. Only use async void for event handlers"
170
+ });
171
+ }
172
+ if (entity.type === "class" /* CLASS */) {
173
+ const fileClasses = classesByFile.get(entity.filePath) || [];
174
+ fileClasses.push(entity);
175
+ classesByFile.set(entity.filePath, fileClasses);
176
+ }
177
+ if (isMethodLike && (entity.name === ".ctor" || typeStr === "constructor") && params.length >= 10) {
178
+ patterns.push({
179
+ pattern: "god-service",
180
+ severity: "medium",
181
+ entityId: entity.id,
182
+ entityName: entity.name,
183
+ filePath: entity.filePath,
184
+ line,
185
+ description: `Constructor with ${params.length} parameters \u2014 likely a God Service violating SRP`,
186
+ suggestion: "Split into smaller focused services. Group related dependencies into aggregate services"
187
+ });
188
+ }
189
+ if (isMethodLike && modifiers.includes("async") && returnType !== "void" && returnType !== "Void" && (returnType.includes("Task") || returnType.includes("ValueTask")) && !params.some((p) => p.type?.includes("CancellationToken"))) {
190
+ patterns.push({
191
+ pattern: "missing-cancellation",
192
+ severity: "medium",
193
+ entityId: entity.id,
194
+ entityName: entity.name,
195
+ filePath: entity.filePath,
196
+ line,
197
+ description: `Async method '${entity.name}' without CancellationToken \u2014 cannot be cancelled gracefully`,
198
+ suggestion: "Add CancellationToken parameter and pass it to downstream async calls"
199
+ });
200
+ }
201
+ if (isFieldLike && modifiers.includes("static") && !modifiers.includes("readonly") && !modifiers.includes("const")) {
202
+ const decorators = entity.metadata?.decorators || [];
203
+ const hasSingletonIndicator = decorators.some(
204
+ (d) => d.name === "Singleton" || d.name === "SingleInstance" || d.name === "ServiceLifetime.Singleton"
205
+ ) || entity.name.toLowerCase().includes("singleton");
206
+ if (hasSingletonIndicator) {
207
+ patterns.push({
208
+ pattern: "singleton-mutable-state",
209
+ severity: "high",
210
+ entityId: entity.id,
211
+ entityName: entity.name,
212
+ filePath: entity.filePath,
213
+ line,
214
+ description: `Singleton class has mutable field '${entity.name}' \u2014 concurrent access will cause data races`,
215
+ suggestion: "Use ConcurrentDictionary/ImmutableCollections, change to Scoped lifetime, or add lock synchronization"
216
+ });
217
+ }
218
+ }
219
+ }
220
+ return patterns;
221
+ }
222
+
223
+ // src/analysis/chaos/state-detector.ts
224
+ init_logging();
225
+
226
+ // src/analysis/chaos/race-detector.ts
227
+ init_logging();
228
+ var HIDDEN_ASYNC_APIS = [
229
+ // Storage APIs - synchronous but with I/O delays
230
+ "localStorage",
231
+ "sessionStorage",
232
+ "indexedDB",
233
+ "caches",
234
+ // Cache API
235
+ // DOM APIs with async rendering effects
236
+ "requestAnimationFrame",
237
+ "requestIdleCallback",
238
+ "IntersectionObserver",
239
+ "MutationObserver",
240
+ "ResizeObserver",
241
+ "PerformanceObserver",
242
+ // Network/Communication
243
+ "fetch",
244
+ "XMLHttpRequest",
245
+ "WebSocket",
246
+ "EventSource",
247
+ "BroadcastChannel",
248
+ "SharedWorker",
249
+ "ServiceWorker",
250
+ // Timers
251
+ "setTimeout",
252
+ "setInterval",
253
+ "queueMicrotask",
254
+ // History API - can trigger events async
255
+ "history.pushState",
256
+ "history.replaceState",
257
+ // Clipboard API
258
+ "navigator.clipboard",
259
+ // Geolocation
260
+ "navigator.geolocation",
261
+ // Media APIs
262
+ "MediaRecorder",
263
+ "RTCPeerConnection",
264
+ // File APIs
265
+ "FileReader",
266
+ "FileWriter",
267
+ // C# Task-based async APIs
268
+ "Task.Run",
269
+ "Task.Factory.StartNew",
270
+ "Task.WhenAll",
271
+ "Task.WhenAny",
272
+ "Task.Delay",
273
+ "Parallel.ForEach",
274
+ "Parallel.For",
275
+ "Parallel.ForEachAsync",
276
+ "ThreadPool.QueueUserWorkItem",
277
+ "Channel.",
278
+ "Timer",
279
+ "PeriodicTimer"
280
+ ];
281
+ var ASYNC_BEHAVIOR_PATTERNS = [
282
+ /\.addEventListener\s*\(/,
283
+ /\.on[A-Z]\w+\s*=/,
284
+ /new\s+Promise\s*\(/,
285
+ /\.then\s*\(/,
286
+ /\.catch\s*\(/,
287
+ /\.finally\s*\(/,
288
+ /callback/i,
289
+ /handler/i,
290
+ /listener/i,
291
+ /\.subscribe\s*\(/,
292
+ /\.pipe\s*\(/,
293
+ /rxjs/i,
294
+ /observable/i,
295
+ /subject/i,
296
+ /\$\./,
297
+ // jQuery async patterns
298
+ /emit/i,
299
+ /dispatch/i,
300
+ /broadcast/i,
301
+ // C# async patterns
302
+ /async\s+Task/,
303
+ /async\s+ValueTask/,
304
+ /\.ConfigureAwait\(/,
305
+ /CancellationToken/,
306
+ /IAsyncEnumerable/
307
+ ];
308
+ var RaceDetector = class {
309
+ constructor(storage) {
310
+ this.storage = storage;
311
+ }
312
+ _entityCache = null;
313
+ /** Pre-loaded relationship lookup: entityId → Relationship[] */
314
+ _relLookup = null;
315
+ /** Cache "is parent async" result per entityId to avoid re-processing */
316
+ _relCache = /* @__PURE__ */ new Map();
317
+ /**
318
+ * Set pre-loaded entity cache to avoid N+1 DB queries.
319
+ * Call before analyzeRaces() and clear after with clearEntityCache().
320
+ */
321
+ setEntityCache(entities) {
322
+ this._entityCache = new Map(entities.map((e) => [e.id, e]));
323
+ }
324
+ /** Set pre-loaded relationship lookup to avoid per-entity DB queries */
325
+ setRelationshipCache(lookup) {
326
+ this._relLookup = lookup;
327
+ }
328
+ clearEntityCache() {
329
+ this._entityCache = null;
330
+ this._relLookup = null;
331
+ this._relCache.clear();
332
+ }
333
+ /** Lookup entity from cache first, fallback to DB */
334
+ async getEntityCached(id) {
335
+ if (this._entityCache) {
336
+ return this._entityCache.get(id) || null;
337
+ }
338
+ return this.storage.getEntity(id);
339
+ }
340
+ /**
341
+ * Analyze state operations for race conditions
342
+ */
343
+ async analyzeRaces(stateIdentifier, operations) {
344
+ const readers = operations.filter((op) => op.operationType === "read" || op.operationType === "check");
345
+ const writers = operations.filter(
346
+ (op) => op.operationType === "write" || op.operationType === "initialize" || op.operationType === "emit"
347
+ );
348
+ const tMut = performance.now();
349
+ const mutations = await this.analyzeMutations(writers);
350
+ const dtMut = performance.now() - tMut;
351
+ if (dtMut > 200) {
352
+ log.w("CHAOS_RACE", "slow_mutations", {
353
+ id: stateIdentifier,
354
+ writers: writers.length,
355
+ ms: +dtMut.toFixed(0),
356
+ cached: !!this._entityCache
357
+ });
358
+ }
359
+ const factors = this.calculateRiskFactors(mutations);
360
+ const conflicts = this.detectConflicts(stateIdentifier, mutations, readers);
361
+ const raceRisk = this.calculateRaceRisk(factors, conflicts);
362
+ return {
363
+ stateIdentifier,
364
+ readers: readers.length,
365
+ writers: writers.length,
366
+ asyncWriters: mutations.filter((m) => m.isAsync).length,
367
+ unprotectedWriters: mutations.filter((m) => !m.hasLock).length,
368
+ raceRisk,
369
+ raceReason: this.generateRaceReason(factors, conflicts),
370
+ conflicts,
371
+ mutations
372
+ };
373
+ }
374
+ /**
375
+ * Convert state operations to detailed mutation points.
376
+ * Uses parallel async context detection for better throughput.
377
+ */
378
+ async analyzeMutations(writers) {
379
+ const asyncResults = await Promise.all(writers.map((w) => this.isAsyncContext(w)));
380
+ return writers.map((writer, i) => ({
381
+ file: writer.file,
382
+ line: writer.line,
383
+ entityId: writer.entityId,
384
+ entityName: writer.entityName,
385
+ mutationType: this.classifyMutation(writer),
386
+ condition: this.extractCondition(writer.context),
387
+ isAsync: asyncResults[i],
388
+ hasLock: this.detectLockPattern(writer.code, writer.context),
389
+ code: writer.code
390
+ }));
391
+ }
392
+ /**
393
+ * Check if operation is in async context
394
+ * Includes detection of hidden async APIs that appear synchronous.
395
+ * Uses entity cache to avoid N+1 DB queries.
396
+ */
397
+ async isAsyncContext(operation) {
398
+ const codeToCheck = `${operation.code}
399
+ ${operation.context || ""}`;
400
+ if (/async\s|await\s|\.then\(|Promise|setTimeout|setInterval|\.subscribe\(/.test(codeToCheck)) {
401
+ return true;
402
+ }
403
+ for (const api of HIDDEN_ASYNC_APIS) {
404
+ if (codeToCheck.includes(api)) return true;
405
+ }
406
+ for (const pattern of ASYNC_BEHAVIOR_PATTERNS) {
407
+ if (pattern.test(codeToCheck)) return true;
408
+ }
409
+ if (!operation.entityId) return false;
410
+ try {
411
+ const entity = await this.getEntityCached(operation.entityId);
412
+ if (!entity) return false;
413
+ if (entity.language === "csharp" || entity.metadata?.language === "csharp") {
414
+ if (entity.metadata?.modifiers?.includes("async")) return true;
415
+ if (/Task|ValueTask/.test(entity.metadata?.returnType || "")) return true;
416
+ }
417
+ const code = entity.code || "";
418
+ if (/^async\s/.test(code) || /async\s+function/.test(code)) return true;
419
+ for (const api of HIDDEN_ASYNC_APIS) {
420
+ if (code.includes(api)) return true;
421
+ }
422
+ for (const pattern of ASYNC_BEHAVIOR_PATTERNS) {
423
+ if (pattern.test(code)) return true;
424
+ }
425
+ if (this._relCache.has(operation.entityId)) {
426
+ return this._relCache.get(operation.entityId);
427
+ }
428
+ const relationships = this._relLookup?.get(operation.entityId) || await this.storage.getRelationshipsForEntity(operation.entityId);
429
+ let parentIsAsync = false;
430
+ for (const rel of relationships) {
431
+ if (rel.type === "contains" /* CONTAINS */) {
432
+ const parent = await this.getEntityCached(rel.fromId);
433
+ if (parent?.name && /handler|listener|callback|on[A-Z]/i.test(parent.name)) {
434
+ parentIsAsync = true;
435
+ break;
436
+ }
437
+ }
438
+ }
439
+ this._relCache.set(operation.entityId, parentIsAsync);
440
+ if (parentIsAsync) return true;
441
+ } catch {
442
+ }
443
+ return false;
444
+ }
445
+ /**
446
+ * Detect if mutation is protected by lock/mutex pattern
447
+ */
448
+ detectLockPattern(code, context) {
449
+ const lockPatterns = [
450
+ /mutex/i,
451
+ /lock\(/i,
452
+ /semaphore/i,
453
+ /synchronized/i,
454
+ /atomic/i,
455
+ /\.lock\(\)/,
456
+ /await\s+.*lock/i,
457
+ /critical\s*section/i,
458
+ /with\s*lock/i,
459
+ // C# synchronization primitives
460
+ /\block\s*\(/,
461
+ /SemaphoreSlim/,
462
+ /Monitor\.(Enter|Exit|TryEnter)/,
463
+ /Interlocked\./,
464
+ /ReaderWriterLockSlim/,
465
+ /SpinLock/,
466
+ /Volatile\.(Read|Write)/
467
+ ];
468
+ const combined = `${code}
469
+ ${context}`;
470
+ return lockPatterns.some((p) => p.test(combined));
471
+ }
472
+ /**
473
+ * Extract guard condition from context
474
+ */
475
+ extractCondition(context) {
476
+ const ifMatch = context.match(/if\s*\(([^)]+)\)/);
477
+ if (ifMatch?.[1]) {
478
+ return ifMatch[1].trim();
479
+ }
480
+ const ternaryMatch = context.match(/([^?]+)\s*\?/);
481
+ if (ternaryMatch?.[1] && ternaryMatch[1].length < 50) {
482
+ return ternaryMatch[1].trim();
483
+ }
484
+ const caseMatch = context.match(/case\s+([^:]+):/);
485
+ if (caseMatch?.[1]) {
486
+ return `case ${caseMatch[1].trim()}`;
487
+ }
488
+ return void 0;
489
+ }
490
+ /**
491
+ * Classify mutation type from operation
492
+ */
493
+ classifyMutation(op) {
494
+ const code = op.code.toLowerCase();
495
+ if (/=\s*(null|undefined|false|0|''|""|``)\s*[;,)]/.test(code) || /\.(clear|reset)\(\)/.test(code)) {
496
+ return "reset";
497
+ }
498
+ if (/\+\+|--|\+=\s*1|-=\s*1/.test(code)) {
499
+ return "increment";
500
+ }
501
+ if (/=\s*!/.test(code)) {
502
+ return "toggle";
503
+ }
504
+ if (op.context && /if\s*\(/.test(op.context)) {
505
+ return "conditional-write";
506
+ }
507
+ return "write";
508
+ }
509
+ /**
510
+ * Calculate risk factors from mutations
511
+ */
512
+ calculateRiskFactors(mutations) {
513
+ const conditions = mutations.filter((m) => m.condition).map((m) => m.condition);
514
+ const uniqueConditions = new Set(conditions);
515
+ let oppositeConditions = 0;
516
+ for (const cond of uniqueConditions) {
517
+ const negated = cond.startsWith("!") ? cond.slice(1) : `!${cond}`;
518
+ if (uniqueConditions.has(negated)) {
519
+ oppositeConditions++;
520
+ }
521
+ }
522
+ const conditionCounts = /* @__PURE__ */ new Map();
523
+ for (const cond of conditions) {
524
+ conditionCounts.set(cond, (conditionCounts.get(cond) || 0) + 1);
525
+ }
526
+ const sharedConditions = Array.from(conditionCounts.values()).filter((c) => c > 1).length;
527
+ return {
528
+ writerCount: mutations.length,
529
+ asyncBoundaries: mutations.filter((m) => m.isAsync).length,
530
+ sharedConditions,
531
+ oppositeConditions: oppositeConditions / 2,
532
+ // Pairs
533
+ hasLocks: mutations.some((m) => m.hasLock),
534
+ resetPoints: mutations.filter((m) => m.mutationType === "reset").length
535
+ };
536
+ }
537
+ /**
538
+ * Detect specific race conflict patterns
539
+ */
540
+ detectConflicts(stateIdentifier, mutations, _readers) {
541
+ const conflicts = [];
542
+ const resets = mutations.filter((m) => m.mutationType === "reset");
543
+ if (resets.length >= 2) {
544
+ conflicts.push({
545
+ pattern: "competing-resets",
546
+ severity: this.assessConflictSeverity(resets),
547
+ stateIdentifier,
548
+ description: `${resets.length} places reset ${stateIdentifier}`,
549
+ locations: resets,
550
+ explanation: `Multiple functions reset ${stateIdentifier} to initial value. If called concurrently, state may be reset while another function expects it to be set.`,
551
+ suggestion: "Centralize reset logic in a single function or use state machine pattern"
552
+ });
553
+ }
554
+ const conditionalWrites = mutations.filter((m) => m.mutationType === "conditional-write" && !m.hasLock);
555
+ if (conditionalWrites.length >= 2) {
556
+ const conditionGroups = /* @__PURE__ */ new Map();
557
+ for (const m of conditionalWrites) {
558
+ if (m.condition) {
559
+ const key = m.condition.replace(/\s+/g, "");
560
+ let arr = conditionGroups.get(key);
561
+ if (!arr) {
562
+ arr = [];
563
+ conditionGroups.set(key, arr);
564
+ }
565
+ arr.push(m);
566
+ }
567
+ }
568
+ for (const [condition, group] of conditionGroups) {
569
+ if (group.length >= 2) {
570
+ conflicts.push({
571
+ pattern: "check-then-act",
572
+ severity: "high",
573
+ stateIdentifier,
574
+ description: `${group.length} places check "${condition}" then modify ${stateIdentifier}`,
575
+ locations: group,
576
+ sharedCondition: condition,
577
+ explanation: `Multiple functions check the same condition before modifying state. Between check and act, another function may change the state.`,
578
+ suggestion: "Use atomic compare-and-swap, mutex, or combine into single handler"
579
+ });
580
+ }
581
+ }
582
+ }
583
+ const asyncMutations = mutations.filter((m) => m.isAsync && !m.hasLock);
584
+ if (asyncMutations.length >= 2) {
585
+ conflicts.push({
586
+ pattern: "async-boundary",
587
+ severity: "high",
588
+ stateIdentifier,
589
+ description: `${asyncMutations.length} async operations modify ${stateIdentifier} without synchronization`,
590
+ locations: asyncMutations,
591
+ explanation: `Async operations can interleave unpredictably. One operation may overwrite changes from another.`,
592
+ suggestion: "Add mutex/lock or use queue-based state updates"
593
+ });
594
+ }
595
+ const oppositeGroups = this.findOppositeConditionGroups(mutations);
596
+ for (const group of oppositeGroups) {
597
+ conflicts.push({
598
+ pattern: "competing-mutations",
599
+ severity: "critical",
600
+ stateIdentifier,
601
+ description: `Functions with opposite conditions both modify ${stateIdentifier}`,
602
+ locations: group,
603
+ explanation: `Functions checking opposite conditions (e.g., "if (x)" vs "if (!x)") both modify the state. This is a classic race condition pattern where both may execute simultaneously.`,
604
+ suggestion: "Merge into single handler with exclusive logic, or add explicit synchronization"
605
+ });
606
+ }
607
+ const handlerMutations = mutations.filter((m) => /handler|listener|on[A-Z]|callback/i.test(m.entityName));
608
+ if (handlerMutations.length >= 2) {
609
+ conflicts.push({
610
+ pattern: "event-handler-race",
611
+ severity: "medium",
612
+ stateIdentifier,
613
+ description: `${handlerMutations.length} event handlers modify ${stateIdentifier}`,
614
+ locations: handlerMutations,
615
+ explanation: `Multiple event handlers modify shared state. Event ordering may vary, causing unpredictable state.`,
616
+ suggestion: "Consolidate handlers or use event queue with single consumer"
617
+ });
618
+ }
619
+ return conflicts;
620
+ }
621
+ /**
622
+ * Find groups of mutations with opposite conditions
623
+ */
624
+ findOppositeConditionGroups(mutations) {
625
+ const groups = [];
626
+ const processed = /* @__PURE__ */ new Set();
627
+ for (let i = 0; i < mutations.length; i++) {
628
+ const mutationI = mutations[i];
629
+ if (processed.has(i) || !mutationI || !mutationI.condition) continue;
630
+ const cond = mutationI.condition;
631
+ const normalizedCond = cond.replace(/\s+/g, "");
632
+ const negatedCond = cond.startsWith("!") ? cond.slice(1).replace(/\s+/g, "") : `!${normalizedCond}`;
633
+ const opposites = [mutationI];
634
+ processed.add(i);
635
+ for (let j = i + 1; j < mutations.length; j++) {
636
+ const mutationJ = mutations[j];
637
+ if (processed.has(j) || !mutationJ || !mutationJ.condition) continue;
638
+ const otherCond = mutationJ.condition.replace(/\s+/g, "");
639
+ if (otherCond === negatedCond || `!${otherCond}` === normalizedCond) {
640
+ opposites.push(mutationJ);
641
+ processed.add(j);
642
+ }
643
+ }
644
+ if (opposites.length >= 2) {
645
+ groups.push(opposites);
646
+ }
647
+ }
648
+ return groups;
649
+ }
650
+ /**
651
+ * Assess severity of a conflict based on mutations
652
+ */
653
+ assessConflictSeverity(mutations) {
654
+ const hasAsync = mutations.some((m) => m.isAsync);
655
+ const allUnprotected = mutations.every((m) => !m.hasLock);
656
+ const count = mutations.length;
657
+ if (hasAsync && allUnprotected && count >= 3) return "critical";
658
+ if (hasAsync && allUnprotected) return "high";
659
+ if (count >= 3 && allUnprotected) return "high";
660
+ if (count >= 2 && allUnprotected) return "medium";
661
+ return "low";
662
+ }
663
+ /**
664
+ * Calculate overall race risk from factors and conflicts
665
+ */
666
+ calculateRaceRisk(factors, conflicts) {
667
+ let risk = "none";
668
+ if (factors.writerCount === 1) {
669
+ return "none";
670
+ }
671
+ if (factors.writerCount >= 2) {
672
+ risk = "low";
673
+ }
674
+ if (factors.asyncBoundaries >= 2 && !factors.hasLocks) {
675
+ risk = "high";
676
+ }
677
+ if (factors.oppositeConditions >= 1) {
678
+ risk = "high";
679
+ }
680
+ if (factors.resetPoints >= 2 && factors.asyncBoundaries >= 1) {
681
+ risk = "critical";
682
+ }
683
+ for (const conflict of conflicts) {
684
+ if (this.riskLevel(conflict.severity) > this.riskLevel(risk)) {
685
+ risk = conflict.severity;
686
+ }
687
+ }
688
+ return risk;
689
+ }
690
+ /**
691
+ * Convert risk to numeric level for comparison
692
+ */
693
+ riskLevel(risk) {
694
+ const levels = {
695
+ none: 0,
696
+ low: 1,
697
+ medium: 2,
698
+ high: 3,
699
+ critical: 4
700
+ };
701
+ return levels[risk];
702
+ }
703
+ /**
704
+ * Generate human-readable race reason
705
+ */
706
+ generateRaceReason(factors, conflicts) {
707
+ if (factors.writerCount <= 1) {
708
+ return void 0;
709
+ }
710
+ const parts = [];
711
+ parts.push(`${factors.writerCount} writers`);
712
+ if (factors.asyncBoundaries > 0) {
713
+ parts.push(`${factors.asyncBoundaries} async`);
714
+ }
715
+ if (!factors.hasLocks && factors.writerCount >= 2) {
716
+ parts.push("no sync");
717
+ }
718
+ if (conflicts.length > 0) {
719
+ const patterns = [...new Set(conflicts.map((c) => c.pattern))];
720
+ parts.push(`patterns: ${patterns.join(", ")}`);
721
+ }
722
+ return parts.join(", ");
723
+ }
724
+ };
725
+
726
+ // src/analysis/chaos/state-detector.ts
727
+ function buildRelationshipLookup(relationships) {
728
+ const lookup = /* @__PURE__ */ new Map();
729
+ for (const rel of relationships) {
730
+ let fromArr = lookup.get(rel.fromId);
731
+ if (!fromArr) {
732
+ fromArr = [];
733
+ lookup.set(rel.fromId, fromArr);
734
+ }
735
+ fromArr.push(rel);
736
+ if (rel.toId !== rel.fromId) {
737
+ let toArr = lookup.get(rel.toId);
738
+ if (!toArr) {
739
+ toArr = [];
740
+ lookup.set(rel.toId, toArr);
741
+ }
742
+ toArr.push(rel);
743
+ }
744
+ }
745
+ return lookup;
746
+ }
747
+ var StateDetector = class {
748
+ constructor(storage) {
749
+ this.storage = storage;
750
+ this.raceDetector = new RaceDetector(storage);
751
+ }
752
+ raceDetector;
753
+ _cachedEntities = null;
754
+ /** Pre-loaded functions/methods for body scanning — shared across all identifiers */
755
+ _cachedFunctions = null;
756
+ /** Pre-loaded relationships indexed by entityId for O(1) lookup */
757
+ _relLookup = null;
758
+ async detectPatterns(options, cachedEntities, relLookup) {
759
+ const t0 = performance.now();
760
+ this._cachedEntities = cachedEntities || await this.storage.getAllEntities();
761
+ this._relLookup = relLookup || null;
762
+ const patterns = [];
763
+ this.raceDetector.setEntityCache(this._cachedEntities);
764
+ if (this._relLookup) {
765
+ this.raceDetector.setRelationshipCache(this._relLookup);
766
+ }
767
+ const tFns = performance.now();
768
+ this._cachedFunctions = await this.storage.searchEntities({
769
+ types: ["function" /* FUNCTION */, "method" /* METHOD */, "constructor"]
770
+ });
771
+ log.i("CHAOS_DET", "preload_functions", {
772
+ count: this._cachedFunctions.length,
773
+ ms: +(performance.now() - tFns).toFixed(0)
774
+ });
775
+ if (options.stateIdentifiers && options.stateIdentifiers.length > 0) {
776
+ for (const identifier of options.stateIdentifiers) {
777
+ const pattern = await this.analyzeIdentifier(identifier);
778
+ if (pattern) patterns.push(pattern);
779
+ }
780
+ log.i("CHAOS_DET", "manual_identifiers", {
781
+ count: options.stateIdentifiers.length,
782
+ matched: patterns.length,
783
+ ms: +(performance.now() - t0).toFixed(0)
784
+ });
785
+ } else if (options.autoDetect) {
786
+ const tAutoStart = performance.now();
787
+ const identifiers = this.autoDetectStateIdentifiers(this._cachedEntities);
788
+ log.i("CHAOS_DET", "autoDetect", {
789
+ candidates: identifiers.length,
790
+ fromEntities: this._cachedEntities.length,
791
+ ms: +(performance.now() - tAutoStart).toFixed(0)
792
+ });
793
+ for (let i = 0; i < identifiers.length; i++) {
794
+ const tIdent = performance.now();
795
+ const pattern = await this.analyzeIdentifier(identifiers[i]);
796
+ if (pattern) patterns.push(pattern);
797
+ const dtIdent = performance.now() - tIdent;
798
+ if (dtIdent > 1e3) {
799
+ log.w("CHAOS_DET", "slow_identifier", {
800
+ identifier: identifiers[i],
801
+ index: i,
802
+ ms: +dtIdent.toFixed(0),
803
+ ops: pattern?.operations.length || 0,
804
+ writers: pattern?.raceAnalysis.writers || 0
805
+ });
806
+ }
807
+ }
808
+ log.i("CHAOS_DET", "all_identifiers", {
809
+ total: identifiers.length,
810
+ matched: patterns.length,
811
+ ms: +(performance.now() - t0).toFixed(0)
812
+ });
813
+ }
814
+ this._cachedEntities = null;
815
+ this._cachedFunctions = null;
816
+ this._relLookup = null;
817
+ this.raceDetector.clearEntityCache();
818
+ return patterns.sort((a, b) => {
819
+ const riskOrder = { critical: 0, high: 1, medium: 2, low: 3, none: 4 };
820
+ return riskOrder[a.raceAnalysis.raceRisk] - riskOrder[b.raceAnalysis.raceRisk];
821
+ });
822
+ }
823
+ autoDetectStateIdentifiers(entities) {
824
+ const identifiers = /* @__PURE__ */ new Map();
825
+ for (const entity of entities) {
826
+ const lang = entity.language || entity.metadata?.language;
827
+ const isCSharp = lang === "csharp";
828
+ const typeStr = entity.type;
829
+ const isStateType = entity.type === "variable" /* VARIABLE */ || entity.type === "constant" /* CONSTANT */ || typeStr === "field" || typeStr === "property";
830
+ if (!isStateType) continue;
831
+ const isState = isCSharp ? isCSharpStateIdentifier(entity) : isStateIdentifier(entity.name);
832
+ if (isState) {
833
+ identifiers.set(entity.name, (identifiers.get(entity.name) || 0) + 1);
834
+ }
835
+ }
836
+ return Array.from(identifiers.entries()).sort((a, b) => b[1] - a[1]).slice(0, 20).map(([name]) => name);
837
+ }
838
+ async analyzeIdentifier(identifier) {
839
+ const tOps = performance.now();
840
+ const operations = await this.findStateOperations(identifier);
841
+ const tOpsEnd = performance.now();
842
+ if (operations.length === 0) return null;
843
+ const relatedIdentifiers = this.findRelatedIdentifiers(identifier);
844
+ const tRace = performance.now();
845
+ const raceAnalysis = await this.raceDetector.analyzeRaces(identifier, operations);
846
+ const tRaceEnd = performance.now();
847
+ log.d("CHAOS_DET", "analyzeIdentifier", {
848
+ id: identifier,
849
+ ops: operations.length,
850
+ opsMs: +(tOpsEnd - tOps).toFixed(0),
851
+ raceMs: +(tRaceEnd - tRace).toFixed(0),
852
+ writers: raceAnalysis.writers,
853
+ risk: raceAnalysis.raceRisk
854
+ });
855
+ return {
856
+ identifier,
857
+ type: this.inferStateType(identifier, operations),
858
+ scope: this.inferScope(operations),
859
+ operations,
860
+ relatedIdentifiers,
861
+ raceAnalysis
862
+ };
863
+ }
864
+ async findStateOperations(identifier) {
865
+ const tStart = performance.now();
866
+ const operations = [];
867
+ const variables = await this.storage.searchEntities({
868
+ namePattern: identifier,
869
+ types: ["variable" /* VARIABLE */, "constant" /* CONSTANT */, "field", "property"]
870
+ });
871
+ for (const entity of variables) {
872
+ operations.push({
873
+ file: entity.filePath,
874
+ line: entity.location?.start?.line || 0,
875
+ column: entity.location?.start?.column || 0,
876
+ entityId: entity.id,
877
+ entityName: entity.name,
878
+ operationType: "initialize",
879
+ code: entity.code?.slice(0, 200) || "",
880
+ context: "",
881
+ isDefensive: false,
882
+ depth: 0
883
+ });
884
+ }
885
+ const entityLookup = /* @__PURE__ */ new Map();
886
+ if (this._cachedEntities) {
887
+ for (const e of this._cachedEntities) {
888
+ entityLookup.set(e.id, e);
889
+ }
890
+ }
891
+ for (const variable of variables) {
892
+ const relationships = this._relLookup?.get(variable.id) || await this.storage.getRelationshipsForEntity(variable.id);
893
+ const refIds = [];
894
+ for (const rel of relationships) {
895
+ if (rel.type === "references" /* REFERENCES */) {
896
+ const refId = rel.fromId === variable.id ? rel.toId : rel.fromId;
897
+ if (refId !== variable.id && !entityLookup.has(refId)) {
898
+ refIds.push(refId);
899
+ }
900
+ }
901
+ }
902
+ if (refIds.length > 0) {
903
+ const fetched = await this.storage.getEntitiesBatch(refIds);
904
+ for (const [id, e] of fetched) {
905
+ entityLookup.set(id, e);
906
+ }
907
+ }
908
+ for (const rel of relationships) {
909
+ if (rel.type === "references" /* REFERENCES */) {
910
+ const refId = rel.fromId === variable.id ? rel.toId : rel.fromId;
911
+ const refEntity = entityLookup.get(refId) || null;
912
+ if (refEntity && refEntity.id !== variable.id) {
913
+ const opType = await this.classifyOperation(refEntity, identifier);
914
+ operations.push({
915
+ file: refEntity.filePath,
916
+ line: refEntity.location?.start?.line || 0,
917
+ column: refEntity.location?.start?.column || 0,
918
+ entityId: refEntity.id,
919
+ entityName: refEntity.name,
920
+ operationType: opType,
921
+ code: refEntity.code?.slice(0, 200) || "",
922
+ context: await this.getContext(refEntity),
923
+ isDefensive: this.isDefensiveCode(refEntity.code || ""),
924
+ depth: 1
925
+ });
926
+ }
927
+ }
928
+ }
929
+ }
930
+ const functions = this._cachedFunctions || await this.storage.searchEntities({
931
+ types: ["function" /* FUNCTION */, "method" /* METHOD */, "constructor"]
932
+ });
933
+ const seenEntityIds = new Set(operations.map((op) => op.entityId));
934
+ const identifierRegex = new RegExp(`\\b${identifier}\\b`);
935
+ for (const fn of functions) {
936
+ if (!fn.code || seenEntityIds.has(fn.id)) continue;
937
+ if (!identifierRegex.test(fn.code)) continue;
938
+ const opType = this.classifyOperationFromCode(fn.code, identifier);
939
+ seenEntityIds.add(fn.id);
940
+ operations.push({
941
+ file: fn.filePath,
942
+ line: fn.location?.start?.line || 0,
943
+ column: fn.location?.start?.column || 0,
944
+ entityId: fn.id,
945
+ entityName: fn.name,
946
+ operationType: opType,
947
+ code: this.extractRelevantCode(fn.code, identifier),
948
+ context: "",
949
+ isDefensive: this.isDefensiveCode(fn.code),
950
+ depth: 1
951
+ });
952
+ }
953
+ const dtOps = performance.now() - tStart;
954
+ if (dtOps > 500) {
955
+ log.w("CHAOS_DET", "slow_findOps", {
956
+ identifier,
957
+ vars: variables.length,
958
+ fns: functions.length,
959
+ ops: operations.length,
960
+ ms: +dtOps.toFixed(0)
961
+ });
962
+ }
963
+ return operations;
964
+ }
965
+ /**
966
+ * Classify operation type from entity
967
+ */
968
+ async classifyOperation(entity, identifier) {
969
+ const code = entity.code || "";
970
+ return this.classifyOperationFromCode(code, identifier);
971
+ }
972
+ /**
973
+ * Classify operation type from code string
974
+ */
975
+ classifyOperationFromCode(code, identifier) {
976
+ const writePattern = new RegExp(`${identifier}\\s*=(?!=)`, "g");
977
+ if (writePattern.test(code)) {
978
+ const resetPattern = new RegExp(`${identifier}\\s*=\\s*(null|undefined|false|0|''|""|\\[\\]|\\{\\})`, "g");
979
+ if (resetPattern.test(code)) {
980
+ return "write";
981
+ }
982
+ return "write";
983
+ }
984
+ if (/\.next\(|\.emit\(/.test(code)) return "emit";
985
+ if (/\.subscribe\(|\.pipe\(/.test(code)) return "subscribe";
986
+ if (/\block\s*\(/.test(code)) return "write";
987
+ if (/Interlocked\./.test(code)) return "write";
988
+ if (/\bawait\b/.test(code)) return "read";
989
+ const checkPattern = new RegExp(`if\\s*\\([^)]*${identifier}`, "g");
990
+ if (checkPattern.test(code)) return "check";
991
+ const passPattern = new RegExp(`\\(\\s*[^)]*${identifier}[^)]*\\)`, "g");
992
+ if (passPattern.test(code)) return "pass";
993
+ return "read";
994
+ }
995
+ /**
996
+ * Extract the most relevant code snippet containing the identifier
997
+ */
998
+ extractRelevantCode(code, identifier) {
999
+ const lines = code.split("\n");
1000
+ for (let i = 0; i < lines.length; i++) {
1001
+ const line = lines[i];
1002
+ if (line?.includes(identifier)) {
1003
+ const start = Math.max(0, i - 1);
1004
+ const end = Math.min(lines.length, i + 2);
1005
+ return lines.slice(start, end).join("\n").slice(0, 200);
1006
+ }
1007
+ }
1008
+ return code.slice(0, 200);
1009
+ }
1010
+ /**
1011
+ * Get surrounding context for an entity
1012
+ */
1013
+ async getContext(_entity) {
1014
+ return "";
1015
+ }
1016
+ /**
1017
+ * Check if code contains defensive patterns
1018
+ */
1019
+ isDefensiveCode(code) {
1020
+ return /!==?\s*(null|undefined)|typeof\s+\w+|&&\s*\w+\.|try\s*{|\?\?|\?\./i.test(code);
1021
+ }
1022
+ /**
1023
+ * Find identifiers with similar names
1024
+ */
1025
+ findRelatedIdentifiers(identifier) {
1026
+ const related = [];
1027
+ const entities = this._cachedEntities || [];
1028
+ const variations = [
1029
+ `_${identifier}`,
1030
+ `${identifier}_`,
1031
+ `${identifier}Value`,
1032
+ `${identifier}State`,
1033
+ `current${identifier.charAt(0).toUpperCase()}${identifier.slice(1)}`,
1034
+ `saved${identifier.charAt(0).toUpperCase()}${identifier.slice(1)}`,
1035
+ `old${identifier.charAt(0).toUpperCase()}${identifier.slice(1)}`,
1036
+ `new${identifier.charAt(0).toUpperCase()}${identifier.slice(1)}`
1037
+ ];
1038
+ for (const entity of entities) {
1039
+ if (variations.includes(entity.name) || entity.name.toLowerCase().includes(identifier.toLowerCase())) {
1040
+ if (entity.name !== identifier && !related.includes(entity.name)) {
1041
+ related.push(entity.name);
1042
+ }
1043
+ }
1044
+ }
1045
+ return related.slice(0, 5);
1046
+ }
1047
+ /**
1048
+ * Infer TypeScript type from operations
1049
+ */
1050
+ inferStateType(identifier, operations) {
1051
+ for (const op of operations) {
1052
+ if (op.operationType === "initialize" && op.code) {
1053
+ const typeMatch = op.code.match(new RegExp(`${identifier}\\s*:\\s*([\\w<>\\[\\]|&]+)`));
1054
+ if (typeMatch?.[1]) return typeMatch[1];
1055
+ if (/=\s*(true|false)/.test(op.code)) return "boolean";
1056
+ if (/=\s*\d+/.test(op.code)) return "number";
1057
+ if (/=\s*['"`]/.test(op.code)) return "string";
1058
+ if (/=\s*\[/.test(op.code)) return "array";
1059
+ if (/=\s*\{/.test(op.code)) return "object";
1060
+ if (/=\s*new\s+(\w+)/.test(op.code)) {
1061
+ const match = op.code.match(/=\s*new\s+(\w+)/);
1062
+ return match?.[1] ? match[1] : "unknown";
1063
+ }
1064
+ }
1065
+ }
1066
+ return "unknown";
1067
+ }
1068
+ /**
1069
+ * Infer scope from operations
1070
+ */
1071
+ inferScope(operations) {
1072
+ const files = new Set(operations.map((op) => op.file));
1073
+ if (files.size === 1) return "local";
1074
+ if (files.size <= 3) return "module";
1075
+ return "global";
1076
+ }
1077
+ };
1078
+
1079
+ // src/analysis/chaos/chaos-analyzer.ts
1080
+ var ChaosAnalyzer = class {
1081
+ detector;
1082
+ storage;
1083
+ _csharpPatterns = [];
1084
+ constructor(storage) {
1085
+ this.storage = storage;
1086
+ this.detector = new StateDetector(storage);
1087
+ }
1088
+ /** C# anti-patterns detected in last analyze() call */
1089
+ get csharpPatterns() {
1090
+ return this._csharpPatterns;
1091
+ }
1092
+ async analyze(options) {
1093
+ const t0 = performance.now();
1094
+ const allEntities = await this.storage.getAllEntities();
1095
+ const tEntities = performance.now();
1096
+ log.i("CHAOS", "1_getAllEntities", { count: allEntities.length, ms: +(tEntities - t0).toFixed(0) });
1097
+ const allRelationships = await this.storage.getAllRelationships();
1098
+ const relLookup = buildRelationshipLookup(allRelationships);
1099
+ const tRels = performance.now();
1100
+ log.i("CHAOS", "1b_getAllRelationships", {
1101
+ count: allRelationships.length,
1102
+ indexKeys: relLookup.size,
1103
+ ms: +(tRels - tEntities).toFixed(0)
1104
+ });
1105
+ const patterns = await this.detector.detectPatterns(options, allEntities, relLookup);
1106
+ const tPatterns = performance.now();
1107
+ log.i("CHAOS", "2_detectPatterns", { patterns: patterns.length, ms: +(tPatterns - tEntities).toFixed(0) });
1108
+ const results = [];
1109
+ try {
1110
+ this._csharpPatterns = detectCSharpChaosPatterns(allEntities);
1111
+ } catch {
1112
+ this._csharpPatterns = [];
1113
+ }
1114
+ const tCsharp = performance.now();
1115
+ log.i("CHAOS", "3_csharpPatterns", { count: this._csharpPatterns.length, ms: +(tCsharp - tPatterns).toFixed(0) });
1116
+ for (const pattern of patterns) {
1117
+ const { raceAnalysis } = pattern;
1118
+ const files = new Set(pattern.operations.map((op) => op.file));
1119
+ const chaosScore = this.calculateChaosScore(pattern, raceAnalysis);
1120
+ const divergenceRisk = this.assessDivergenceRisk(pattern, raceAnalysis);
1121
+ const hotspots = this.generateHotspots(pattern, raceAnalysis);
1122
+ const quickFixes = this.generateQuickFixes(pattern, raceAnalysis);
1123
+ const strategy = this.chooseStrategy(pattern, raceAnalysis);
1124
+ const summary = {
1125
+ overview: {
1126
+ stateIdentifier: pattern.identifier,
1127
+ totalFiles: files.size,
1128
+ totalOperations: pattern.operations.length,
1129
+ chaosScore,
1130
+ divergenceRisk,
1131
+ raceRisk: raceAnalysis.raceRisk,
1132
+ writers: raceAnalysis.writers,
1133
+ conflicts: raceAnalysis.conflicts.length
1134
+ },
1135
+ hotspots,
1136
+ raceConflicts: raceAnalysis.conflicts.map((c) => ({
1137
+ pattern: c.pattern,
1138
+ severity: c.severity,
1139
+ locations: c.locations.map((l) => `${l.file}:${l.line}`),
1140
+ suggestion: c.suggestion
1141
+ })),
1142
+ quickFixes,
1143
+ refactoringStrategy: strategy,
1144
+ estimatedEffort: this.estimateEffort(pattern, raceAnalysis)
1145
+ };
1146
+ const writeOps = pattern.operations.filter(
1147
+ (op) => op.operationType === "write" || op.operationType === "initialize" || op.operationType === "emit"
1148
+ );
1149
+ const mutationFiles = new Set(writeOps.map((op) => op.file));
1150
+ results.push({
1151
+ statePattern: pattern,
1152
+ flowMap: {
1153
+ stateIdentifier: pattern.identifier,
1154
+ origin: {
1155
+ file: pattern.operations[0]?.file || "",
1156
+ line: pattern.operations[0]?.line || 0,
1157
+ entityName: pattern.identifier,
1158
+ type: "initialization",
1159
+ code: pattern.operations[0]?.code || ""
1160
+ },
1161
+ nodes: [],
1162
+ edges: [],
1163
+ totalOperations: pattern.operations.length,
1164
+ mutationPoints: raceAnalysis.writers,
1165
+ maxDepth: Math.max(...pattern.operations.map((op) => op.depth), 0)
1166
+ },
1167
+ metrics: {
1168
+ stateIdentifier: pattern.identifier,
1169
+ coupling: {
1170
+ score: Math.min(100, files.size * 15),
1171
+ affectedComponents: pattern.operations.length,
1172
+ sharedStateCount: pattern.relatedIdentifiers.length,
1173
+ bidirectionalBindings: 0
1174
+ },
1175
+ defensive: {
1176
+ nullChecks: pattern.operations.filter((op) => op.isDefensive).length,
1177
+ typeGuards: 0,
1178
+ defaultValues: 0,
1179
+ tryCatch: 0,
1180
+ localCopies: 0
1181
+ },
1182
+ mutationSpread: {
1183
+ totalMutations: raceAnalysis.writers,
1184
+ filesWithMutations: mutationFiles.size,
1185
+ componentsWithMutations: raceAnalysis.mutations.length,
1186
+ averageMutationsPerComponent: mutationFiles.size > 0 ? raceAnalysis.writers / mutationFiles.size : 0
1187
+ },
1188
+ divergenceRisk,
1189
+ complexity: {
1190
+ cyclomaticComplexity: 0,
1191
+ cognitiveComplexity: 0
1192
+ },
1193
+ score: chaosScore
1194
+ },
1195
+ raceAnalysis,
1196
+ refactoringPlan: {
1197
+ stateIdentifier: pattern.identifier,
1198
+ currentMetrics: {
1199
+ stateIdentifier: pattern.identifier,
1200
+ coupling: {
1201
+ score: Math.min(100, files.size * 15),
1202
+ affectedComponents: pattern.operations.length,
1203
+ sharedStateCount: pattern.relatedIdentifiers.length,
1204
+ bidirectionalBindings: 0
1205
+ },
1206
+ defensive: {
1207
+ nullChecks: pattern.operations.filter((op) => op.isDefensive).length,
1208
+ typeGuards: 0,
1209
+ defaultValues: 0,
1210
+ tryCatch: 0,
1211
+ localCopies: 0
1212
+ },
1213
+ mutationSpread: {
1214
+ totalMutations: raceAnalysis.writers,
1215
+ filesWithMutations: mutationFiles.size,
1216
+ componentsWithMutations: raceAnalysis.mutations.length,
1217
+ averageMutationsPerComponent: mutationFiles.size > 0 ? raceAnalysis.writers / mutationFiles.size : 0
1218
+ },
1219
+ divergenceRisk,
1220
+ complexity: {
1221
+ cyclomaticComplexity: 0,
1222
+ cognitiveComplexity: 0
1223
+ },
1224
+ score: chaosScore
1225
+ },
1226
+ strategy,
1227
+ reasoning: this.generateReasoning(strategy, raceAnalysis),
1228
+ steps: [],
1229
+ newComponents: [],
1230
+ benefits: {
1231
+ reducedCoupling: strategy === "Service" ? 40 : strategy === "ImmutableState" ? 50 : 60,
1232
+ reducedMutations: Math.floor(raceAnalysis.writers / 2),
1233
+ reducedComplexity: raceAnalysis.conflicts.length > 0 ? 50 : 30,
1234
+ improvedTestability: true,
1235
+ improvedMaintainability: true,
1236
+ estimatedLOCChange: 0
1237
+ },
1238
+ risks: raceAnalysis.conflicts.length > 0 ? ["\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0442\u0449\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438"] : [],
1239
+ prerequisites: []
1240
+ },
1241
+ summary,
1242
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1243
+ });
1244
+ }
1245
+ log.i("CHAOS", "4_total", {
1246
+ entities: allEntities.length,
1247
+ statePatterns: patterns.length,
1248
+ csharpPatterns: this._csharpPatterns.length,
1249
+ results: results.length,
1250
+ totalMs: +(performance.now() - t0).toFixed(0)
1251
+ });
1252
+ return results;
1253
+ }
1254
+ /**
1255
+ * Format results for AI consumption (compact, token-efficient)
1256
+ */
1257
+ formatForAI(results) {
1258
+ if (results.length === 0 && this._csharpPatterns.length === 0) {
1259
+ return "No state patterns detected.";
1260
+ }
1261
+ const output = ["# State Chaos Analysis\n"];
1262
+ for (const result of results) {
1263
+ const { summary, raceAnalysis } = result;
1264
+ const { overview } = summary;
1265
+ const riskEmoji = this.getRiskEmoji(overview.raceRisk);
1266
+ output.push(`## ${riskEmoji} ${overview.stateIdentifier}`);
1267
+ output.push(
1268
+ `Chaos: ${overview.chaosScore}/100 | Files: ${overview.totalFiles} | Writers: ${overview.writers} | Race: ${overview.raceRisk}`
1269
+ );
1270
+ if (raceAnalysis.conflicts.length > 0) {
1271
+ output.push("\n**\u26A0\uFE0F Race Conditions:**");
1272
+ for (const conflict of raceAnalysis.conflicts) {
1273
+ output.push(`- **${conflict.pattern}** (${conflict.severity})`);
1274
+ output.push(` ${conflict.explanation}`);
1275
+ output.push(` \u{1F4CD} ${conflict.locations.map((l) => `${l.entityName}:${l.line}`).join(", ")}`);
1276
+ output.push(` \u{1F4A1} ${conflict.suggestion}`);
1277
+ }
1278
+ }
1279
+ if (summary.quickFixes.length > 0) {
1280
+ output.push("\n**Quick Fixes:**");
1281
+ for (const fix of summary.quickFixes) {
1282
+ output.push(`- ${fix}`);
1283
+ }
1284
+ }
1285
+ output.push(`
1286
+ **Strategy:** ${summary.refactoringStrategy}`);
1287
+ output.push(`**Effort:** ${summary.estimatedEffort}
1288
+ `);
1289
+ output.push("---\n");
1290
+ }
1291
+ if (this._csharpPatterns.length > 0) {
1292
+ output.push(this.formatCSharpPatternsForAI());
1293
+ }
1294
+ return output.join("\n");
1295
+ }
1296
+ /**
1297
+ * Format C# anti-patterns for AI consumption
1298
+ */
1299
+ formatCSharpPatternsForAI() {
1300
+ const lines = ["\n# C# Anti-Patterns\n"];
1301
+ const byPattern = /* @__PURE__ */ new Map();
1302
+ for (const p of this._csharpPatterns) {
1303
+ const arr = byPattern.get(p.pattern) || [];
1304
+ arr.push(p);
1305
+ byPattern.set(p.pattern, arr);
1306
+ }
1307
+ const severityEmoji = {
1308
+ critical: "\u{1F534}",
1309
+ high: "\u{1F7E0}",
1310
+ medium: "\u{1F7E1}"
1311
+ };
1312
+ for (const [pattern, items] of byPattern) {
1313
+ const sev = items[0].severity;
1314
+ lines.push(`## ${severityEmoji[sev] || "\u26AA"} ${pattern} (${items.length} found)`);
1315
+ for (const item of items.slice(0, 10)) {
1316
+ lines.push(`- **${item.entityName}** ${item.filePath}:${item.line}`);
1317
+ lines.push(` ${item.description}`);
1318
+ lines.push(` \u{1F4A1} ${item.suggestion}`);
1319
+ }
1320
+ if (items.length > 10) {
1321
+ lines.push(` ... and ${items.length - 10} more`);
1322
+ }
1323
+ lines.push("");
1324
+ }
1325
+ return lines.join("\n");
1326
+ }
1327
+ /**
1328
+ * Format single result with full details
1329
+ */
1330
+ formatDetailed(result) {
1331
+ const { statePattern: pattern, raceAnalysis, metrics, summary } = result;
1332
+ const lines = [];
1333
+ lines.push(`# \u0410\u043D\u0430\u043B\u0438\u0437 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F: ${pattern.identifier}`);
1334
+ lines.push(`**\u0422\u0438\u043F:** ${pattern.type || "unknown"}`);
1335
+ lines.push(`**Scope:** ${pattern.scope}`);
1336
+ lines.push("");
1337
+ lines.push("## \u041C\u0435\u0442\u0440\u0438\u043A\u0438");
1338
+ lines.push(`- **Chaos Score:** ${metrics.score}/100`);
1339
+ lines.push(`- **Divergence Risk:** ${metrics.divergenceRisk}`);
1340
+ lines.push(`- **Race Risk:** ${raceAnalysis.raceRisk}`);
1341
+ lines.push(`- **\u041E\u043F\u0435\u0440\u0430\u0446\u0438\u0439:** ${pattern.operations.length}`);
1342
+ lines.push(`- **\u0424\u0430\u0439\u043B\u043E\u0432:** ${new Set(pattern.operations.map((op) => op.file)).size}`);
1343
+ lines.push(`- **\u041F\u0438\u0441\u0430\u0442\u0435\u043B\u0435\u0439:** ${raceAnalysis.writers} (async: ${raceAnalysis.asyncWriters})`);
1344
+ lines.push("");
1345
+ if (raceAnalysis.conflicts.length > 0) {
1346
+ lines.push("## \u26A0\uFE0F \u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u043D\u044B\u0435 \u0433\u043E\u043D\u043A\u0438");
1347
+ for (const conflict of raceAnalysis.conflicts) {
1348
+ lines.push(`### ${conflict.pattern} (${conflict.severity})`);
1349
+ lines.push(`**\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435:** ${conflict.description}`);
1350
+ lines.push(`**\u041E\u0431\u044A\u044F\u0441\u043D\u0435\u043D\u0438\u0435:** ${conflict.explanation}`);
1351
+ lines.push("**\u041B\u043E\u043A\u0430\u0446\u0438\u0438:**");
1352
+ for (const loc of conflict.locations) {
1353
+ lines.push(`- \`${loc.entityName}\` \u0432 ${loc.file}:${loc.line}`);
1354
+ if (loc.condition) {
1355
+ lines.push(` \u0423\u0441\u043B\u043E\u0432\u0438\u0435: \`${loc.condition}\``);
1356
+ }
1357
+ }
1358
+ lines.push(`**\u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u044F:** ${conflict.suggestion}`);
1359
+ lines.push("");
1360
+ }
1361
+ }
1362
+ if (raceAnalysis.mutations.length > 0) {
1363
+ lines.push("## \u0422\u043E\u0447\u043A\u0438 \u043C\u0443\u0442\u0430\u0446\u0438\u0438");
1364
+ for (const mut of raceAnalysis.mutations) {
1365
+ const flags = [];
1366
+ if (mut.isAsync) flags.push("async");
1367
+ if (!mut.hasLock) flags.push("no-lock");
1368
+ const flagStr = flags.length > 0 ? ` [${flags.join(", ")}]` : "";
1369
+ lines.push(`- **${mut.entityName}** (${mut.mutationType})${flagStr}`);
1370
+ lines.push(` ${mut.file}:${mut.line}`);
1371
+ if (mut.condition) {
1372
+ lines.push(` \u0423\u0441\u043B\u043E\u0432\u0438\u0435: \`${mut.condition}\``);
1373
+ }
1374
+ }
1375
+ lines.push("");
1376
+ }
1377
+ if (pattern.relatedIdentifiers.length > 0) {
1378
+ lines.push("## \u0421\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u0438\u0434\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440\u044B");
1379
+ lines.push(pattern.relatedIdentifiers.map((id) => `\`${id}\``).join(", "));
1380
+ lines.push("");
1381
+ }
1382
+ lines.push("## \u0420\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0430\u0446\u0438\u0438");
1383
+ lines.push(`**\u0421\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044F:** ${summary.refactoringStrategy}`);
1384
+ lines.push(`**\u041E\u0431\u043E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u0435:** ${result.refactoringPlan.reasoning}`);
1385
+ lines.push(`**Effort:** ${summary.estimatedEffort}`);
1386
+ return lines.join("\n");
1387
+ }
1388
+ /**
1389
+ * Calculate overall chaos score combining spread and race risk
1390
+ */
1391
+ calculateChaosScore(pattern, raceAnalysis) {
1392
+ const baseScore = Math.min(50, pattern.operations.length * 3);
1393
+ const fileScore = Math.min(20, new Set(pattern.operations.map((op) => op.file)).size * 5);
1394
+ const raceScore = this.raceRiskToScore(raceAnalysis.raceRisk);
1395
+ return Math.min(100, baseScore + fileScore + raceScore);
1396
+ }
1397
+ raceRiskToScore(risk) {
1398
+ const scores = {
1399
+ none: 0,
1400
+ low: 5,
1401
+ medium: 15,
1402
+ high: 25,
1403
+ critical: 30
1404
+ };
1405
+ return scores[risk];
1406
+ }
1407
+ /**
1408
+ * Assess divergence risk combining chaos and race factors
1409
+ */
1410
+ assessDivergenceRisk(pattern, raceAnalysis) {
1411
+ const opCount = pattern.operations.length;
1412
+ const fileCount = new Set(pattern.operations.map((op) => op.file)).size;
1413
+ const raceRisk = raceAnalysis.raceRisk;
1414
+ if (raceRisk === "critical") return "critical";
1415
+ if (raceRisk === "high" && opCount >= 10) return "critical";
1416
+ if (raceRisk === "high" || fileCount >= 5 && opCount >= 15) return "high";
1417
+ if (raceRisk === "medium" || fileCount >= 3 && opCount >= 8) return "medium";
1418
+ return "low";
1419
+ }
1420
+ /**
1421
+ * Generate hotspots from pattern and race analysis
1422
+ */
1423
+ generateHotspots(_pattern, raceAnalysis) {
1424
+ const hotspots = [];
1425
+ for (const conflict of raceAnalysis.conflicts) {
1426
+ for (const loc of conflict.locations) {
1427
+ hotspots.push({
1428
+ file: loc.file,
1429
+ entity: loc.entityName,
1430
+ issues: [conflict.pattern, conflict.description],
1431
+ priority: conflict.severity === "critical" || conflict.severity === "high" ? "high" : "medium"
1432
+ });
1433
+ }
1434
+ }
1435
+ for (const mut of raceAnalysis.mutations) {
1436
+ if (mut.isAsync && !mut.hasLock) {
1437
+ const existing = hotspots.find((h) => h.file === mut.file && h.entity === mut.entityName);
1438
+ if (existing) {
1439
+ existing.issues.push("unprotected async write");
1440
+ } else {
1441
+ hotspots.push({
1442
+ file: mut.file,
1443
+ entity: mut.entityName,
1444
+ issues: ["unprotected async write"],
1445
+ priority: "medium"
1446
+ });
1447
+ }
1448
+ }
1449
+ }
1450
+ return hotspots.sort((a, b) => a.priority === "high" ? -1 : b.priority === "high" ? 1 : 0).slice(0, 5);
1451
+ }
1452
+ /**
1453
+ * Generate quick fix suggestions
1454
+ */
1455
+ generateQuickFixes(pattern, raceAnalysis) {
1456
+ const fixes = [];
1457
+ if (raceAnalysis.conflicts.some((c) => c.pattern === "competing-resets")) {
1458
+ fixes.push("\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u0438\u0442\u0435 \u0444\u0443\u043D\u043A\u0446\u0438\u0438 \u0441\u0431\u0440\u043E\u0441\u0430 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u0432 \u0435\u0434\u0438\u043D\u044B\u0439 \u043C\u0435\u0442\u043E\u0434 reset()");
1459
+ }
1460
+ if (raceAnalysis.asyncWriters > 0 && raceAnalysis.unprotectedWriters > 0) {
1461
+ fixes.push("\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u043C\u044C\u044E\u0442\u0435\u043A\u0441 \u0438\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 queue \u0434\u043B\u044F \u0430\u0441\u0438\u043D\u0445\u0440\u043E\u043D\u043D\u044B\u0445 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0439");
1462
+ }
1463
+ if (raceAnalysis.conflicts.some((c) => c.pattern === "check-then-act")) {
1464
+ fixes.push("\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0430\u0442\u043E\u043C\u0430\u0440\u043D\u044B\u0435 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u043B\u0438 compare-and-swap \u043F\u0430\u0442\u0442\u0435\u0440\u043D");
1465
+ }
1466
+ if (pattern.relatedIdentifiers.length > 2) {
1467
+ fixes.push(`\u041A\u043E\u043D\u0441\u043E\u043B\u0438\u0434\u0438\u0440\u0443\u0439\u0442\u0435 \u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435: ${pattern.relatedIdentifiers.slice(0, 3).join(", ")}`);
1468
+ }
1469
+ if (new Set(pattern.operations.map((op) => op.file)).size > 3) {
1470
+ fixes.push("\u0421\u043E\u0437\u0434\u0430\u0439\u0442\u0435 \u0446\u0435\u043D\u0442\u0440\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0434\u043B\u044F \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435\u043C");
1471
+ }
1472
+ const allCode = pattern.operations.map((op) => op.code).join("\n");
1473
+ if (/async\s+void\b/.test(allCode)) {
1474
+ fixes.push("\u0417\u0430\u043C\u0435\u043D\u0438\u0442\u0435 'async void' \u043D\u0430 'async Task' \u2014 async void \u043D\u0435 \u043E\u0431\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0435\u0442 \u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F");
1475
+ }
1476
+ if (/static\b/.test(allCode) && !/readonly|const|ConcurrentDictionary/.test(allCode)) {
1477
+ fixes.push("\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 ConcurrentDictionary \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u043C\u0435\u0441\u0442\u0438\u0442\u0435 state \u0432 DI-managed service");
1478
+ }
1479
+ if (/\.ctor|constructor/i.test(pattern.identifier) && pattern.operations.length > 10) {
1480
+ fixes.push("\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u0435 god-service \u043D\u0430 \u043C\u0435\u043D\u044C\u0448\u0438\u0435 \u0441\u0444\u043E\u043A\u0443\u0441\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044B");
1481
+ }
1482
+ if (/async\s+Task/.test(allCode) && !/CancellationToken/.test(allCode)) {
1483
+ fixes.push("\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 CancellationToken \u0432 async \u043C\u0435\u0442\u043E\u0434\u044B");
1484
+ }
1485
+ return fixes;
1486
+ }
1487
+ /**
1488
+ * Choose refactoring strategy based on analysis
1489
+ */
1490
+ chooseStrategy(pattern, raceAnalysis) {
1491
+ if (raceAnalysis.raceRisk === "critical" || raceAnalysis.conflicts.length >= 3) {
1492
+ return "StateMachine";
1493
+ }
1494
+ const allCode = pattern.operations.map((op) => op.code).join("\n");
1495
+ const isCSharpContext = /\bTask\b|\basync\s+Task\b|\bawait\b.*\.\w+Async\b/.test(allCode) || pattern.operations.some((op) => op.file.endsWith(".cs"));
1496
+ if (isCSharpContext) {
1497
+ if (/static\b/.test(allCode) && /Singleton|AddSingleton/.test(allCode)) {
1498
+ return "DI_Lifetime";
1499
+ }
1500
+ if (raceAnalysis.raceRisk === "high" && raceAnalysis.asyncWriters > 2) {
1501
+ return "Channel";
1502
+ }
1503
+ if (raceAnalysis.writers > 5 && raceAnalysis.unprotectedWriters > 3) {
1504
+ return "ImmutableState";
1505
+ }
1506
+ }
1507
+ if (raceAnalysis.raceRisk === "high" && raceAnalysis.asyncWriters > 2) {
1508
+ return "EventBus";
1509
+ }
1510
+ const files = new Set(pattern.operations.map((op) => op.file)).size;
1511
+ if (files > 5) {
1512
+ return "Store";
1513
+ }
1514
+ if (pattern.operations.some((op) => op.angularPattern === "behavior_subject")) {
1515
+ return "Service";
1516
+ }
1517
+ return "Service";
1518
+ }
1519
+ /**
1520
+ * Generate reasoning for chosen strategy
1521
+ */
1522
+ generateReasoning(strategy, raceAnalysis) {
1523
+ const reasons = {
1524
+ StateMachine: `\u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u044B \u043A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0433\u043E\u043D\u043A\u0438 (${raceAnalysis.conflicts.length} \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u043E\u0432). State machine \u043E\u0431\u0435\u0441\u043F\u0435\u0447\u0438\u0442 \u044F\u0432\u043D\u044B\u0435 \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u044B \u0438 \u043F\u0440\u0435\u0434\u043E\u0442\u0432\u0440\u0430\u0442\u0438\u0442 invalid states.`,
1525
+ EventBus: `${raceAnalysis.asyncWriters} \u0430\u0441\u0438\u043D\u0445\u0440\u043E\u043D\u043D\u044B\u0445 \u043F\u0438\u0441\u0430\u0442\u0435\u043B\u0435\u0439 \u0441\u043E\u0437\u0434\u0430\u044E\u0442 \u043D\u0435\u043F\u0440\u0435\u0434\u0441\u043A\u0430\u0437\u0443\u0435\u043C\u043E\u0435 \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435. Event-driven \u0430\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430 \u0443\u043F\u043E\u0440\u044F\u0434\u043E\u0447\u0438\u0442 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F.`,
1526
+ Store: "\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0440\u0430\u0437\u0431\u0440\u043E\u0441\u0430\u043D\u043E \u043F\u043E \u043C\u043D\u043E\u0433\u0438\u043C \u0444\u0430\u0439\u043B\u0430\u043C. \u0426\u0435\u043D\u0442\u0440\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u043D\u043D\u044B\u0439 store \u043E\u0431\u0435\u0441\u043F\u0435\u0447\u0438\u0442 single source of truth.",
1527
+ Service: "\u0426\u0435\u043D\u0442\u0440\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0443\u043F\u0440\u043E\u0441\u0442\u0438\u0442 \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435\u043C \u0438 \u0434\u043E\u0431\u0430\u0432\u0438\u0442 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u0430.",
1528
+ Context: "Context API \u043F\u043E\u0434\u043E\u0439\u0434\u0451\u0442 \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u0447\u0435\u0440\u0435\u0437 \u0434\u0435\u0440\u0435\u0432\u043E \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432.",
1529
+ Signal: "Angular Signals \u043E\u0431\u0435\u0441\u043F\u0435\u0447\u0430\u0442 \u0440\u0435\u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0441\u0442\u044C \u0441 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u043C \u043E\u0442\u0441\u043B\u0435\u0436\u0438\u0432\u0430\u043D\u0438\u0435\u043C \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439.",
1530
+ StateManager: "\u041A\u0430\u0441\u0442\u043E\u043C\u043D\u044B\u0439 StateManager \u043F\u043E\u0437\u0432\u043E\u043B\u0438\u0442 \u0440\u0435\u0430\u043B\u0438\u0437\u043E\u0432\u0430\u0442\u044C \u0441\u043F\u0435\u0446\u0438\u0444\u0438\u0447\u043D\u0443\u044E \u043B\u043E\u0433\u0438\u043A\u0443 \u0441\u0438\u043D\u0445\u0440\u043E\u043D\u0438\u0437\u0430\u0446\u0438\u0438.",
1531
+ DI_Lifetime: "Mutable state \u0432 Singleton-\u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442 \u0433\u043E\u043D\u043A\u0438. \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u0435 lifetime \u043D\u0430 Scoped \u0438\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 ImmutableDictionary.",
1532
+ Channel: `${raceAnalysis.asyncWriters} async producer/consumer \u043D\u0443\u0436\u0434\u0430\u044E\u0442\u0441\u044F \u0432 \u0443\u043F\u043E\u0440\u044F\u0434\u043E\u0447\u0435\u043D\u043D\u043E\u0439 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0435. System.Threading.Channels \u043E\u0431\u0435\u0441\u043F\u0435\u0447\u0438\u0442 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u0443\u044E \u043E\u0447\u0435\u0440\u0435\u0434\u044C.`,
1533
+ ImmutableState: "\u041C\u043D\u043E\u0436\u0435\u0441\u0442\u0432\u043E \u043D\u0435\u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0438\u0440\u0443\u0435\u043C\u044B\u0445 \u043C\u0443\u0442\u0430\u0446\u0438\u0439. \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 record types \u0438 ImmutableCollections \u0434\u043B\u044F \u043F\u0440\u0435\u0434\u043E\u0442\u0432\u0440\u0430\u0449\u0435\u043D\u0438\u044F \u0433\u043E\u043D\u043E\u043A."
1534
+ };
1535
+ return reasons[strategy];
1536
+ }
1537
+ /**
1538
+ * Estimate implementation effort
1539
+ */
1540
+ estimateEffort(pattern, raceAnalysis) {
1541
+ const files = new Set(pattern.operations.map((op) => op.file)).size;
1542
+ const conflicts = raceAnalysis.conflicts.length;
1543
+ if (conflicts >= 3 || files > 10) return "\u0412\u044B\u0441\u043E\u043A\u0430\u044F (1-2 \u043D\u0435\u0434\u0435\u043B\u0438)";
1544
+ if (conflicts >= 1 || files > 5) return "\u0421\u0440\u0435\u0434\u043D\u044F\u044F (2-5 \u0434\u043D\u0435\u0439)";
1545
+ return "\u041D\u0438\u0437\u043A\u0430\u044F (1-2 \u0434\u043D\u044F)";
1546
+ }
1547
+ /**
1548
+ * Get emoji for risk level
1549
+ */
1550
+ getRiskEmoji(risk) {
1551
+ const emojis = {
1552
+ none: "\u2705",
1553
+ low: "\u{1F7E2}",
1554
+ medium: "\u{1F7E1}",
1555
+ high: "\u{1F7E0}",
1556
+ critical: "\u{1F534}"
1557
+ };
1558
+ return emojis[risk];
1559
+ }
1560
+ };
1561
+
1562
+ export { CSHARP_ASYNC_APIS, CSHARP_LOCK_PATTERNS, ChaosAnalyzer, RaceDetector, StateDetector, detectCSharpChaosPatterns, isCSharpStateIdentifier, isStateIdentifier };
1563
+ //# sourceMappingURL=chaos-W3XRVJ7K.js.map
1564
+ //# sourceMappingURL=chaos-W3XRVJ7K.js.map