@kevinrabun/judges 3.56.0 → 3.58.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 (70) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +112 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/assertion-density.d.ts +5 -0
  6. package/dist/commands/assertion-density.d.ts.map +1 -0
  7. package/dist/commands/assertion-density.js +264 -0
  8. package/dist/commands/assertion-density.js.map +1 -0
  9. package/dist/commands/async-safety.d.ts +5 -0
  10. package/dist/commands/async-safety.d.ts.map +1 -0
  11. package/dist/commands/async-safety.js +267 -0
  12. package/dist/commands/async-safety.js.map +1 -0
  13. package/dist/commands/cache-audit.d.ts +5 -0
  14. package/dist/commands/cache-audit.d.ts.map +1 -0
  15. package/dist/commands/cache-audit.js +220 -0
  16. package/dist/commands/cache-audit.js.map +1 -0
  17. package/dist/commands/clone-detect.d.ts +5 -0
  18. package/dist/commands/clone-detect.d.ts.map +1 -0
  19. package/dist/commands/clone-detect.js +233 -0
  20. package/dist/commands/clone-detect.js.map +1 -0
  21. package/dist/commands/comment-drift.d.ts +5 -0
  22. package/dist/commands/comment-drift.d.ts.map +1 -0
  23. package/dist/commands/comment-drift.js +229 -0
  24. package/dist/commands/comment-drift.js.map +1 -0
  25. package/dist/commands/contract-verify.d.ts +5 -0
  26. package/dist/commands/contract-verify.d.ts.map +1 -0
  27. package/dist/commands/contract-verify.js +317 -0
  28. package/dist/commands/contract-verify.js.map +1 -0
  29. package/dist/commands/dead-code-detect.d.ts +5 -0
  30. package/dist/commands/dead-code-detect.d.ts.map +1 -0
  31. package/dist/commands/dead-code-detect.js +256 -0
  32. package/dist/commands/dead-code-detect.js.map +1 -0
  33. package/dist/commands/encoding-safety.d.ts +5 -0
  34. package/dist/commands/encoding-safety.d.ts.map +1 -0
  35. package/dist/commands/encoding-safety.js +276 -0
  36. package/dist/commands/encoding-safety.js.map +1 -0
  37. package/dist/commands/error-ux.d.ts +5 -0
  38. package/dist/commands/error-ux.d.ts.map +1 -0
  39. package/dist/commands/error-ux.js +253 -0
  40. package/dist/commands/error-ux.js.map +1 -0
  41. package/dist/commands/event-leak.d.ts +5 -0
  42. package/dist/commands/event-leak.d.ts.map +1 -0
  43. package/dist/commands/event-leak.js +263 -0
  44. package/dist/commands/event-leak.js.map +1 -0
  45. package/dist/commands/idempotency-audit.d.ts +5 -0
  46. package/dist/commands/idempotency-audit.d.ts.map +1 -0
  47. package/dist/commands/idempotency-audit.js +223 -0
  48. package/dist/commands/idempotency-audit.js.map +1 -0
  49. package/dist/commands/input-guard.d.ts +5 -0
  50. package/dist/commands/input-guard.d.ts.map +1 -0
  51. package/dist/commands/input-guard.js +256 -0
  52. package/dist/commands/input-guard.js.map +1 -0
  53. package/dist/commands/privilege-path.d.ts +5 -0
  54. package/dist/commands/privilege-path.d.ts.map +1 -0
  55. package/dist/commands/privilege-path.js +234 -0
  56. package/dist/commands/privilege-path.js.map +1 -0
  57. package/dist/commands/state-integrity.d.ts +5 -0
  58. package/dist/commands/state-integrity.d.ts.map +1 -0
  59. package/dist/commands/state-integrity.js +284 -0
  60. package/dist/commands/state-integrity.js.map +1 -0
  61. package/dist/commands/timeout-audit.d.ts +5 -0
  62. package/dist/commands/timeout-audit.d.ts.map +1 -0
  63. package/dist/commands/timeout-audit.js +211 -0
  64. package/dist/commands/timeout-audit.js.map +1 -0
  65. package/dist/commands/type-boundary.d.ts +5 -0
  66. package/dist/commands/type-boundary.d.ts.map +1 -0
  67. package/dist/commands/type-boundary.js +236 -0
  68. package/dist/commands/type-boundary.js.map +1 -0
  69. package/package.json +1 -1
  70. package/server.json +2 -2
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Error UX — audit user-facing error messages for actionability, consistency, and info leakage.
3
+ */
4
+ import { readFileSync, readdirSync, statSync } from "fs";
5
+ import { join, extname } from "path";
6
+ // ─── File Collection ────────────────────────────────────────────────────────
7
+ const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs"]);
8
+ function collectFiles(dir, max = 300) {
9
+ const files = [];
10
+ function walk(d) {
11
+ if (files.length >= max)
12
+ return;
13
+ let entries;
14
+ try {
15
+ entries = readdirSync(d);
16
+ }
17
+ catch {
18
+ return;
19
+ }
20
+ for (const e of entries) {
21
+ if (files.length >= max)
22
+ return;
23
+ if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
24
+ continue;
25
+ const full = join(d, e);
26
+ try {
27
+ if (statSync(full).isDirectory())
28
+ walk(full);
29
+ else if (CODE_EXTS.has(extname(full)))
30
+ files.push(full);
31
+ }
32
+ catch {
33
+ /* skip */
34
+ }
35
+ }
36
+ }
37
+ walk(dir);
38
+ return files;
39
+ }
40
+ // ─── Analysis ───────────────────────────────────────────────────────────────
41
+ function analyzeFile(filepath) {
42
+ const issues = [];
43
+ let content;
44
+ try {
45
+ content = readFileSync(filepath, "utf-8");
46
+ }
47
+ catch {
48
+ return issues;
49
+ }
50
+ const lines = content.split("\n");
51
+ // Skip test and fixture files
52
+ if (/\.test\.|\.spec\.|__test__|fixture|mock/i.test(filepath))
53
+ return issues;
54
+ for (let i = 0; i < lines.length; i++) {
55
+ const line = lines[i];
56
+ // Generic unhelpful error messages
57
+ if (/(?:throw\s+new\s+Error|res\.status.*\.(?:json|send))\s*\(\s*['"]([^'"]+)['"]\s*\)/.test(line)) {
58
+ const msgMatch = line.match(/['"]([^'"]+)['"]/);
59
+ if (msgMatch) {
60
+ const msg = msgMatch[1];
61
+ if (/^(?:error|something went wrong|an error occurred|internal error|unknown error|bad request|failed|invalid)$/i.test(msg.trim())) {
62
+ issues.push({
63
+ file: filepath,
64
+ line: i + 1,
65
+ issue: "Generic error message",
66
+ severity: "medium",
67
+ detail: `"${msg}" — provide actionable guidance: what went wrong and how to fix it`,
68
+ });
69
+ }
70
+ }
71
+ }
72
+ // Stack trace leaked to client
73
+ if (/(?:res\.(?:json|send)|response\.(?:json|send))\s*\(/.test(line)) {
74
+ const block = lines.slice(i, Math.min(i + 5, lines.length)).join("\n");
75
+ if (/\.stack|stackTrace|stack_trace|err\.message|error\.message/i.test(block)) {
76
+ if (!/production|NODE_ENV|process\.env/i.test(block)) {
77
+ issues.push({
78
+ file: filepath,
79
+ line: i + 1,
80
+ issue: "Stack trace may leak to client",
81
+ severity: "high",
82
+ detail: "Error details sent in response without environment check — may expose internals in production",
83
+ });
84
+ }
85
+ }
86
+ }
87
+ // Internal paths leaked in error messages
88
+ if (/(?:throw|console\.error|res\..*(?:json|send))\s*\(/.test(line)) {
89
+ if (/(?:\/home\/|\/var\/|\/usr\/|C:\\|D:\\|\/opt\/|__dirname|__filename)/i.test(line)) {
90
+ issues.push({
91
+ file: filepath,
92
+ line: i + 1,
93
+ issue: "Internal file path in error output",
94
+ severity: "medium",
95
+ detail: "Server filesystem path in error — reveals deployment structure to attacker",
96
+ });
97
+ }
98
+ }
99
+ // SQL/DB error details sent to client
100
+ if (/catch\s*\(/.test(line)) {
101
+ const catchBlock = lines.slice(i, Math.min(i + 10, lines.length)).join("\n");
102
+ if (/(?:sql|query|database|ECONNREFUSED|ETIMEDOUT)/i.test(catchBlock)) {
103
+ if (/res\.(?:json|send|status)|response\.(?:json|send)/i.test(catchBlock)) {
104
+ if (/err\.message|error\.message|e\.message/i.test(catchBlock)) {
105
+ issues.push({
106
+ file: filepath,
107
+ line: i + 1,
108
+ issue: "Database error details sent to client",
109
+ severity: "high",
110
+ detail: "DB error message forwarded to response — may expose table names, SQL syntax, or connection info",
111
+ });
112
+ }
113
+ }
114
+ }
115
+ }
116
+ // Error message with jargon (internal codes exposed)
117
+ if (/(?:throw\s+new\s+Error|\.send|\.json)\s*\(/.test(line)) {
118
+ const msgMatch = line.match(/['"]([^'"]{10,})['"]/);
119
+ if (msgMatch) {
120
+ const msg = msgMatch[1];
121
+ if (/(?:ENOENT|EACCES|EPERM|SIGTERM|ENOMEM|OOM|segfault|null pointer|NullReferenceException)/i.test(msg)) {
122
+ issues.push({
123
+ file: filepath,
124
+ line: i + 1,
125
+ issue: "Technical jargon in user-facing error",
126
+ severity: "medium",
127
+ detail: `"${msg.slice(0, 50)}" — replace system-level jargon with plain language for end users`,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ // Empty catch blocks (silent failures)
133
+ if (/catch\s*\(\s*\w*\s*\)\s*\{\s*\}/.test(line) || /catch\s*\(\s*\w*\s*\)\s*\{\s*\/\//i.test(line)) {
134
+ issues.push({
135
+ file: filepath,
136
+ line: i + 1,
137
+ issue: "Silent error swallowing",
138
+ severity: "medium",
139
+ detail: "Empty catch block hides failures — log the error or surface a user-friendly message",
140
+ });
141
+ }
142
+ // HTTP status code mismatch
143
+ if (/res\.status\s*\(\s*(\d+)\s*\)/.test(line)) {
144
+ const statusMatch = line.match(/res\.status\s*\(\s*(\d+)\s*\)/);
145
+ const msgMatch2 = line.match(/['"]([^'"]+)['"]/);
146
+ if (statusMatch && msgMatch2) {
147
+ const status = parseInt(statusMatch[1], 10);
148
+ const msg = msgMatch2[1].toLowerCase();
149
+ if (status === 200 && /error|fail|invalid|denied/i.test(msg)) {
150
+ issues.push({
151
+ file: filepath,
152
+ line: i + 1,
153
+ issue: "Success status with error message",
154
+ severity: "medium",
155
+ detail: `HTTP 200 with error message "${msg.slice(0, 40)}" — use appropriate 4xx/5xx status code`,
156
+ });
157
+ }
158
+ if (status === 500 && /not found|unauthorized|forbidden/i.test(msg)) {
159
+ issues.push({
160
+ file: filepath,
161
+ line: i + 1,
162
+ issue: "Incorrect HTTP status code",
163
+ severity: "low",
164
+ detail: `HTTP 500 for "${msg.slice(0, 30)}" — use 404/401/403 for client errors`,
165
+ });
166
+ }
167
+ }
168
+ }
169
+ // Error without remediation hint
170
+ if (/throw\s+new\s+Error\s*\(\s*['"]([^'"]{15,})['"]\s*\)/.test(line)) {
171
+ const errMsg = line.match(/throw\s+new\s+Error\s*\(\s*['"]([^'"]+)['"]\s*\)/)?.[1] || "";
172
+ if (!/please|try|check|ensure|make sure|verify|see|refer|visit|use|run|set|configure|install/i.test(errMsg)) {
173
+ issues.push({
174
+ file: filepath,
175
+ line: i + 1,
176
+ issue: "Error without remediation hint",
177
+ severity: "low",
178
+ detail: `"${errMsg.slice(0, 50)}" — add guidance on how the user can resolve the issue`,
179
+ });
180
+ }
181
+ }
182
+ // Inconsistent error format (mix of throw/console.error/process.exit)
183
+ if (/process\.exit\s*\(\s*1\s*\)/.test(line)) {
184
+ const block = lines.slice(Math.max(0, i - 3), i + 1).join("\n");
185
+ if (!/console\.error|console\.log|logger|log\./i.test(block)) {
186
+ issues.push({
187
+ file: filepath,
188
+ line: i + 1,
189
+ issue: "process.exit without error message",
190
+ severity: "medium",
191
+ detail: "Process exits with code 1 but no error message — user sees nothing about what went wrong",
192
+ });
193
+ }
194
+ }
195
+ }
196
+ return issues;
197
+ }
198
+ // ─── CLI ────────────────────────────────────────────────────────────────────
199
+ export function runErrorUx(argv) {
200
+ if (argv.includes("--help") || argv.includes("-h")) {
201
+ console.log(`
202
+ judges error-ux — Audit user-facing error messages for quality and safety
203
+
204
+ Usage:
205
+ judges error-ux [dir]
206
+ judges error-ux src/ --format json
207
+
208
+ Options:
209
+ [dir] Directory to scan (default: .)
210
+ --format json JSON output
211
+ --help, -h Show this help
212
+
213
+ Checks: generic messages, stack trace leaks, internal path exposure, DB error forwarding,
214
+ jargon in user errors, silent catch blocks, HTTP status mismatches, missing remediation hints.
215
+ `);
216
+ return;
217
+ }
218
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
219
+ const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
220
+ const files = collectFiles(dir);
221
+ const allIssues = [];
222
+ for (const f of files)
223
+ allIssues.push(...analyzeFile(f));
224
+ const highCount = allIssues.filter((i) => i.severity === "high").length;
225
+ const medCount = allIssues.filter((i) => i.severity === "medium").length;
226
+ const score = Math.max(0, 100 - highCount * 8 - medCount * 3);
227
+ if (format === "json") {
228
+ console.log(JSON.stringify({
229
+ issues: allIssues,
230
+ score,
231
+ summary: { high: highCount, medium: medCount, total: allIssues.length },
232
+ timestamp: new Date().toISOString(),
233
+ }, null, 2));
234
+ }
235
+ else {
236
+ const badge = score >= 80 ? "✅ CLEAR" : score >= 50 ? "⚠️ MIXED" : "❌ POOR";
237
+ console.log(`\n Error UX Quality: ${badge} (${score}/100)\n ─────────────────────────────`);
238
+ if (allIssues.length === 0) {
239
+ console.log(" No error UX issues detected.\n");
240
+ return;
241
+ }
242
+ for (const issue of allIssues.slice(0, 25)) {
243
+ const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
244
+ console.log(` ${icon} ${issue.issue}`);
245
+ console.log(` ${issue.file}:${issue.line}`);
246
+ console.log(` ${issue.detail}`);
247
+ }
248
+ if (allIssues.length > 25)
249
+ console.log(` ... and ${allIssues.length - 25} more`);
250
+ console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
251
+ }
252
+ }
253
+ //# sourceMappingURL=error-ux.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-ux.js","sourceRoot":"","sources":["../../src/commands/error-ux.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAYrC,+EAA+E;AAE/E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAExF,SAAS,YAAY,CAAC,GAAW,EAAE,GAAG,GAAG,GAAG;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,SAAS,IAAI,CAAC,CAAS;QACrB,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;YAAE,OAAO;QAChC,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,CAAC,CAAwB,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;gBAAE,OAAO;YAChC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO;gBAAE,SAAS;YACzF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC;gBACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;oBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;qBACxC,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAE/E,SAAS,WAAW,CAAC,QAAgB;IACnC,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,8BAA8B;IAC9B,IAAI,0CAA0C,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,MAAM,CAAC;IAE7E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEtB,mCAAmC;QACnC,IAAI,mFAAmF,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnG,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAChD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACxB,IACE,6GAA6G,CAAC,IAAI,CAChH,GAAG,CAAC,IAAI,EAAE,CACX,EACD,CAAC;oBACD,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,uBAAuB;wBAC9B,QAAQ,EAAE,QAAQ;wBAClB,MAAM,EAAE,IAAI,GAAG,oEAAoE;qBACpF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,IAAI,qDAAqD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvE,IAAI,6DAA6D,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9E,IAAI,CAAC,mCAAmC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBACrD,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,gCAAgC;wBACvC,QAAQ,EAAE,MAAM;wBAChB,MAAM,EAAE,+FAA+F;qBACxG,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,0CAA0C;QAC1C,IAAI,oDAAoD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpE,IAAI,sEAAsE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtF,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,GAAG,CAAC;oBACX,KAAK,EAAE,oCAAoC;oBAC3C,QAAQ,EAAE,QAAQ;oBAClB,MAAM,EAAE,4EAA4E;iBACrF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7E,IAAI,gDAAgD,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtE,IAAI,oDAAoD,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC1E,IAAI,yCAAyC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC/D,MAAM,CAAC,IAAI,CAAC;4BACV,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,CAAC,GAAG,CAAC;4BACX,KAAK,EAAE,uCAAuC;4BAC9C,QAAQ,EAAE,MAAM;4BAChB,MAAM,EAAE,iGAAiG;yBAC1G,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,qDAAqD;QACrD,IAAI,4CAA4C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACpD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACxB,IAAI,0FAA0F,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACzG,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,uCAAuC;wBAC9C,QAAQ,EAAE,QAAQ;wBAClB,MAAM,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,mEAAmE;qBAChG,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,IAAI,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpG,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,KAAK,EAAE,yBAAyB;gBAChC,QAAQ,EAAE,QAAQ;gBAClB,MAAM,EAAE,qFAAqF;aAC9F,CAAC,CAAC;QACL,CAAC;QAED,4BAA4B;QAC5B,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAChE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACjD,IAAI,WAAW,IAAI,SAAS,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC5C,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;gBACvC,IAAI,MAAM,KAAK,GAAG,IAAI,4BAA4B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC7D,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,mCAAmC;wBAC1C,QAAQ,EAAE,QAAQ;wBAClB,MAAM,EAAE,gCAAgC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,yCAAyC;qBAClG,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,MAAM,KAAK,GAAG,IAAI,mCAAmC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACpE,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,4BAA4B;wBACnC,QAAQ,EAAE,KAAK;wBACf,MAAM,EAAE,iBAAiB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,uCAAuC;qBACjF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,iCAAiC;QACjC,IAAI,sDAAsD,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,kDAAkD,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACzF,IAAI,CAAC,yFAAyF,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5G,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,GAAG,CAAC;oBACX,KAAK,EAAE,gCAAgC;oBACvC,QAAQ,EAAE,KAAK;oBACf,MAAM,EAAE,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,wDAAwD;iBACxF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,sEAAsE;QACtE,IAAI,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChE,IAAI,CAAC,2CAA2C,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7D,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,GAAG,CAAC;oBACX,KAAK,EAAE,oCAAoC;oBAC3C,QAAQ,EAAE,QAAQ;oBAClB,MAAM,EAAE,0FAA0F;iBACnG,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;CAcf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC;IAC1F,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC;IAE/E,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,SAAS,GAAmB,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,SAAS,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzD,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACxE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;IAE9D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;YACE,MAAM,EAAE,SAAS;YACjB,KAAK;YACL,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE;YACvE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,KAAK,KAAK,wCAAwC,CAAC,CAAC;QAE9F,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1F,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;QAEpF,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,CAAC,MAAM,YAAY,SAAS,cAAc,QAAQ,aAAa,KAAK,QAAQ,CAAC,CAAC;IACrH,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Event leak — detect orphaned event listeners, unsubscribed observables, dangling async handles.
3
+ */
4
+ export declare function runEventLeak(argv: string[]): void;
5
+ //# sourceMappingURL=event-leak.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-leak.d.ts","sourceRoot":"","sources":["../../src/commands/event-leak.ts"],"names":[],"mappings":"AAAA;;GAEG;AAwOH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+DjD"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Event leak — detect orphaned event listeners, unsubscribed observables, dangling async handles.
3
+ */
4
+ import { readFileSync, readdirSync, statSync } from "fs";
5
+ import { join, extname } from "path";
6
+ // ─── File Collection ────────────────────────────────────────────────────────
7
+ const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx"]);
8
+ function collectFiles(dir, max = 300) {
9
+ const files = [];
10
+ function walk(d) {
11
+ if (files.length >= max)
12
+ return;
13
+ let entries;
14
+ try {
15
+ entries = readdirSync(d);
16
+ }
17
+ catch {
18
+ return;
19
+ }
20
+ for (const e of entries) {
21
+ if (files.length >= max)
22
+ return;
23
+ if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
24
+ continue;
25
+ const full = join(d, e);
26
+ try {
27
+ if (statSync(full).isDirectory())
28
+ walk(full);
29
+ else if (CODE_EXTS.has(extname(full)))
30
+ files.push(full);
31
+ }
32
+ catch {
33
+ /* skip */
34
+ }
35
+ }
36
+ }
37
+ walk(dir);
38
+ return files;
39
+ }
40
+ // ─── Analysis ───────────────────────────────────────────────────────────────
41
+ function analyzeFile(filepath) {
42
+ const issues = [];
43
+ let content;
44
+ try {
45
+ content = readFileSync(filepath, "utf-8");
46
+ }
47
+ catch {
48
+ return issues;
49
+ }
50
+ const lines = content.split("\n");
51
+ const fullText = content;
52
+ for (let i = 0; i < lines.length; i++) {
53
+ const line = lines[i];
54
+ // addEventListener without removeEventListener
55
+ if (/\.addEventListener\s*\(/.test(line)) {
56
+ const eventMatch = line.match(/addEventListener\s*\(\s*['"](\w+)['"]/);
57
+ if (eventMatch) {
58
+ const event = eventMatch[1];
59
+ if (!fullText.includes(`removeEventListener`) || !fullText.includes(`'${event}'`)) {
60
+ // Check for AbortController signal
61
+ const block = lines.slice(i, Math.min(i + 3, lines.length)).join("\n");
62
+ if (!/signal|AbortController|once:\s*true/i.test(block)) {
63
+ issues.push({
64
+ file: filepath,
65
+ line: i + 1,
66
+ issue: "addEventListener without cleanup",
67
+ severity: "high",
68
+ detail: `'${event}' listener added but no removeEventListener found — memory leak risk`,
69
+ });
70
+ }
71
+ }
72
+ }
73
+ }
74
+ // .on() without .off() / .removeListener()
75
+ if (/\.on\s*\(\s*['"](\w+)['"]/.test(line) && !/\.once\s*\(/.test(line)) {
76
+ const eventMatch = line.match(/\.on\s*\(\s*['"](\w+)['"]/);
77
+ if (eventMatch) {
78
+ const event = eventMatch[1];
79
+ if (!/\.off\(|\.removeListener\(|\.removeAllListeners\(/i.test(fullText) ||
80
+ !new RegExp(`(?:\\.off|\\.removeListener)\\s*\\(\\s*['"]${event}['"]`).test(fullText)) {
81
+ issues.push({
82
+ file: filepath,
83
+ line: i + 1,
84
+ issue: "Event emitter .on() without .off()",
85
+ severity: "medium",
86
+ detail: `'${event}' listener registered but no corresponding .off() or .removeListener()`,
87
+ });
88
+ }
89
+ }
90
+ }
91
+ // Observable subscribe without unsubscribe
92
+ if (/\.subscribe\s*\(/.test(line)) {
93
+ const block = lines.slice(i, Math.min(i + 10, lines.length)).join("\n");
94
+ if (!/unsubscribe|takeUntil|take\(|first\(\)|pipe.*take|subscription.*=|\.add\(/i.test(block) &&
95
+ !/unsubscribe|takeUntil|ngOnDestroy|componentWillUnmount|useEffect.*return/i.test(fullText)) {
96
+ issues.push({
97
+ file: filepath,
98
+ line: i + 1,
99
+ issue: "Observable subscribe without unsubscribe",
100
+ severity: "high",
101
+ detail: "Subscription not cleaned up — will leak memory and may trigger after component unmounts",
102
+ });
103
+ }
104
+ }
105
+ // setInterval without clearInterval
106
+ if (/setInterval\s*\(/.test(line)) {
107
+ if (!/clearInterval/i.test(fullText)) {
108
+ issues.push({
109
+ file: filepath,
110
+ line: i + 1,
111
+ issue: "setInterval without clearInterval",
112
+ severity: "high",
113
+ detail: "Interval runs forever — add cleanup in destructor/unmount/close handler",
114
+ });
115
+ }
116
+ }
117
+ // setTimeout stored but never cleared
118
+ if (/setTimeout\s*\(/.test(line)) {
119
+ const assignMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*setTimeout/);
120
+ if (assignMatch) {
121
+ const varName = assignMatch[1];
122
+ if (!fullText.includes(`clearTimeout(${varName})`)) {
123
+ issues.push({
124
+ file: filepath,
125
+ line: i + 1,
126
+ issue: "setTimeout assigned but never cleared",
127
+ severity: "low",
128
+ detail: `Timer '${varName}' stored but clearTimeout never called — may fire after disposal`,
129
+ });
130
+ }
131
+ }
132
+ }
133
+ // WebSocket/SSE without close handler
134
+ if (/new WebSocket\s*\(|new EventSource\s*\(/.test(line)) {
135
+ const block = lines.slice(i, Math.min(i + 15, lines.length)).join("\n");
136
+ if (!/\.close\s*\(|onclose|\.addEventListener.*close/i.test(block)) {
137
+ issues.push({
138
+ file: filepath,
139
+ line: i + 1,
140
+ issue: "WebSocket/SSE without close handler",
141
+ severity: "medium",
142
+ detail: "Connection opened but no close handler — resource leak on navigation or unmount",
143
+ });
144
+ }
145
+ }
146
+ // React useEffect without cleanup return
147
+ if (/useEffect\s*\(\s*\(\s*\)\s*=>\s*\{/.test(line)) {
148
+ // Find the matching effect body
149
+ let depth = 0;
150
+ let effectEnd = i;
151
+ for (let j = i; j < Math.min(i + 30, lines.length); j++) {
152
+ for (const ch of lines[j]) {
153
+ if (ch === "{")
154
+ depth++;
155
+ if (ch === "}")
156
+ depth--;
157
+ }
158
+ if (depth <= 0) {
159
+ effectEnd = j;
160
+ break;
161
+ }
162
+ }
163
+ const effectBody = lines.slice(i, effectEnd + 1).join("\n");
164
+ const hasSideEffect = /addEventListener|\.on\(|subscribe|setInterval|setTimeout|fetch|WebSocket/i.test(effectBody);
165
+ const hasCleanup = /return\s*\(\s*\)\s*=>|return\s*\(\)\s*\{|return\s*function/i.test(effectBody);
166
+ if (hasSideEffect && !hasCleanup) {
167
+ issues.push({
168
+ file: filepath,
169
+ line: i + 1,
170
+ issue: "useEffect with side effects but no cleanup",
171
+ severity: "high",
172
+ detail: "Effect creates subscriptions/listeners but returns no cleanup function — leak on re-render",
173
+ });
174
+ }
175
+ }
176
+ // MutationObserver / IntersectionObserver / ResizeObserver without disconnect
177
+ if (/new (?:Mutation|Intersection|Resize)Observer\s*\(/.test(line)) {
178
+ if (!/\.disconnect\s*\(/i.test(fullText)) {
179
+ const observerType = line.match(/(Mutation|Intersection|Resize)Observer/)?.[1] || "Observer";
180
+ issues.push({
181
+ file: filepath,
182
+ line: i + 1,
183
+ issue: `${observerType}Observer without disconnect`,
184
+ severity: "medium",
185
+ detail: "Observer created but never disconnected — continues observing after disposal",
186
+ });
187
+ }
188
+ }
189
+ // AbortController created but never aborted
190
+ if (/new AbortController\s*\(/.test(line)) {
191
+ const varMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*new AbortController/);
192
+ if (varMatch) {
193
+ const varName = varMatch[1];
194
+ if (!fullText.includes(`${varName}.abort()`)) {
195
+ issues.push({
196
+ file: filepath,
197
+ line: i + 1,
198
+ issue: "AbortController never aborted",
199
+ severity: "low",
200
+ detail: `AbortController '${varName}' created but .abort() never called — no cleanup on cancel`,
201
+ });
202
+ }
203
+ }
204
+ }
205
+ }
206
+ return issues;
207
+ }
208
+ // ─── CLI ────────────────────────────────────────────────────────────────────
209
+ export function runEventLeak(argv) {
210
+ if (argv.includes("--help") || argv.includes("-h")) {
211
+ console.log(`
212
+ judges event-leak — Detect orphaned listeners, unsubscribed observables, dangling handles
213
+
214
+ Usage:
215
+ judges event-leak [dir]
216
+ judges event-leak src/ --format json
217
+
218
+ Options:
219
+ [dir] Directory to scan (default: .)
220
+ --format json JSON output
221
+ --help, -h Show this help
222
+
223
+ Checks: addEventListener without cleanup, .on() without .off(), observable leaks,
224
+ setInterval without clear, useEffect without cleanup, Observer without disconnect.
225
+ `);
226
+ return;
227
+ }
228
+ const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
229
+ const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
230
+ const files = collectFiles(dir);
231
+ const allIssues = [];
232
+ for (const f of files)
233
+ allIssues.push(...analyzeFile(f));
234
+ const highCount = allIssues.filter((i) => i.severity === "high").length;
235
+ const medCount = allIssues.filter((i) => i.severity === "medium").length;
236
+ const score = Math.max(0, 100 - highCount * 10 - medCount * 4);
237
+ if (format === "json") {
238
+ console.log(JSON.stringify({
239
+ issues: allIssues,
240
+ score,
241
+ summary: { high: highCount, medium: medCount, total: allIssues.length },
242
+ timestamp: new Date().toISOString(),
243
+ }, null, 2));
244
+ }
245
+ else {
246
+ const badge = score >= 80 ? "✅ CLEAN" : score >= 50 ? "⚠️ LEAKY" : "❌ LEAKING";
247
+ console.log(`\n Event Leaks: ${badge} (${score}/100)\n ─────────────────────────────`);
248
+ if (allIssues.length === 0) {
249
+ console.log(" No event leaks detected.\n");
250
+ return;
251
+ }
252
+ for (const issue of allIssues.slice(0, 25)) {
253
+ const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
254
+ console.log(` ${icon} ${issue.issue}`);
255
+ console.log(` ${issue.file}:${issue.line}`);
256
+ console.log(` ${issue.detail}`);
257
+ }
258
+ if (allIssues.length > 25)
259
+ console.log(` ... and ${allIssues.length - 25} more`);
260
+ console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
261
+ }
262
+ }
263
+ //# sourceMappingURL=event-leak.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-leak.js","sourceRoot":"","sources":["../../src/commands/event-leak.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Idempotency audit — verify retried/webhook operations are safely idempotent.
3
+ */
4
+ export declare function runIdempotencyAudit(argv: string[]): void;
5
+ //# sourceMappingURL=idempotency-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-audit.d.ts","sourceRoot":"","sources":["../../src/commands/idempotency-audit.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2LH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA+DxD"}