truecourse 0.7.0-next.4 → 0.7.0-next.5

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 (94) hide show
  1. package/README.md +15 -3
  2. package/cli.mjs +1393 -682
  3. package/package.json +1 -1
  4. package/public/assets/{index-rfhWLMD4.js → index-CI8n7IHP.js} +231 -226
  5. package/public/index.html +1 -1
  6. package/roslyn-host/Humanizer.dll +0 -0
  7. package/roslyn-host/Microsoft.Bcl.AsyncInterfaces.dll +0 -0
  8. package/roslyn-host/Microsoft.Build.Locator.dll +0 -0
  9. package/roslyn-host/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
  10. package/roslyn-host/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  11. package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll +0 -0
  12. package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.MSBuild.dll +0 -0
  13. package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
  14. package/roslyn-host/Microsoft.CodeAnalysis.dll +0 -0
  15. package/roslyn-host/Microsoft.Extensions.DependencyInjection.Abstractions.dll +0 -0
  16. package/roslyn-host/Microsoft.Extensions.Logging.Abstractions.dll +0 -0
  17. package/roslyn-host/Newtonsoft.Json.dll +0 -0
  18. package/roslyn-host/System.Composition.AttributedModel.dll +0 -0
  19. package/roslyn-host/System.Composition.Convention.dll +0 -0
  20. package/roslyn-host/System.Composition.Hosting.dll +0 -0
  21. package/roslyn-host/System.Composition.Runtime.dll +0 -0
  22. package/roslyn-host/System.Composition.TypedParts.dll +0 -0
  23. package/roslyn-host/System.IO.Pipelines.dll +0 -0
  24. package/roslyn-host/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  25. package/roslyn-host/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  26. package/roslyn-host/cs/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  27. package/roslyn-host/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  28. package/roslyn-host/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
  29. package/roslyn-host/csharp-roslyn-host.deps.json +545 -0
  30. package/roslyn-host/csharp-roslyn-host.dll +0 -0
  31. package/roslyn-host/csharp-roslyn-host.dll.config +623 -0
  32. package/roslyn-host/csharp-roslyn-host.pdb +0 -0
  33. package/roslyn-host/csharp-roslyn-host.runtimeconfig.json +16 -0
  34. package/roslyn-host/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  35. package/roslyn-host/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  36. package/roslyn-host/de/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  37. package/roslyn-host/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  38. package/roslyn-host/de/Microsoft.CodeAnalysis.resources.dll +0 -0
  39. package/roslyn-host/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  40. package/roslyn-host/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  41. package/roslyn-host/es/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  42. package/roslyn-host/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  43. package/roslyn-host/es/Microsoft.CodeAnalysis.resources.dll +0 -0
  44. package/roslyn-host/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  45. package/roslyn-host/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  46. package/roslyn-host/fr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  47. package/roslyn-host/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  48. package/roslyn-host/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
  49. package/roslyn-host/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  50. package/roslyn-host/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  51. package/roslyn-host/it/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  52. package/roslyn-host/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  53. package/roslyn-host/it/Microsoft.CodeAnalysis.resources.dll +0 -0
  54. package/roslyn-host/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  55. package/roslyn-host/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  56. package/roslyn-host/ja/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  57. package/roslyn-host/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  58. package/roslyn-host/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
  59. package/roslyn-host/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  60. package/roslyn-host/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  61. package/roslyn-host/ko/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  62. package/roslyn-host/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  63. package/roslyn-host/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
  64. package/roslyn-host/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  65. package/roslyn-host/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  66. package/roslyn-host/pl/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  67. package/roslyn-host/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  68. package/roslyn-host/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
  69. package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  70. package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  71. package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  72. package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  73. package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
  74. package/roslyn-host/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  75. package/roslyn-host/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  76. package/roslyn-host/ru/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  77. package/roslyn-host/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  78. package/roslyn-host/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
  79. package/roslyn-host/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  80. package/roslyn-host/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  81. package/roslyn-host/tr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  82. package/roslyn-host/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  83. package/roslyn-host/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
  84. package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  85. package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  86. package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  87. package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  88. package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
  89. package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  90. package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  91. package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
  92. package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  93. package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
  94. package/server.mjs +1018 -344
package/server.mjs CHANGED
@@ -28008,7 +28008,12 @@ var init_result = __esm({
28008
28008
  durationMs: external_exports.number().nonnegative(),
28009
28009
  /** Present on `fail` / `error`. */
28010
28010
  failure: GuardFailureDetailSchema.optional(),
28011
- /** Repo-relative pointer into `evidence/`; present on `fail` / `error`. */
28011
+ /**
28012
+ * Repo-relative pointer into `evidence/`; present on every EXECUTED outcome —
28013
+ * `pass` (the run transcript proving what ran) as well as `fail` / `error`.
28014
+ * Absent on non-executed `stale` / `orphaned`, and on pre-2026-07-08 runs whose
28015
+ * passes were never given a bundle (optional keeps those parsing + rendering).
28016
+ */
28012
28017
  evidencePath: external_exports.string().optional(),
28013
28018
  /**
28014
28019
  * Present when the bound section moved: the section kept its text but now
@@ -44192,8 +44197,8 @@ function getMaxParameters(filePath) {
44192
44197
  }
44193
44198
  return 5;
44194
44199
  }
44195
- function isBootstrapEntry(functionName, filePath) {
44196
- return LANGUAGE_CONFIGS.some((c) => c.bootstrap.functionNames.includes(functionName) && c.bootstrap.filePattern.test(filePath));
44200
+ function isBootstrapEntry(functionName2, filePath) {
44201
+ return LANGUAGE_CONFIGS.some((c) => c.bootstrap.functionNames.includes(functionName2) && c.bootstrap.filePattern.test(filePath));
44197
44202
  }
44198
44203
  function getLanguageConfig(language) {
44199
44204
  const config2 = LANGUAGE_CONFIGS.find((c) => c.name === language);
@@ -48928,9 +48933,6 @@ function buildCSharpSymbolIndex(files, rootPath) {
48928
48933
  }
48929
48934
  function contributeCSharpEdges(files, rootPath) {
48930
48935
  const index = buildCSharpSymbolIndex(files, rootPath);
48931
- if (index.stats.ambiguousRefs > 0) {
48932
- console.warn(`[TrueCourse] C# symbol index: ${index.stats.ambiguousRefs} ambiguous type reference(s) skipped (same type name visible from multiple namespaces)`);
48933
- }
48934
48936
  return index.edges;
48935
48937
  }
48936
48938
  var TYPE_DECL_KINDS;
@@ -54259,12 +54261,9 @@ var init_lsp_client = __esm({
54259
54261
  this.process.stdout.on("data", (chunk3) => this.onData(chunk3));
54260
54262
  this.process.stderr.on("data", (chunk3) => {
54261
54263
  });
54262
- this.process.on("error", (err) => {
54263
- for (const pending of this.pendingRequests.values()) {
54264
- pending.reject(err);
54265
- }
54266
- this.pendingRequests.clear();
54267
- });
54264
+ this.process.on("error", (err) => this.rejectAllPending(err));
54265
+ this.process.stdin.on("error", (err) => this.rejectAllPending(new Error(`${this.config.name} LSP stdin error: ${err.message}`)));
54266
+ this.process.on("exit", (code, signal) => this.rejectAllPending(new Error(`${this.config.name} LSP server exited (${signal ?? `code ${code}`}) before responding`)));
54268
54267
  const initResult = await this.sendRequest("initialize", {
54269
54268
  processId: process.pid,
54270
54269
  capabilities: {
@@ -54443,17 +54442,33 @@ var init_lsp_client = __esm({
54443
54442
  // -------------------------------------------------------------------------
54444
54443
  // Internal: JSON-RPC transport
54445
54444
  // -------------------------------------------------------------------------
54445
+ /** Reject and clear every in-flight request — used when the server dies. */
54446
+ rejectAllPending(err) {
54447
+ if (this.pendingRequests.size === 0)
54448
+ return;
54449
+ for (const pending of this.pendingRequests.values())
54450
+ pending.reject(err);
54451
+ this.pendingRequests.clear();
54452
+ }
54446
54453
  sendRequest(method, params) {
54447
54454
  return new Promise((resolve11, reject) => {
54448
54455
  const id = ++this.requestId;
54449
54456
  this.pendingRequests.set(id, { resolve: resolve11, reject });
54450
54457
  const msg = { jsonrpc: "2.0", id, method, params };
54451
- this.process.stdin.write(encodeMessage(msg));
54458
+ try {
54459
+ this.process.stdin.write(encodeMessage(msg));
54460
+ } catch (err) {
54461
+ this.pendingRequests.delete(id);
54462
+ reject(err instanceof Error ? err : new Error(String(err)));
54463
+ }
54452
54464
  });
54453
54465
  }
54454
54466
  sendNotification(method, params) {
54455
54467
  const msg = { jsonrpc: "2.0", method, params };
54456
- this.process.stdin.write(encodeMessage(msg));
54468
+ try {
54469
+ this.process.stdin.write(encodeMessage(msg));
54470
+ } catch {
54471
+ }
54457
54472
  }
54458
54473
  onData(chunk3) {
54459
54474
  this.buffer = Buffer.concat([this.buffer, chunk3]);
@@ -64938,7 +64953,8 @@ var init_deterministic4 = __esm({
64938
64953
  description: "Function parameter is never referenced in the function body \u2014 remove it or prefix with `_`.",
64939
64954
  enabled: true,
64940
64955
  severity: "low",
64941
- type: "deterministic"
64956
+ type: "deterministic",
64957
+ engine: "roslyn-host"
64942
64958
  },
64943
64959
  {
64944
64960
  key: "code-quality/deterministic/hardcoded-url",
@@ -76222,6 +76238,94 @@ var init_production_debug_enabled = __esm({
76222
76238
  });
76223
76239
 
76224
76240
  // packages/analyzer/dist/rules/security/visitors/javascript/insecure-random.js
76241
+ function isSecuritySensitiveName(name) {
76242
+ const lower = name.toLowerCase();
76243
+ return SECURITY_KEYWORDS.some((kw) => lower.includes(kw));
76244
+ }
76245
+ function securityContextName(callNode) {
76246
+ let parent = callNode.parent;
76247
+ while (parent) {
76248
+ switch (parent.type) {
76249
+ case "variable_declarator":
76250
+ return leafName(parent.childForFieldName("name"));
76251
+ case "assignment_expression":
76252
+ case "augmented_assignment_expression":
76253
+ return leafName(parent.childForFieldName("left"));
76254
+ case "pair":
76255
+ return leafName(parent.childForFieldName("key"));
76256
+ case "public_field_definition":
76257
+ case "field_definition":
76258
+ return leafName(parent.childForFieldName("property") ?? parent.childForFieldName("name"));
76259
+ // The value is returned from the enclosing function (e.g.
76260
+ // `function generateToken() { return Math.random()... }`) — the
76261
+ // function name is the security context.
76262
+ case "return_statement":
76263
+ return enclosingFunctionName(parent);
76264
+ // Expression-bodied arrow returns the value directly, e.g.
76265
+ // `const genToken = () => Math.random()...`.
76266
+ case "arrow_function":
76267
+ return functionName(parent);
76268
+ // Structural boundaries: the random value is discarded or otherwise
76269
+ // not bound to a name.
76270
+ case "expression_statement":
76271
+ case "statement_block":
76272
+ case "if_statement":
76273
+ case "for_statement":
76274
+ case "for_in_statement":
76275
+ case "while_statement":
76276
+ case "do_statement":
76277
+ case "switch_statement":
76278
+ case "function_declaration":
76279
+ case "function_expression":
76280
+ case "method_definition":
76281
+ return null;
76282
+ }
76283
+ parent = parent.parent;
76284
+ }
76285
+ return null;
76286
+ }
76287
+ function enclosingFunctionName(node2) {
76288
+ let p = node2.parent;
76289
+ while (p) {
76290
+ if (p.type === "function_declaration" || p.type === "generator_function_declaration" || p.type === "method_definition" || p.type === "arrow_function" || p.type === "function_expression") {
76291
+ return functionName(p);
76292
+ }
76293
+ p = p.parent;
76294
+ }
76295
+ return null;
76296
+ }
76297
+ function functionName(fn) {
76298
+ const nameField = fn.childForFieldName("name");
76299
+ if (nameField)
76300
+ return leafName(nameField);
76301
+ const p = fn.parent;
76302
+ if (!p)
76303
+ return null;
76304
+ if (p.type === "variable_declarator")
76305
+ return leafName(p.childForFieldName("name"));
76306
+ if (p.type === "pair")
76307
+ return leafName(p.childForFieldName("key"));
76308
+ if (p.type === "assignment_expression")
76309
+ return leafName(p.childForFieldName("left"));
76310
+ if (p.type === "public_field_definition" || p.type === "field_definition") {
76311
+ return leafName(p.childForFieldName("property") ?? p.childForFieldName("name"));
76312
+ }
76313
+ return null;
76314
+ }
76315
+ function leafName(node2) {
76316
+ if (!node2)
76317
+ return null;
76318
+ if (node2.type === "identifier" || node2.type === "property_identifier" || node2.type === "shorthand_property_identifier") {
76319
+ return node2.text;
76320
+ }
76321
+ if (node2.type === "member_expression") {
76322
+ return node2.childForFieldName("property")?.text ?? null;
76323
+ }
76324
+ if (node2.type === "string") {
76325
+ return node2.text.slice(1, -1);
76326
+ }
76327
+ return node2.text;
76328
+ }
76225
76329
  function isFixtureOrTestFile(filePath) {
76226
76330
  const lower = filePath.toLowerCase();
76227
76331
  return /[\\/]seeds?[\\/]/.test(lower) || /[\\/](?:seed|seeds)\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower) || /\.seed\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower) || /[\\/]__tests__[\\/]/.test(lower) || /\.(?:test|spec)\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower);
@@ -76252,19 +76356,9 @@ var init_insecure_random = __esm({
76252
76356
  if (/\.length\b/.test(parentText))
76253
76357
  return null;
76254
76358
  }
76255
- let parent = node2.parent;
76256
- while (parent) {
76257
- const parentText = parent.text.toLowerCase();
76258
- if (SECURITY_KEYWORDS.some((kw) => parentText.includes(kw))) {
76259
- const immediateParent = node2.parent;
76260
- if (immediateParent?.type === "binary_expression" && /\.length\b/.test(immediateParent.text)) {
76261
- return null;
76262
- }
76263
- return makeViolation(this.ruleKey, node2, filePath, "high", "Insecure random number generator", "Math.random() is not cryptographically secure. Do not use it for tokens, keys, or secrets.", sourceCode, "Use crypto.randomBytes() or crypto.randomUUID() instead.");
76264
- }
76265
- if (parent.type === "expression_statement" || parent.type === "variable_declaration" || parent.type === "assignment_expression" || parent.type === "lexical_declaration")
76266
- break;
76267
- parent = parent.parent;
76359
+ const contextName = securityContextName(node2);
76360
+ if (contextName && isSecuritySensitiveName(contextName)) {
76361
+ return makeViolation(this.ruleKey, node2, filePath, "high", "Insecure random number generator", "Math.random() is not cryptographically secure. Do not use it for tokens, keys, or secrets.", sourceCode, "Use crypto.randomBytes() or crypto.randomUUID() instead.");
76268
76362
  }
76269
76363
  }
76270
76364
  }
@@ -76693,6 +76787,11 @@ var init_unverified_cross_origin_message = __esm({
76693
76787
  }
76694
76788
  if (methodName3 !== "addEventListener")
76695
76789
  return null;
76790
+ if (fn.type === "member_expression") {
76791
+ const receiver = fn.childForFieldName("object")?.text ?? "";
76792
+ if (receiver !== "window" && receiver !== "self" && receiver !== "globalThis")
76793
+ return null;
76794
+ }
76696
76795
  const args = node2.childForFieldName("arguments");
76697
76796
  if (!args)
76698
76797
  return null;
@@ -77175,7 +77274,11 @@ var init_timing_attack_comparison = __esm({
77175
77274
  return null;
77176
77275
  const leftLeaf = leafIdentifier(left);
77177
77276
  const rightLeaf = leafIdentifier(right);
77178
- if (SENSITIVE_COMPARISON_PATTERNS.test(leftLeaf) || SENSITIVE_COMPARISON_PATTERNS.test(rightLeaf)) {
77277
+ const leftIsLiteral = left.type === "string" || left.type === "template_string";
77278
+ const rightIsLiteral = right.type === "string" || right.type === "template_string";
77279
+ const leftMatches = !leftIsLiteral && SENSITIVE_COMPARISON_PATTERNS.test(leftLeaf);
77280
+ const rightMatches = !rightIsLiteral && SENSITIVE_COMPARISON_PATTERNS.test(rightLeaf);
77281
+ if (leftMatches || rightMatches) {
77179
77282
  if (sourceCode.includes("timingSafeEqual"))
77180
77283
  return null;
77181
77284
  return makeViolation(this.ruleKey, node2, filePath, "medium", "Timing attack via string comparison", `Using ${operator.text} to compare what may be a secret/token. This is vulnerable to timing attacks.`, sourceCode, "Use crypto.timingSafeEqual() for comparing secrets and tokens.");
@@ -77762,6 +77865,14 @@ var init_mixed_http_methods = __esm({
77762
77865
  });
77763
77866
 
77764
77867
  // packages/analyzer/dist/rules/security/visitors/javascript/process-signaling.js
77868
+ function isOwnedProcessPid(node2) {
77869
+ if (node2.type !== "member_expression")
77870
+ return false;
77871
+ if (node2.childForFieldName("property")?.text !== "pid")
77872
+ return false;
77873
+ const obj = node2.childForFieldName("object");
77874
+ return obj?.type === "member_expression" && obj.childForFieldName("property")?.text === "process";
77875
+ }
77765
77876
  var processSignalingVisitor;
77766
77877
  var init_process_signaling = __esm({
77767
77878
  "packages/analyzer/dist/rules/security/visitors/javascript/process-signaling.js"() {
@@ -77789,6 +77900,8 @@ var init_process_signaling = __esm({
77789
77900
  const pidArg = args.namedChildren[0];
77790
77901
  if (!pidArg)
77791
77902
  return null;
77903
+ if (isOwnedProcessPid(pidArg))
77904
+ return null;
77792
77905
  if (pidArg.type !== "number") {
77793
77906
  return makeViolation(this.ruleKey, node2, filePath, "high", "Sending signals to arbitrary processes", "process.kill() called with a non-literal PID. If the PID is user-controlled, this enables process manipulation.", sourceCode, "Validate and sanitize the PID before passing it to process.kill().");
77794
77907
  }
@@ -79142,8 +79255,8 @@ var init_sql_injection2 = __esm({
79142
79255
  if (!firstArg)
79143
79256
  return null;
79144
79257
  if (firstArg.type === "string" && firstArg.text.startsWith("f")) {
79145
- const hasInterpolation2 = firstArg.namedChildren.some((c) => c.type === "interpolation");
79146
- if (hasInterpolation2) {
79258
+ const hasInterpolation = firstArg.namedChildren.some((c) => c.type === "interpolation");
79259
+ if (hasInterpolation) {
79147
79260
  const queryText = firstArg.text;
79148
79261
  if (/:[a-z_]\w*|%s|%\(\w+\)s/.test(queryText))
79149
79262
  return null;
@@ -79653,17 +79766,17 @@ var init_eval_usage2 = __esm({
79653
79766
  funcName = attr.text;
79654
79767
  const receiver = fn.childForFieldName("object");
79655
79768
  if (receiver) {
79656
- let leafName2 = null;
79769
+ let leafName3 = null;
79657
79770
  if (receiver.type === "identifier") {
79658
- leafName2 = receiver.text;
79771
+ leafName3 = receiver.text;
79659
79772
  } else if (receiver.type === "attribute") {
79660
79773
  const attrChild = receiver.childForFieldName("attribute");
79661
79774
  if (attrChild) {
79662
- leafName2 = attrChild.text.replace(/^_+/, "");
79775
+ leafName3 = attrChild.text.replace(/^_+/, "");
79663
79776
  }
79664
79777
  }
79665
- if (leafName2 && SAFE_METHOD_RECEIVERS.has(leafName2)) {
79666
- const expectedSources = SAFE_RECEIVER_IMPORT_SOURCES[leafName2];
79778
+ if (leafName3 && SAFE_METHOD_RECEIVERS.has(leafName3)) {
79779
+ const expectedSources = SAFE_RECEIVER_IMPORT_SOURCES[leafName3];
79667
79780
  if (expectedSources) {
79668
79781
  const sources = getPythonImportSources(node2);
79669
79782
  for (const src of sources) {
@@ -83635,6 +83748,12 @@ var init_exclusions = __esm({
83635
83748
  /^https?:\/\/(?!hooks\.slack\.com|[a-z0-9]+\.webhook\.office\.com)/,
83636
83749
  // Docker / container image references
83637
83750
  /^(?:docker|ghcr|quay|gcr|ecr)\.io\//,
83751
+ // PostHog public project API key (phc_…) — intentionally client-exposed,
83752
+ // embedded in frontend code to send analytics events. Its 40+ char base62
83753
+ // body collides with the generic Cohere-token shape, but it is not a
83754
+ // private credential. Private PostHog keys use the phx_/phs_ prefixes and
83755
+ // are deliberately NOT allowlisted here.
83756
+ /^phc_[A-Za-z0-9]{30,}$/,
83638
83757
  // Common config patterns that aren't secrets
83639
83758
  /^\*+$/,
83640
83759
  // All asterisks (masked values)
@@ -85450,10 +85569,33 @@ var init_secret_scanner = __esm({
85450
85569
  });
85451
85570
 
85452
85571
  // packages/analyzer/dist/rules/security/visitors/universal.js
85572
+ function hasPasswordToken(text4) {
85573
+ const lower = text4.toLowerCase();
85574
+ return lower.includes("password") || lower.includes("passwd");
85575
+ }
85576
+ function isPasswordHashingContext(node2) {
85577
+ let stmt = node2.parent;
85578
+ while (stmt && !STATEMENT_NODE_TYPES2.has(stmt.type) && !FUNCTION_NODE_TYPES3.has(stmt.type)) {
85579
+ stmt = stmt.parent;
85580
+ }
85581
+ if (stmt && STATEMENT_NODE_TYPES2.has(stmt.type) && hasPasswordToken(stmt.text))
85582
+ return true;
85583
+ let fnNode = node2.parent;
85584
+ while (fnNode && !FUNCTION_NODE_TYPES3.has(fnNode.type))
85585
+ fnNode = fnNode.parent;
85586
+ if (fnNode) {
85587
+ const nameNode = fnNode.childForFieldName("name");
85588
+ const paramsNode = fnNode.childForFieldName("parameters") ?? fnNode.childForFieldName("parameter_list");
85589
+ const signature = `${nameNode?.text ?? ""} ${paramsNode?.text ?? ""}`;
85590
+ if (hasPasswordToken(signature))
85591
+ return true;
85592
+ }
85593
+ return false;
85594
+ }
85453
85595
  function stripCommentMarkers(text4) {
85454
85596
  return text4.replace(/^\/{2,3}\s?/, "").replace(/^\/\*\s?/, "").replace(/\s?\*\/$/, "").replace(/^\s*\*\s?/gm, "").replace(/^#\s?/gm, "").trim();
85455
85597
  }
85456
- var STRING_NODE_TYPES, hardcodedSecretVisitor, IPV4_REGEX, EXCLUDED_IPS, hardcodedIpVisitor, CLEARTEXT_PROTOCOLS, LOCALHOST_PREFIXES, clearTextProtocolVisitor, BIP39_COMMON_WORDS, hardcodedBlockchainMnemonicVisitor, DB_URL_PATTERN, DB_PASSWORD_KV_PATTERNS, NON_LITERAL_PASSWORD_SEGMENT, PLACEHOLDER_PASSWORDS, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
85598
+ var STRING_NODE_TYPES, hardcodedSecretVisitor, IPV4_REGEX, EXCLUDED_IPS, hardcodedIpVisitor, CLEARTEXT_PROTOCOLS, LOCALHOST_PREFIXES, clearTextProtocolVisitor, BIP39_COMMON_WORDS, hardcodedBlockchainMnemonicVisitor, DB_URL_PATTERN, DB_PASSWORD_KV_PATTERNS, NON_LITERAL_PASSWORD_SEGMENT, PLACEHOLDER_PASSWORDS, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, STATEMENT_NODE_TYPES2, FUNCTION_NODE_TYPES3, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
85457
85599
  var init_universal = __esm({
85458
85600
  "packages/analyzer/dist/rules/security/visitors/universal.js"() {
85459
85601
  "use strict";
@@ -86633,6 +86775,29 @@ var init_universal = __esm({
86633
86775
  "sha256",
86634
86776
  "sha512"
86635
86777
  ]);
86778
+ STATEMENT_NODE_TYPES2 = /* @__PURE__ */ new Set([
86779
+ "expression_statement",
86780
+ "return_statement",
86781
+ "lexical_declaration",
86782
+ "variable_declaration",
86783
+ "variable_declarator",
86784
+ "local_declaration_statement",
86785
+ "assignment",
86786
+ "augmented_assignment",
86787
+ "assignment_expression",
86788
+ "augmented_assignment_expression"
86789
+ ]);
86790
+ FUNCTION_NODE_TYPES3 = /* @__PURE__ */ new Set([
86791
+ "function_declaration",
86792
+ "function_expression",
86793
+ "arrow_function",
86794
+ "generator_function_declaration",
86795
+ "method_definition",
86796
+ "function_definition",
86797
+ "method_declaration",
86798
+ "local_function_statement",
86799
+ "constructor_declaration"
86800
+ ]);
86636
86801
  unpredictableSaltMissingVisitor = {
86637
86802
  ruleKey: "security/deterministic/unpredictable-salt-missing",
86638
86803
  nodeTypes: ["call_expression", "call", "invocation_expression"],
@@ -86650,21 +86815,12 @@ var init_universal = __esm({
86650
86815
  }
86651
86816
  if (!HASH_FUNCTIONS_NEEDING_SALT.has(methodName3))
86652
86817
  return null;
86653
- let parent = node2.parent;
86654
- while (parent) {
86655
- const parentText = parent.text.toLowerCase();
86656
- if (parentText.includes("password") || parentText.includes("passwd")) {
86657
- const args = node2.childForFieldName("arguments");
86658
- if (args && args.namedChildren.length < 2) {
86659
- return makeViolation(this.ruleKey, node2, filePath, "high", "Missing salt in hash", `${methodName3}() called without a salt in a password context. This allows rainbow table attacks.`, sourceCode, "Use a password hashing function like bcrypt, argon2, or PBKDF2 which handle salting automatically.");
86660
- }
86661
- return null;
86662
- }
86663
- if (parent.type === "function_declaration" || parent.type === "function_definition" || parent.type === "method_definition" || parent.type === "program" || parent.type === "method_declaration" || parent.type === "compilation_unit")
86664
- break;
86665
- parent = parent.parent;
86666
- }
86667
- return null;
86818
+ const args = node2.childForFieldName("arguments");
86819
+ if (!args || args.namedChildren.length >= 2)
86820
+ return null;
86821
+ if (!isPasswordHashingContext(node2))
86822
+ return null;
86823
+ return makeViolation(this.ruleKey, node2, filePath, "high", "Missing salt in hash", `${methodName3}() called without a salt in a password context. This allows rainbow table attacks.`, sourceCode, "Use a password hashing function like bcrypt, argon2, or PBKDF2 which handle salting automatically.");
86668
86824
  }
86669
86825
  };
86670
86826
  sensitiveFileVisitor = {
@@ -86757,7 +86913,7 @@ function isLiteralNode(n) {
86757
86913
  }
86758
86914
  return false;
86759
86915
  }
86760
- var JS_LANGUAGES, TS_LANGUAGES, COMPARISON_OPERATORS, CONSTANT_LITERALS, TERMINAL_TYPES, VALID_TYPEOF_VALUES, LITERAL_TYPES, ARRAY_METHODS_REQUIRING_RETURN, PRIMITIVE_TYPES, PURE_ARRAY_METHODS, KNOWN_ARG_ORDERS, VOID_RETURNING_METHODS, VOID_RETURNING_GLOBALS, NON_CONSTRUCTORS, PROTOTYPE_BUILTINS, NON_CALLABLE_GLOBALS, READ_ONLY_GLOBALS, RESTRICTED_NAMES, THROWABLE_TYPES;
86916
+ var JS_LANGUAGES, TS_LANGUAGES, COMPARISON_OPERATORS, CONSTANT_LITERALS, TERMINAL_TYPES, VALID_TYPEOF_VALUES, LITERAL_TYPES, ARRAY_METHODS_REQUIRING_RETURN, PRIMITIVE_TYPES, PURE_ARRAY_METHODS, SOCKET_IO_RECEIVERS, KNOWN_ARG_ORDERS, VOID_RETURNING_METHODS, VOID_RETURNING_GLOBALS, NON_CONSTRUCTORS, PROTOTYPE_BUILTINS, NON_CALLABLE_GLOBALS, READ_ONLY_GLOBALS, RESTRICTED_NAMES, THROWABLE_TYPES;
86761
86917
  var init_helpers = __esm({
86762
86918
  "packages/analyzer/dist/rules/bugs/visitors/javascript/_helpers.js"() {
86763
86919
  "use strict";
@@ -86815,6 +86971,7 @@ var init_helpers = __esm({
86815
86971
  "reduce",
86816
86972
  "reduceRight"
86817
86973
  ]);
86974
+ SOCKET_IO_RECEIVERS = /* @__PURE__ */ new Set(["socket", "sockets"]);
86818
86975
  KNOWN_ARG_ORDERS = [
86819
86976
  { fn: "startsWith", params: [["prefix", "str", "string", "start", "needle", "search"]] },
86820
86977
  { fn: "endsWith", params: [["suffix", "str", "string", "end", "needle", "search"]] },
@@ -87451,6 +87608,15 @@ var init_loss_of_precision = __esm({
87451
87608
  const num = Number(text4);
87452
87609
  if (!Number.isFinite(num))
87453
87610
  return null;
87611
+ const parent = node2.parent;
87612
+ if (parent?.type === "arguments") {
87613
+ const call = parent.parent;
87614
+ if (call?.type === "call_expression") {
87615
+ const fn = call.childForFieldName("function");
87616
+ if (fn?.type === "identifier" && fn.text === "BigInt")
87617
+ return null;
87618
+ }
87619
+ }
87454
87620
  if (!Number.isSafeInteger(num)) {
87455
87621
  return makeViolation(this.ruleKey, node2, filePath, "high", "Loss of precision", `\`${text4}\` exceeds Number.MAX_SAFE_INTEGER and will lose precision at runtime.`, sourceCode, `Use BigInt (\`${text4}n\`) or restructure to avoid large integer literals.`);
87456
87622
  }
@@ -89722,6 +89888,17 @@ var init_useless_increment = __esm({
89722
89888
  });
89723
89889
 
89724
89890
  // packages/analyzer/dist/rules/bugs/visitors/javascript/ignored-return-value.js
89891
+ function receiverBaseName(obj) {
89892
+ if (!obj)
89893
+ return null;
89894
+ if (obj.type === "identifier")
89895
+ return obj.text.toLowerCase();
89896
+ if (obj.type === "member_expression") {
89897
+ const prop = obj.childForFieldName("property");
89898
+ return prop ? prop.text.toLowerCase() : null;
89899
+ }
89900
+ return null;
89901
+ }
89725
89902
  var ignoredReturnValueVisitor;
89726
89903
  var init_ignored_return_value = __esm({
89727
89904
  "packages/analyzer/dist/rules/bugs/visitors/javascript/ignored-return-value.js"() {
@@ -89742,6 +89919,11 @@ var init_ignored_return_value = __esm({
89742
89919
  const prop = fn.childForFieldName("property");
89743
89920
  if (!prop || !PURE_ARRAY_METHODS.has(prop.text))
89744
89921
  return null;
89922
+ if (prop.text === "join") {
89923
+ const base = receiverBaseName(fn.childForFieldName("object"));
89924
+ if (base && SOCKET_IO_RECEIVERS.has(base))
89925
+ return null;
89926
+ }
89745
89927
  return makeViolation(this.ruleKey, expr, filePath, "high", "Ignored return value", `The return value of \`.${prop.text}()\` is ignored \u2014 this method does not mutate the array in place; the result must be used.`, sourceCode, `Assign the result: \`const result = arr.${prop.text}(...)\`.`);
89746
89928
  }
89747
89929
  };
@@ -90403,7 +90585,12 @@ var init_global_this_usage = __esm({
90403
90585
  let current = node2.parent;
90404
90586
  while (current) {
90405
90587
  const t = current.type;
90406
- if (t === "function_declaration" || t === "function" || t === "method_definition" || t === "class_declaration" || t === "class") {
90588
+ if (t === "function_declaration" || t === "function" || // Modern tree-sitter-javascript names a `function () {}` expression
90589
+ // `function_expression` (only the legacy grammar used bare `function`).
90590
+ // Without it, `this` inside any function-expression callback / IIFE /
90591
+ // prototype-assigned method walks up to program scope and is wrongly
90592
+ // reported as a global reference.
90593
+ t === "function_expression" || t === "generator_function" || t === "generator_function_declaration" || t === "method_definition" || t === "class_declaration" || t === "class") {
90407
90594
  return null;
90408
90595
  }
90409
90596
  if (t === "arrow_function") {
@@ -93069,6 +93256,9 @@ var init_missing_await = __esm({
93069
93256
  return null;
93070
93257
  if (!isInsideAsyncFunction(node2))
93071
93258
  return null;
93259
+ const declaredName = node2.childForFieldName("name");
93260
+ if (declaredName?.type === "identifier" && /Promise$/.test(declaredName.text))
93261
+ return null;
93072
93262
  const isPromise = typeQuery.isPromiseLike(filePath, value.startPosition.row, value.startPosition.column, value.endPosition.row, value.endPosition.column);
93073
93263
  if (!isPromise)
93074
93264
  return null;
@@ -93645,25 +93835,26 @@ var init_unbound_method = __esm({
93645
93835
  let classBody = node2.parent;
93646
93836
  while (classBody && classBody.type !== "class_body")
93647
93837
  classBody = classBody.parent;
93648
- if (classBody) {
93649
- for (const member of classBody.namedChildren) {
93650
- if (member.type === "public_field_definition" || member.type === "field_definition") {
93651
- const nameNode = member.children.find((c) => c.type === "property_identifier" || c.type === "private_property_identifier");
93652
- if (nameNode?.text !== methodName3)
93653
- continue;
93654
- const value = member.childForFieldName("value");
93655
- if (value?.type === "arrow_function" || value?.type === "function_expression")
93656
- return null;
93657
- if (value)
93658
- return null;
93659
- const typeAnnot = member.namedChildren.find((c) => c.type === "type_annotation");
93660
- const typeChild = typeAnnot?.namedChildren[0];
93661
- if (typeChild && typeChild.type !== "function_type" && typeChild.type !== "constructor_type") {
93662
- return null;
93663
- }
93664
- }
93838
+ if (!classBody)
93839
+ return null;
93840
+ let resolvesToMethod = false;
93841
+ for (const member of classBody.namedChildren) {
93842
+ if (member.type === "method_definition") {
93843
+ const nameNode = member.childForFieldName("name");
93844
+ if (nameNode?.text !== methodName3)
93845
+ continue;
93846
+ const isAccessor = member.children.some((c) => (c.text === "get" || c.text === "set") && c.type !== "property_identifier");
93847
+ resolvesToMethod = !isAccessor;
93848
+ break;
93849
+ }
93850
+ if (member.type === "public_field_definition" || member.type === "field_definition") {
93851
+ const nameNode = member.children.find((c) => c.type === "property_identifier" || c.type === "private_property_identifier");
93852
+ if (nameNode?.text === methodName3)
93853
+ return null;
93665
93854
  }
93666
93855
  }
93856
+ if (!resolvesToMethod)
93857
+ return null;
93667
93858
  return makeViolation(this.ruleKey, node2, filePath, "high", "Unbound method reference", `\`this.${methodName3}\` is used as a value without binding \u2014 \`this\` will be lost when called as a callback.`, sourceCode, "Use `this.method.bind(this)`, an arrow wrapper `() => this.method()`, or declare as an arrow function property.");
93668
93859
  }
93669
93860
  };
@@ -93766,7 +93957,7 @@ var init_missing_error_boundary = __esm({
93766
93957
  visit(node2, filePath, sourceCode) {
93767
93958
  if (!sourceCode.includes("React") && !sourceCode.includes("react") && !sourceCode.includes("jsx"))
93768
93959
  return null;
93769
- const hasAsyncData = /(?<![.\w])useQuery\b/.test(sourceCode) || /(?<![.\w])useSWR\b/.test(sourceCode) || /\buseSuspenseQuery\b/.test(sourceCode) || /\buseSuspenseInfiniteQuery\b/.test(sourceCode) || /\bSuspense\b/.test(sourceCode);
93960
+ const hasAsyncData = /(?<![.\w])useQuery\b/.test(sourceCode) || /(?<![.\w])useSWR\b/.test(sourceCode) || /\buseSuspenseQuery\b/.test(sourceCode) || /\buseSuspenseInfiniteQuery\b/.test(sourceCode);
93770
93961
  if (!hasAsyncData)
93771
93962
  return null;
93772
93963
  if (/\bErrorBoundary\b/.test(sourceCode))
@@ -106591,6 +106782,9 @@ var init_star_import = __esm({
106591
106782
  if (sourceText === "zod" || sourceText.startsWith("@react-email/") || sourceText.startsWith("fumadocs-ui/") || sourceText.startsWith("fumadocs-core/")) {
106592
106783
  return null;
106593
106784
  }
106785
+ if (sourceText === "semver" || sourceText.startsWith("@sentry/")) {
106786
+ return null;
106787
+ }
106594
106788
  if (sourceText.startsWith("./") || sourceText.startsWith("../"))
106595
106789
  return null;
106596
106790
  const nsImport = node2.namedChildren.find((c) => c.type === "import_clause")?.namedChildren.find((cc) => cc.type === "namespace_import");
@@ -108159,14 +108353,20 @@ var init_expression_complexity = __esm({
108159
108353
  if (!expr)
108160
108354
  return null;
108161
108355
  let target = expr;
108162
- while (target.type === "parenthesized_expression" && target.namedChildren[0]) {
108356
+ while ((target.type === "parenthesized_expression" || target.type === "as_expression" || target.type === "satisfies_expression" || target.type === "non_null_expression") && target.namedChildren[0]) {
108163
108357
  target = target.namedChildren[0];
108164
108358
  }
108359
+ if (target.type === "object" || target.type === "array")
108360
+ return null;
108165
108361
  if (target.type === "arrow_function" || target.type === "function_expression" || target.type === "function_declaration" || target.type === "generator_function" || target.type === "generator_function_declaration")
108166
108362
  return null;
108167
108363
  if (target.type === "jsx_element" || target.type === "jsx_fragment" || target.type === "jsx_self_closing_element")
108168
108364
  return null;
108169
108365
  let operatorCount = 0;
108366
+ let hasLogical = false;
108367
+ let hasArithmetic = false;
108368
+ const LOGICAL_OPS = /* @__PURE__ */ new Set(["&&", "||", "??"]);
108369
+ const ARITHMETIC_OPS3 = /* @__PURE__ */ new Set(["+", "-", "*", "/", "%", "**", "&", "|", "^", "<<", ">>", ">>>"]);
108170
108370
  const BINARY_TYPES = /* @__PURE__ */ new Set(["binary_expression", "logical_expression"]);
108171
108371
  const FUNCTION_BOUNDARY_TYPES3 = /* @__PURE__ */ new Set([
108172
108372
  "function_declaration",
@@ -108179,6 +108379,13 @@ var init_expression_complexity = __esm({
108179
108379
  function countOps(n) {
108180
108380
  if (BINARY_TYPES.has(n.type)) {
108181
108381
  operatorCount++;
108382
+ const op = n.childForFieldName("operator")?.text;
108383
+ if (op) {
108384
+ if (LOGICAL_OPS.has(op))
108385
+ hasLogical = true;
108386
+ else if (ARITHMETIC_OPS3.has(op))
108387
+ hasArithmetic = true;
108388
+ }
108182
108389
  }
108183
108390
  for (let i = 0; i < n.childCount; i++) {
108184
108391
  const child = n.child(i);
@@ -108191,6 +108398,8 @@ var init_expression_complexity = __esm({
108191
108398
  }
108192
108399
  countOps(expr);
108193
108400
  if (operatorCount > 5) {
108401
+ if (hasLogical && !hasArithmetic)
108402
+ return null;
108194
108403
  return makeViolation(this.ruleKey, node2, filePath, "medium", "Complex expression", `Expression has ${operatorCount} binary/logical operators (max 5). Break it into named variables for readability.`, sourceCode, "Split the expression into smaller, named intermediate variables.");
108195
108404
  }
108196
108405
  return null;
@@ -109015,12 +109224,73 @@ var init_strict_equality = __esm({
109015
109224
  }
109016
109225
  });
109017
109226
 
109018
- // packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js
109019
- var commentedOutCodeVisitor;
109227
+ // packages/analyzer/dist/rules/code-quality/visitors/_shared/commented-out-code.js
109228
+ function stripLineMarker(line) {
109229
+ return line.replace(/^\s*(?:\/\/+|#+)\s?/, "").replace(/^\s*\*\/?\s?/, "").replace(/\*\/\s*$/, "").trim();
109230
+ }
109231
+ function looksLikeProse(line) {
109232
+ const words = line.match(/[A-Za-z][A-Za-z'-]+/g) ?? [];
109233
+ const codePunct = (line.match(/[;{}()=<>[\]]/g) ?? []).length;
109234
+ return words.length >= 4 && codePunct <= 1;
109235
+ }
109236
+ function looksLikeCode(line, opts) {
109237
+ if (/[;{}]\s*$/.test(line))
109238
+ return true;
109239
+ if (opts.colonTerminates && /:\s*$/.test(line))
109240
+ return true;
109241
+ if (/=>/.test(line))
109242
+ return true;
109243
+ if (/^[\w.]+\s*\([^)]*\)\s*;?\s*$/.test(line))
109244
+ return true;
109245
+ if (/^[\w.[\]]+\s*=\s*[^=].*$/.test(line) && !/==/.test(line))
109246
+ return true;
109247
+ return false;
109248
+ }
109249
+ function isCommentedOutCode(inner, opts) {
109250
+ let code = 0;
109251
+ let prose = 0;
109252
+ for (const raw of inner.split("\n")) {
109253
+ const line = stripLineMarker(raw);
109254
+ if (line.length === 0)
109255
+ continue;
109256
+ if (looksLikeProse(line))
109257
+ prose++;
109258
+ else if (looksLikeCode(line, opts))
109259
+ code++;
109260
+ }
109261
+ return code > 0 && code >= prose;
109262
+ }
109263
+ function lineCommentRunInner(node2, opts) {
109264
+ const isRunLine = (n) => n != null && n.type === "comment" && opts.isLineComment(n.text);
109265
+ if (!isRunLine(node2))
109266
+ return null;
109267
+ const prev = node2.previousSibling;
109268
+ if (isRunLine(prev) && prev.startPosition.row === node2.startPosition.row - 1)
109269
+ return null;
109270
+ const lines = [opts.strip(node2.text)];
109271
+ let cur = node2.nextSibling;
109272
+ let lastRow = node2.startPosition.row;
109273
+ while (isRunLine(cur) && cur.startPosition.row === lastRow + 1) {
109274
+ lines.push(opts.strip(cur.text));
109275
+ lastRow = cur.startPosition.row;
109276
+ cur = cur.nextSibling;
109277
+ }
109278
+ return lines.join("\n");
109279
+ }
109020
109280
  var init_commented_out_code = __esm({
109281
+ "packages/analyzer/dist/rules/code-quality/visitors/_shared/commented-out-code.js"() {
109282
+ "use strict";
109283
+ }
109284
+ });
109285
+
109286
+ // packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js
109287
+ var isLineComment, commentedOutCodeVisitor;
109288
+ var init_commented_out_code2 = __esm({
109021
109289
  "packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js"() {
109022
109290
  "use strict";
109023
109291
  init_types3();
109292
+ init_commented_out_code();
109293
+ isLineComment = (t) => t.startsWith("//");
109024
109294
  commentedOutCodeVisitor = {
109025
109295
  ruleKey: "code-quality/deterministic/commented-out-code",
109026
109296
  languages: ["typescript", "tsx", "javascript"],
@@ -109029,21 +109299,19 @@ var init_commented_out_code = __esm({
109029
109299
  const text4 = node2.text;
109030
109300
  if (text4.startsWith("/**"))
109031
109301
  return null;
109032
- let inner = text4;
109033
- if (inner.startsWith("//"))
109034
- inner = inner.slice(2).trim();
109035
- else if (inner.startsWith("/*"))
109036
- inner = inner.slice(2, -2).trim();
109037
- if (inner.length < 10)
109038
- return null;
109039
- const codePatterns = [
109040
- /^\s*(const|let|var|function|return|if|for|while|import|export|class|throw|try|catch)\s/,
109041
- /[;{}]\s*$/,
109042
- /=>/,
109043
- /\w+\(.*\)\s*[;{]?\s*$/
109044
- ];
109045
- const matchCount = codePatterns.filter((p) => p.test(inner)).length;
109046
- if (matchCount >= 2) {
109302
+ let inner;
109303
+ if (isLineComment(text4)) {
109304
+ inner = lineCommentRunInner(node2, { isLineComment, strip: (t) => t.replace(/^\/\/+/, "") });
109305
+ if (inner === null)
109306
+ return null;
109307
+ } else if (text4.startsWith("/*")) {
109308
+ inner = text4.slice(2, -2);
109309
+ } else {
109310
+ return null;
109311
+ }
109312
+ if (inner.trim().length < 10)
109313
+ return null;
109314
+ if (isCommentedOutCode(inner, {})) {
109047
109315
  return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
109048
109316
  }
109049
109317
  return null;
@@ -109824,6 +110092,11 @@ var init_for_in_without_filter = __esm({
109824
110092
  return true;
109825
110093
  }
109826
110094
  }
110095
+ if (n.type === "member_expression") {
110096
+ const prop = n.childForFieldName("property");
110097
+ if (prop?.text === "hasOwnProperty" || prop?.text === "hasOwn")
110098
+ return true;
110099
+ }
109827
110100
  if (n.type === "string" && (n.text.includes("hasOwnProperty") || n.text.includes("hasOwn")))
109828
110101
  return true;
109829
110102
  for (let i = 0; i < n.childCount; i++) {
@@ -111191,6 +111464,10 @@ var init_selector_parameter = __esm({
111191
111464
  languages: ["typescript", "tsx", "javascript"],
111192
111465
  nodeTypes: JS_FUNCTION_TYPES,
111193
111466
  visit(node2, filePath, sourceCode) {
111467
+ const fnParent = node2.parent;
111468
+ if ((node2.type === "arrow_function" || node2.type === "function_expression") && (fnParent?.type === "arguments" || fnParent?.type === "jsx_expression")) {
111469
+ return null;
111470
+ }
111194
111471
  const params = node2.childForFieldName("parameters");
111195
111472
  if (!params)
111196
111473
  return null;
@@ -113273,6 +113550,15 @@ var init_meaningless_void_operator = __esm({
113273
113550
  });
113274
113551
 
113275
113552
  // packages/analyzer/dist/rules/code-quality/visitors/javascript/test-with-hardcoded-timeout.js
113553
+ function isTestFile(filePath) {
113554
+ const norm = filePath.replace(/\\/g, "/");
113555
+ const base = norm.slice(norm.lastIndexOf("/") + 1);
113556
+ if (/\.(test|spec)\.[cm]?[jt]sx?$/i.test(base))
113557
+ return true;
113558
+ if (/(^|\/)__tests?__\//.test(norm))
113559
+ return true;
113560
+ return false;
113561
+ }
113276
113562
  var testWithHardcodedTimeoutVisitor;
113277
113563
  var init_test_with_hardcoded_timeout = __esm({
113278
113564
  "packages/analyzer/dist/rules/code-quality/visitors/javascript/test-with-hardcoded-timeout.js"() {
@@ -113283,7 +113569,7 @@ var init_test_with_hardcoded_timeout = __esm({
113283
113569
  languages: ["typescript", "tsx", "javascript"],
113284
113570
  nodeTypes: ["call_expression"],
113285
113571
  visit(node2, filePath, sourceCode) {
113286
- if (!filePath.includes("test") && !filePath.includes("spec") && !filePath.includes(".test.") && !filePath.includes(".spec."))
113572
+ if (!isTestFile(filePath))
113287
113573
  return null;
113288
113574
  const fn = node2.childForFieldName("function");
113289
113575
  if (!fn)
@@ -114643,8 +114929,8 @@ var init_test_empty_file = __esm({
114643
114929
  nodeTypes: ["program"],
114644
114930
  visit(node2, filePath, sourceCode) {
114645
114931
  const lowerPath = filePath.toLowerCase();
114646
- const isTestFile5 = lowerPath.includes(".test.") || lowerPath.includes(".spec.") || lowerPath.includes("__tests__");
114647
- if (!isTestFile5)
114932
+ const isTestFile6 = lowerPath.includes(".test.") || lowerPath.includes(".spec.") || lowerPath.includes("__tests__");
114933
+ if (!isTestFile6)
114648
114934
  return null;
114649
114935
  if (hasTestFunction(node2))
114650
114936
  return null;
@@ -118350,7 +118636,7 @@ var init_javascript5 = __esm({
118350
118636
  init_useless_computed_key();
118351
118637
  init_useless_concat();
118352
118638
  init_strict_equality();
118353
- init_commented_out_code();
118639
+ init_commented_out_code2();
118354
118640
  init_inverted_boolean();
118355
118641
  init_prefer_single_boolean_return();
118356
118642
  init_prefer_immediate_return();
@@ -119517,30 +119803,27 @@ var init_unused_variable2 = __esm({
119517
119803
  });
119518
119804
 
119519
119805
  // packages/analyzer/dist/rules/code-quality/visitors/python/commented-out-code.js
119520
- var pythonCommentedOutCodeVisitor;
119521
- var init_commented_out_code2 = __esm({
119806
+ var isLineComment2, pythonCommentedOutCodeVisitor;
119807
+ var init_commented_out_code3 = __esm({
119522
119808
  "packages/analyzer/dist/rules/code-quality/visitors/python/commented-out-code.js"() {
119523
119809
  "use strict";
119524
119810
  init_types3();
119811
+ init_commented_out_code();
119812
+ isLineComment2 = (t) => t.startsWith("#");
119525
119813
  pythonCommentedOutCodeVisitor = {
119526
119814
  ruleKey: "code-quality/deterministic/commented-out-code",
119527
119815
  languages: ["python"],
119528
119816
  nodeTypes: ["comment"],
119529
119817
  visit(node2, filePath, sourceCode) {
119530
119818
  const text4 = node2.text;
119531
- if (!text4.startsWith("#"))
119819
+ if (!isLineComment2(text4))
119532
119820
  return null;
119533
- const inner = text4.slice(1).trim();
119534
- if (inner.length < 10)
119821
+ const inner = lineCommentRunInner(node2, { isLineComment: isLineComment2, strip: (t) => t.replace(/^#+/, "") });
119822
+ if (inner === null)
119535
119823
  return null;
119536
- const codePatterns = [
119537
- /\b(def|class|import|from|return|if|for|while|try|except|with|raise|print)\b/,
119538
- /[;:]\s*$/,
119539
- /\w+\(.*\)/,
119540
- /=\s*\w/
119541
- ];
119542
- const matchCount = codePatterns.filter((p) => p.test(inner)).length;
119543
- if (matchCount >= 2) {
119824
+ if (inner.trim().length < 10)
119825
+ return null;
119826
+ if (isCommentedOutCode(inner, { colonTerminates: true })) {
119544
119827
  return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
119545
119828
  }
119546
119829
  return null;
@@ -128533,8 +128816,8 @@ var init_test_not_discoverable = __esm({
128533
128816
  return null;
128534
128817
  const className = classNameNode.text;
128535
128818
  const isTestClass = className.startsWith("Test") || className.endsWith("Test") || className.endsWith("Tests");
128536
- const isTestFile5 = isPythonTestFile(filePath);
128537
- if (!isTestClass && !isTestFile5)
128819
+ const isTestFile6 = isPythonTestFile(filePath);
128820
+ if (!isTestClass && !isTestFile6)
128538
128821
  return null;
128539
128822
  const body = node2.childForFieldName("body");
128540
128823
  if (!body)
@@ -130303,7 +130586,7 @@ var init_python7 = __esm({
130303
130586
  init_no_debugger2();
130304
130587
  init_require_await2();
130305
130588
  init_unused_variable2();
130306
- init_commented_out_code2();
130589
+ init_commented_out_code3();
130307
130590
  init_abstract_class_without_abstract_method();
130308
130591
  init_and_or_ternary();
130309
130592
  init_assert_in_production();
@@ -131477,7 +131760,18 @@ function withLanguageSupport(rules, visitors) {
131477
131760
  continue;
131478
131761
  }
131479
131762
  if (rule.engine === "roslyn-host" || rule.engine === "roslyn-workspace") {
131480
- support[language] = language === "csharp" ? { status: "supported" } : { status: "not-applicable", reason: "C# semantic rule (Roslyn host); no equivalent in this language" };
131763
+ if (language === "csharp") {
131764
+ support[language] = { status: "supported" };
131765
+ continue;
131766
+ }
131767
+ const hasExplicitForLang = explicitFamilies.get(rule.key)?.has(language) ?? false;
131768
+ const universalExcludesLang2 = universalExcludedFamilies.get(rule.key)?.has(language) ?? false;
131769
+ const hasUniversalForLang = universalRuleKeys.has(rule.key) && UNIVERSAL_VISITOR_FAMILIES.includes(language) && !universalExcludesLang2;
131770
+ if (hasExplicitForLang || hasUniversalForLang) {
131771
+ support[language] = { status: "supported" };
131772
+ continue;
131773
+ }
131774
+ support[language] = { status: "not-applicable", reason: "C# semantic rule (Roslyn host); no equivalent in this language" };
131481
131775
  continue;
131482
131776
  }
131483
131777
  const hasExplicit = explicitFamilies.get(rule.key)?.has(language) ?? false;
@@ -140236,6 +140530,12 @@ function walkCSharp(node2, visit) {
140236
140530
  walkCSharp(child, visit);
140237
140531
  }
140238
140532
  }
140533
+ function isCSharpTestPath(filePath) {
140534
+ return filePath.split(/[\\/]/).some((segment) => {
140535
+ const s = segment.toLowerCase();
140536
+ return /\.tests?$/.test(s) || s.endsWith(".testbase") || s.endsWith("testapp");
140537
+ });
140538
+ }
140239
140539
  var FUNCTION_BOUNDARY_TYPES;
140240
140540
  var init_csharp_helpers = __esm({
140241
140541
  "packages/analyzer/dist/rules/_shared/csharp-helpers.js"() {
@@ -141650,7 +141950,7 @@ function isTypeConstantAccess(node2) {
141650
141950
  const receiver = lastSegment(node2.childForFieldName("expression")?.text ?? "");
141651
141951
  return /^[A-Z]/.test(receiver);
141652
141952
  }
141653
- function leafName(node2) {
141953
+ function leafName2(node2) {
141654
141954
  if (node2.type === "identifier")
141655
141955
  return node2.text;
141656
141956
  if (node2.type === "member_access_expression")
@@ -141686,8 +141986,8 @@ var init_timing_attack_comparison2 = __esm({
141686
141986
  return null;
141687
141987
  if (isTypeConstantAccess(left) || isTypeConstantAccess(right))
141688
141988
  return null;
141689
- const leftLeaf = leafName(left);
141690
- const rightLeaf = leafName(right);
141989
+ const leftLeaf = leafName2(left);
141990
+ const rightLeaf = leafName2(right);
141691
141991
  if (leftLeaf === "Length" || rightLeaf === "Length")
141692
141992
  return null;
141693
141993
  if (ENUM_CONSTANT_PATTERN2.test(leftLeaf) || ENUM_CONSTANT_PATTERN2.test(rightLeaf))
@@ -141706,7 +142006,7 @@ var init_timing_attack_comparison2 = __esm({
141706
142006
  });
141707
142007
 
141708
142008
  // packages/analyzer/dist/rules/security/visitors/csharp/insecure-random.js
141709
- function isTestFile(filePath) {
142009
+ function isTestFile2(filePath) {
141710
142010
  const fileName = filePath.replace(/^.*[\\/]/, "");
141711
142011
  return /Tests?\.cs$/i.test(fileName) || /(?:TestSetup|TestFixture|Fixtures?|FunctionalTests|IntegrationTests|UnitTests|TestHelpers?)/.test(fileName);
141712
142012
  }
@@ -141748,7 +142048,7 @@ var init_insecure_random3 = __esm({
141748
142048
  languages: ["csharp"],
141749
142049
  nodeTypes: ["object_creation_expression", "member_access_expression"],
141750
142050
  visit(node2, filePath, sourceCode) {
141751
- if (isTestFile(filePath))
142051
+ if (isTestFile2(filePath))
141752
142052
  return null;
141753
142053
  if (node2.type === "object_creation_expression") {
141754
142054
  if (getCreatedTypeName(node2) !== "Random")
@@ -144219,11 +144519,14 @@ var init_empty_catch3 = __esm({
144219
144519
  const stmt = tryStatements[0];
144220
144520
  if (/\b(Parse|TryParse|Deserialize)\s*[<(]/.test(stmt.text))
144221
144521
  return null;
144222
- const expr = stmt.namedChildren[0];
144223
- if (expr?.type === "invocation_expression" && BEST_EFFORT_METHODS.has(getCSharpMethodName(expr))) {
144522
+ let expr = stmt.namedChildren[0];
144523
+ if (expr?.type === "await_expression")
144524
+ expr = expr.namedChildren[0];
144525
+ const isBestEffort = (name) => BEST_EFFORT_METHODS.has(name.replace(/Async$/, ""));
144526
+ if (expr?.type === "invocation_expression" && isBestEffort(getCSharpMethodName(expr))) {
144224
144527
  return null;
144225
144528
  }
144226
- if (expr?.type === "conditional_access_expression" && BEST_EFFORT_METHODS.has((expr.text.match(/\.(\w+)\s*\([^()]*\)\s*$/) ?? [])[1] ?? "")) {
144529
+ if (expr?.type === "conditional_access_expression" && isBestEffort((expr.text.match(/\.(\w+)\s*\([^()]*\)\s*$/) ?? [])[1] ?? "")) {
144227
144530
  return null;
144228
144531
  }
144229
144532
  }
@@ -144287,6 +144590,8 @@ var init_self_assignment3 = __esm({
144287
144590
  languages: ["csharp"],
144288
144591
  nodeTypes: ["assignment_expression"],
144289
144592
  visit(node2, filePath, sourceCode) {
144593
+ if (node2.parent?.type === "initializer_expression")
144594
+ return null;
144290
144595
  const left = node2.childForFieldName("left");
144291
144596
  const right = node2.childForFieldName("right");
144292
144597
  const operator = node2.childForFieldName("operator");
@@ -145294,7 +145599,7 @@ var init_infinite_recursion2 = __esm({
145294
145599
  return null;
145295
145600
  if (node2.type === "property_declaration") {
145296
145601
  const arrow = node2.childForFieldName("value");
145297
- const arrowExpr = arrow?.namedChildren[0];
145602
+ const arrowExpr = arrow?.type === "arrow_expression_clause" ? arrow.namedChildren[0] : null;
145298
145603
  if (arrowExpr?.type === "identifier" && arrowExpr.text === name) {
145299
145604
  return makeViolation(this.ruleKey, node2, filePath, "critical", "Infinite recursion", `Property \`${name}\` returns itself \u2014 reading it recurses until StackOverflowException.`, sourceCode, "Return the backing field (or use an auto-property) instead of the property itself.");
145300
145605
  }
@@ -147359,8 +147664,11 @@ var init_useless_finally2 = __esm({
147359
147664
  });
147360
147665
 
147361
147666
  // packages/analyzer/dist/rules/bugs/visitors/csharp/fstring-missing-placeholders.js
147362
- function hasInterpolation(node2) {
147363
- return node2.namedChildren.some((c) => c?.type === "interpolation");
147667
+ function hasRealHole(node2) {
147668
+ if (node2.namedChildren.some((c) => c?.type === "interpolation"))
147669
+ return true;
147670
+ const stripped = node2.text.replace(/\{\{/g, "").replace(/\}\}/g, "");
147671
+ return stripped.includes("{");
147364
147672
  }
147365
147673
  var csharpFstringMissingPlaceholdersVisitor;
147366
147674
  var init_fstring_missing_placeholders2 = __esm({
@@ -147372,7 +147680,7 @@ var init_fstring_missing_placeholders2 = __esm({
147372
147680
  languages: ["csharp"],
147373
147681
  nodeTypes: ["interpolated_string_expression"],
147374
147682
  visit(node2, filePath, sourceCode) {
147375
- if (hasInterpolation(node2))
147683
+ if (hasRealHole(node2))
147376
147684
  return null;
147377
147685
  let top = node2;
147378
147686
  while (top.parent?.type === "binary_expression" && top.parent.childForFieldName("operator")?.text === "+") {
@@ -147383,7 +147691,7 @@ var init_fstring_missing_placeholders2 = __esm({
147383
147691
  const walk20 = (n) => {
147384
147692
  if (siblingHasHoles)
147385
147693
  return;
147386
- if (n.type === "interpolated_string_expression" && n.id !== node2.id && hasInterpolation(n)) {
147694
+ if (n.type === "interpolated_string_expression" && n.id !== node2.id && hasRealHole(n)) {
147387
147695
  siblingHasHoles = true;
147388
147696
  return;
147389
147697
  }
@@ -148863,6 +149171,32 @@ var init_chained_orderby_loses_ordering = __esm({
148863
149171
  });
148864
149172
 
148865
149173
  // packages/analyzer/dist/rules/bugs/visitors/csharp/check-against-value-being-assigned.js
149174
+ function enclosingTypeDecl(node2) {
149175
+ let p = node2.parent;
149176
+ while (p) {
149177
+ if (p.type === "class_declaration" || p.type === "struct_declaration" || p.type === "record_declaration") {
149178
+ return p;
149179
+ }
149180
+ p = p.parent;
149181
+ }
149182
+ return null;
149183
+ }
149184
+ function selfMemberName(target) {
149185
+ if (target.type === "identifier")
149186
+ return target.text;
149187
+ if (target.type === "member_access_expression") {
149188
+ const obj = target.childForFieldName("expression");
149189
+ if (obj?.type === "this")
149190
+ return target.childForFieldName("name")?.text ?? null;
149191
+ }
149192
+ return null;
149193
+ }
149194
+ function enclosingTypeHasProperty(typeDecl, name) {
149195
+ const body = typeDecl.childForFieldName("body") ?? typeDecl.namedChildren.find((c) => c?.type === "declaration_list");
149196
+ if (!body)
149197
+ return false;
149198
+ return body.namedChildren.some((m) => m?.type === "property_declaration" && m.childForFieldName("name")?.text === name);
149199
+ }
148866
149200
  function singleStatement(consequence) {
148867
149201
  if (consequence.type === "block") {
148868
149202
  const stmts = consequence.namedChildren.filter((c) => c?.type !== "comment");
@@ -148910,6 +149244,12 @@ var init_check_against_value_being_assigned = __esm({
148910
149244
  const sameBA = condLeft.text === value.text && condRight.text === target.text;
148911
149245
  if (!sameAB && !sameBA)
148912
149246
  return null;
149247
+ const selfName = selfMemberName(target);
149248
+ if (selfName) {
149249
+ const typeDecl = enclosingTypeDecl(node2);
149250
+ if (typeDecl && enclosingTypeHasProperty(typeDecl, selfName))
149251
+ return null;
149252
+ }
148913
149253
  return makeViolation(this.ruleKey, node2, filePath, "low", "Redundant check against the value being assigned", `\`if (${condition.text}) ${assign.text};\` guards a plain assignment with a check against the same value \u2014 assigning unconditionally has the identical effect.`, sourceCode, "Remove the guard and assign unconditionally, or fix the condition if a different check was intended.");
148914
149254
  }
148915
149255
  };
@@ -149399,7 +149739,8 @@ var init_redundant_base_call = __esm({
149399
149739
  const receiver = node2.childForFieldName("expression");
149400
149740
  if (receiver?.type !== "base" && receiver?.text !== "base")
149401
149741
  return null;
149402
- const memberName3 = node2.childForFieldName("name")?.text;
149742
+ const nameNode = node2.childForFieldName("name");
149743
+ const memberName3 = nameNode?.type === "generic_name" ? nameNode.namedChildren.find((c) => c?.type === "identifier")?.text : nameNode?.text;
149403
149744
  if (!memberName3)
149404
149745
  return null;
149405
149746
  const typeDecl = enclosingType(node2);
@@ -149734,6 +150075,19 @@ function baseEntryName(entry) {
149734
150075
  }
149735
150076
  return null;
149736
150077
  }
150078
+ function baseEntryArity(entry) {
150079
+ const generic = entry.type === "generic_name" ? entry : entry.type === "qualified_name" ? entry.childForFieldName("name") : null;
150080
+ if (generic?.type !== "generic_name")
150081
+ return 0;
150082
+ const args = generic.namedChildren.find((c) => c?.type === "type_argument_list");
150083
+ return args ? args.namedChildren.filter((c) => c != null).length : 0;
150084
+ }
150085
+ function declaredArity(node2) {
150086
+ const params = node2.childForFieldName("type_parameters");
150087
+ if (!params)
150088
+ return 0;
150089
+ return params.namedChildren.filter((c) => c?.type === "type_parameter").length;
150090
+ }
149737
150091
  var csharpRecursiveTypeInheritanceVisitor;
149738
150092
  var init_recursive_type_inheritance = __esm({
149739
150093
  "packages/analyzer/dist/rules/bugs/visitors/csharp/recursive-type-inheritance.js"() {
@@ -149755,6 +150109,8 @@ var init_recursive_type_inheritance = __esm({
149755
150109
  return null;
149756
150110
  if (baseEntryName(firstBase) !== typeName2)
149757
150111
  return null;
150112
+ if (baseEntryArity(firstBase) !== declaredArity(node2))
150113
+ return null;
149758
150114
  return makeViolation(this.ruleKey, firstBase, filePath, "high", "Recursive type inheritance", `\`${typeName2}\` lists itself as its own base type, producing a nonsensical recursive inheritance chain.`, sourceCode, "Remove the self reference from the base list.");
149759
150115
  }
149760
150116
  };
@@ -151982,6 +152338,20 @@ function isCSharpGeneratedSource(filePath, sourceCode) {
151982
152338
  return true;
151983
152339
  return sourceCode.slice(0, 600).includes("<auto-generated");
151984
152340
  }
152341
+ function isCSharpEfMigration(node2) {
152342
+ const cls = getCSharpEnclosingClass(node2);
152343
+ if (!cls)
152344
+ return false;
152345
+ const baseList = cls.namedChildren.find((c) => c?.type === "base_list");
152346
+ if (!baseList)
152347
+ return false;
152348
+ return baseList.namedChildren.some((base) => {
152349
+ if (!base)
152350
+ return false;
152351
+ const simple = base.text.split(".").pop() ?? base.text;
152352
+ return simple === "Migration";
152353
+ });
152354
+ }
151985
152355
  function isCSharpPrivateMember(node2) {
151986
152356
  const accessibility = [];
151987
152357
  for (const child of node2.children) {
@@ -154545,17 +154915,13 @@ var init_collapsible_if3 = __esm({
154545
154915
  });
154546
154916
 
154547
154917
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/commented-out-code.js
154548
- var CODE_PATTERNS, csharpCommentedOutCodeVisitor;
154549
- var init_commented_out_code3 = __esm({
154918
+ var isLineComment3, csharpCommentedOutCodeVisitor;
154919
+ var init_commented_out_code4 = __esm({
154550
154920
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/commented-out-code.js"() {
154551
154921
  "use strict";
154552
154922
  init_types3();
154553
- CODE_PATTERNS = [
154554
- /^\s*(var|int|string|bool|double|decimal|long|float|return|if|for|foreach|while|using|throw|try|catch|switch|await|new|public|private|protected|internal|static)\s/,
154555
- /[;{}]\s*$/,
154556
- /=>/,
154557
- /\w+\(.*\)\s*[;{]?\s*$/
154558
- ];
154923
+ init_commented_out_code();
154924
+ isLineComment3 = (t) => t.startsWith("//") && !t.startsWith("///");
154559
154925
  csharpCommentedOutCodeVisitor = {
154560
154926
  ruleKey: "code-quality/deterministic/commented-out-code",
154561
154927
  languages: ["csharp"],
@@ -154564,15 +154930,19 @@ var init_commented_out_code3 = __esm({
154564
154930
  const text4 = node2.text;
154565
154931
  if (text4.startsWith("///") || text4.startsWith("/**"))
154566
154932
  return null;
154567
- let inner = text4;
154568
- if (inner.startsWith("//"))
154569
- inner = inner.slice(2).trim();
154570
- else if (inner.startsWith("/*"))
154571
- inner = inner.slice(2, -2).trim();
154572
- if (inner.length < 10)
154933
+ let inner;
154934
+ if (isLineComment3(text4)) {
154935
+ inner = lineCommentRunInner(node2, { isLineComment: isLineComment3, strip: (t) => t.replace(/^\/\/+/, "") });
154936
+ if (inner === null)
154937
+ return null;
154938
+ } else if (text4.startsWith("/*")) {
154939
+ inner = text4.slice(2, -2);
154940
+ } else {
154573
154941
  return null;
154574
- const matchCount = CODE_PATTERNS.filter((p) => p.test(inner)).length;
154575
- if (matchCount >= 2) {
154942
+ }
154943
+ if (inner.trim().length < 10)
154944
+ return null;
154945
+ if (isCommentedOutCode(inner, {})) {
154576
154946
  return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
154577
154947
  }
154578
154948
  return null;
@@ -155280,6 +155650,13 @@ function csharpEmptyBodyIsIntentional(node2, body) {
155280
155650
  }
155281
155651
  if (isCSharpNoOpCallbackPosition(node2))
155282
155652
  return true;
155653
+ let enclosingType3 = node2.parent;
155654
+ while (enclosingType3 && enclosingType3.type !== "class_declaration" && enclosingType3.type !== "struct_declaration" && enclosingType3.type !== "record_declaration" && enclosingType3.type !== "record_struct_declaration") {
155655
+ enclosingType3 = enclosingType3.parent;
155656
+ }
155657
+ const typeName2 = enclosingType3?.childForFieldName("name")?.text;
155658
+ if (typeName2 && /^(Null|Noop|NoOp|Empty)[A-Z0-9]/.test(typeName2))
155659
+ return true;
155283
155660
  if (node2.type === "method_declaration") {
155284
155661
  if (hasCSharpModifier(node2, "virtual") || hasCSharpModifier(node2, "override") || hasCSharpModifier(node2, "partial"))
155285
155662
  return true;
@@ -155557,6 +155934,7 @@ var init_hardcoded_url2 = __esm({
155557
155934
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/hardcoded-url.js"() {
155558
155935
  "use strict";
155559
155936
  init_types3();
155937
+ init_csharp_helpers();
155560
155938
  NAMESPACE_URI_HOSTS = /w3\.org|schema\.org|xmlns|openxmlformats|xmlsoap|purl\.org|tempuri\.org|schemas\.microsoft\.com|tools\.ietf\.org|datatracker\.ietf\.org|rfc-editor\.org/;
155561
155939
  STABLE_API_HOSTS = /googleapis\.com|maps\.google|api\.stripe\.com|api\.twilio\.com|api\.sendgrid\.com|api\.github\.com|cdn\.|fonts\.googleapis|cloudflare|unpkg\.com|cdnjs\.cloudflare|jsdelivr\.net|nuget\.org/;
155562
155940
  PLACEHOLDER_NAME = /default|placeholder|example|sample|template|fallback|demo|test|mock|stub|dummy/;
@@ -155568,6 +155946,8 @@ var init_hardcoded_url2 = __esm({
155568
155946
  const text4 = node2.text;
155569
155947
  if (!/https?:\/\/[a-zA-Z0-9]/.test(text4))
155570
155948
  return null;
155949
+ if (isCSharpTestPath(filePath))
155950
+ return null;
155571
155951
  if (text4.includes("example.com") || text4.includes("localhost") || text4.includes("127.0.0.1") || text4.includes("placeholder"))
155572
155952
  return null;
155573
155953
  if (NAMESPACE_URI_HOSTS.test(text4))
@@ -155588,6 +155968,42 @@ var init_hardcoded_url2 = __esm({
155588
155968
  });
155589
155969
 
155590
155970
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/identical-functions.js
155971
+ function isTrivialExpr(e) {
155972
+ switch (e.type) {
155973
+ case "integer_literal":
155974
+ case "real_literal":
155975
+ case "boolean_literal":
155976
+ case "string_literal":
155977
+ case "verbatim_string_literal":
155978
+ case "null_literal":
155979
+ case "character_literal":
155980
+ case "identifier":
155981
+ case "this_expression":
155982
+ case "default_expression":
155983
+ case "member_access_expression":
155984
+ return true;
155985
+ case "invocation_expression": {
155986
+ const args = e.childForFieldName("arguments");
155987
+ if (!args)
155988
+ return true;
155989
+ return args.namedChildren.every((a) => {
155990
+ if (a?.type !== "argument")
155991
+ return true;
155992
+ const value = a.namedChildren.find((c) => c && c.type !== "name_colon");
155993
+ return !value || isTrivialExpr(value);
155994
+ });
155995
+ }
155996
+ default:
155997
+ return false;
155998
+ }
155999
+ }
156000
+ function isTrivialReturnBody(body) {
156001
+ const stmts = body.namedChildren.filter((c) => c && c.type !== "comment");
156002
+ if (stmts.length !== 1 || stmts[0]?.type !== "return_statement")
156003
+ return false;
156004
+ const expr = stmts[0].namedChildren.find((c) => c && c.type !== "comment");
156005
+ return !expr || isTrivialExpr(expr);
156006
+ }
155591
156007
  var COMPARED_TYPES, csharpIdenticalFunctionsVisitor;
155592
156008
  var init_identical_functions2 = __esm({
155593
156009
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/identical-functions.js"() {
@@ -155606,7 +156022,7 @@ var init_identical_functions2 = __esm({
155606
156022
  const body = n.childForFieldName("body");
155607
156023
  if (body && body.type === "block" && body.namedChildCount > 0) {
155608
156024
  const onlyThrow = body.namedChildCount === 1 && body.namedChildren[0]?.type === "throw_statement";
155609
- if (!onlyThrow) {
156025
+ if (!onlyThrow && !isTrivialReturnBody(body)) {
155610
156026
  const normalized = body.text.replace(/\s+/g, " ").trim();
155611
156027
  bodies.push({ body: normalized, fnNode: n });
155612
156028
  }
@@ -155796,6 +156212,8 @@ var init_magic_number2 = __esm({
155796
156212
  return null;
155797
156213
  if (parentType === "argument" && parent.parent?.type === "bracketed_argument_list")
155798
156214
  return null;
156215
+ if (parentType === "argument" && parent.childForFieldName("name"))
156216
+ return null;
155799
156217
  if (parentType === "binary_expression" && TIME_FACTORS2.has(val)) {
155800
156218
  const op = parent.childForFieldName("operator");
155801
156219
  if (op?.text === "*") {
@@ -156117,6 +156535,8 @@ var init_no_extraneous_class2 = __esm({
156117
156535
  return null;
156118
156536
  if (node2.namedChildren.some((c) => c?.type === "base_list"))
156119
156537
  return null;
156538
+ if (node2.namedChildren.some((c) => c?.type === "attribute_list"))
156539
+ return null;
156120
156540
  const body = node2.childForFieldName("body");
156121
156541
  if (!body)
156122
156542
  return null;
@@ -156375,7 +156795,8 @@ var init_redundant_jump3 = __esm({
156375
156795
  if (!grandparent)
156376
156796
  return null;
156377
156797
  if (node2.type === "return_statement") {
156378
- if (node2.namedChildren.length > 0)
156798
+ const hasExpression = node2.children.some((c) => c != null && c.type !== "return" && c.type !== ";");
156799
+ if (hasExpression)
156379
156800
  return null;
156380
156801
  if (!isCSharpFunctionBoundary(grandparent.type))
156381
156802
  return null;
@@ -157740,6 +158161,8 @@ var init_too_many_lines3 = __esm({
157740
158161
  const bodyNode = getCSharpFunctionBody(node2);
157741
158162
  if (!bodyNode)
157742
158163
  return null;
158164
+ if (isCSharpGeneratedSource(filePath, sourceCode) || isCSharpEfMigration(node2))
158165
+ return null;
157743
158166
  const lineCount = bodyNode.endPosition.row - bodyNode.startPosition.row + 1;
157744
158167
  if (lineCount > 50) {
157745
158168
  const name = getCSharpFunctionName(node2);
@@ -158387,93 +158810,6 @@ var init_unused_collection2 = __esm({
158387
158810
  }
158388
158811
  });
158389
158812
 
158390
- // packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-function-parameter.js
158391
- var csharpUnusedFunctionParameterVisitor;
158392
- var init_unused_function_parameter2 = __esm({
158393
- "packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-function-parameter.js"() {
158394
- "use strict";
158395
- init_types3();
158396
- init_csharp_helpers();
158397
- csharpUnusedFunctionParameterVisitor = {
158398
- ruleKey: "code-quality/deterministic/unused-function-parameter",
158399
- languages: ["csharp"],
158400
- nodeTypes: ["method_declaration", "constructor_declaration", "local_function_statement"],
158401
- visit(node2, filePath, sourceCode) {
158402
- const params = node2.childForFieldName("parameters");
158403
- if (!params || params.namedChildCount === 0)
158404
- return null;
158405
- const body = node2.childForFieldName("body");
158406
- if (!body)
158407
- return null;
158408
- if (body.type === "block" && body.namedChildCount === 0)
158409
- return null;
158410
- if (/NotImplementedException|NotSupportedException/.test(body.text))
158411
- return null;
158412
- if (body.type === "block" && body.namedChildCount === 1 && body.namedChildren[0]?.type === "return_statement" && /Task\.CompletedTask|Task\.FromResult|ValueTask\.CompletedTask/.test(body.namedChildren[0].text)) {
158413
- return null;
158414
- }
158415
- if (node2.type === "method_declaration") {
158416
- for (const mod of ["override", "virtual", "abstract", "partial", "extern"]) {
158417
- if (hasCSharpModifier(node2, mod))
158418
- return null;
158419
- }
158420
- if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
158421
- return null;
158422
- if (node2.childForFieldName("name")?.text === "Main")
158423
- return null;
158424
- }
158425
- const paramList = [];
158426
- for (const param of params.namedChildren) {
158427
- if (!param || param.type !== "parameter")
158428
- continue;
158429
- if (param.children.some((c) => c?.type === "this" || c?.type === "modifier" && c.text === "this"))
158430
- continue;
158431
- if (param.childForFieldName("type")?.text === "CancellationToken")
158432
- continue;
158433
- const nameNode = param.childForFieldName("name");
158434
- if (nameNode?.type === "identifier")
158435
- paramList.push({ name: nameNode.text, node: param });
158436
- }
158437
- if (paramList.length === 0)
158438
- return null;
158439
- if (paramList.length === 2) {
158440
- const first2 = params.namedChildren.find((c) => c?.type === "parameter");
158441
- const firstType = first2?.childForFieldName("type")?.text;
158442
- const secondType = paramList[1] ? paramList[1].node.childForFieldName("type")?.text : void 0;
158443
- if ((firstType === "object" || firstType === "object?") && secondType?.endsWith("EventArgs"))
158444
- return null;
158445
- }
158446
- const used = /* @__PURE__ */ new Set();
158447
- function collectReads(n) {
158448
- if (n.type === "identifier") {
158449
- const parent = n.parent;
158450
- const isMemberName = parent && (parent.type === "member_access_expression" || parent.type === "qualified_name" || parent.type === "member_binding_expression") && parent.childForFieldName("name")?.id === n.id;
158451
- if (!isMemberName)
158452
- used.add(n.text);
158453
- }
158454
- for (let i = 0; i < n.namedChildCount; i++) {
158455
- const child = n.namedChild(i);
158456
- if (child)
158457
- collectReads(child);
158458
- }
158459
- }
158460
- collectReads(body);
158461
- const initializer = node2.namedChildren.find((c) => c?.type === "constructor_initializer");
158462
- if (initializer)
158463
- collectReads(initializer);
158464
- for (const { name, node: paramNode } of paramList) {
158465
- if (name.startsWith("_"))
158466
- continue;
158467
- if (!used.has(name)) {
158468
- return makeViolation(this.ruleKey, paramNode, filePath, "low", `Unused parameter \`${name}\``, `Parameter \`${name}\` is never used in the method body. Remove it or rename it to \`_\` if the signature is fixed.`, sourceCode, `Remove unused parameter \`${name}\` or rename it to \`_${name}\`.`);
158469
- }
158470
- }
158471
- return null;
158472
- }
158473
- };
158474
- }
158475
- });
158476
-
158477
158813
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-private-member.js
158478
158814
  var csharpUnusedPrivateMemberVisitor;
158479
158815
  var init_unused_private_member2 = __esm({
@@ -158498,6 +158834,8 @@ var init_unused_private_member2 = __esm({
158498
158834
  for (const member of body.namedChildren) {
158499
158835
  if (!member)
158500
158836
  continue;
158837
+ if (member.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
158838
+ continue;
158501
158839
  if (!isCSharpPrivateMember(member))
158502
158840
  continue;
158503
158841
  if (getCSharpDeclAttributeNames(member).length > 0)
@@ -158895,8 +159233,17 @@ var init_abstract_class_public_constructor = __esm({
158895
159233
  });
158896
159234
 
158897
159235
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/abstract-class-without-abstract-members.js
158898
- function hasAbstractMember(body) {
158899
- return body.namedChildren.some((c) => c && MEMBER_TYPES2.has(c.type) && hasCSharpModifier(c, "abstract"));
159236
+ function hasMemberWithModifier(body, modifier) {
159237
+ return body.namedChildren.some((c) => c != null && MEMBER_TYPES2.has(c.type) && hasCSharpModifier(c, modifier));
159238
+ }
159239
+ function hasProtectedConstructor(body) {
159240
+ return body.namedChildren.some((c) => c?.type === "constructor_declaration" && hasCSharpModifier(c, "protected"));
159241
+ }
159242
+ function hasBaseList(classDecl) {
159243
+ return classDecl.namedChildren.some((c) => c?.type === "base_list");
159244
+ }
159245
+ function hasExtensionSignal(classDecl, body) {
159246
+ return hasMemberWithModifier(body, "abstract") || hasMemberWithModifier(body, "virtual") || hasMemberWithModifier(body, "override") || hasProtectedConstructor(body) || hasBaseList(classDecl);
158900
159247
  }
158901
159248
  var MEMBER_TYPES2, csharpAbstractClassWithoutAbstractMembersVisitor;
158902
159249
  var init_abstract_class_without_abstract_members = __esm({
@@ -158908,6 +159255,7 @@ var init_abstract_class_without_abstract_members = __esm({
158908
159255
  "method_declaration",
158909
159256
  "property_declaration",
158910
159257
  "event_declaration",
159258
+ "event_field_declaration",
158911
159259
  "indexer_declaration"
158912
159260
  ]);
158913
159261
  csharpAbstractClassWithoutAbstractMembersVisitor = {
@@ -158922,10 +159270,10 @@ var init_abstract_class_without_abstract_members = __esm({
158922
159270
  const body = node2.childForFieldName("body");
158923
159271
  if (!body)
158924
159272
  return null;
158925
- if (hasAbstractMember(body))
159273
+ if (hasExtensionSignal(node2, body))
158926
159274
  return null;
158927
159275
  const name = node2.childForFieldName("name")?.text ?? "class";
158928
- return makeViolation(this.ruleKey, node2, filePath, "low", "Abstract class without abstract members", `Abstract class \`${name}\` declares no abstract members, so it should be a concrete base class or an interface instead.`, sourceCode, "Add abstract members, drop the `abstract` modifier, or convert the type to an interface.");
159276
+ return makeViolation(this.ruleKey, node2, filePath, "low", "Abstract class without abstract members", `Abstract class \`${name}\` declares no abstract members and no other reason to be abstract (no virtual/override member, no protected constructor, no base type), so it should be a concrete class or a static holder instead.`, sourceCode, "Add abstract members, drop the `abstract` modifier, or convert the type to an interface.");
158929
159277
  }
158930
159278
  };
158931
159279
  }
@@ -159843,6 +160191,8 @@ var init_static_holder_type_has_constructor = __esm({
159843
160191
  return null;
159844
160192
  if (hasCSharpModifier(node2, "abstract"))
159845
160193
  return null;
160194
+ if (node2.namedChildren.some((c) => c?.type === "base_list"))
160195
+ return null;
159846
160196
  const body = node2.namedChildren.find((c) => c?.type === "declaration_list");
159847
160197
  if (!body)
159848
160198
  return null;
@@ -161726,6 +162076,15 @@ var init_extension_method_on_object = __esm({
161726
162076
  });
161727
162077
 
161728
162078
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/generic-parameter-not-inferable.js
162079
+ function isInterfaceMember(method) {
162080
+ let current = method.parent;
162081
+ while (current) {
162082
+ if (TYPE_DECL_TYPES2.has(current.type))
162083
+ return current.type === "interface_declaration";
162084
+ current = current.parent;
162085
+ }
162086
+ return false;
162087
+ }
161729
162088
  function identifiersIn2(node2) {
161730
162089
  const used = /* @__PURE__ */ new Set();
161731
162090
  if (!node2)
@@ -161747,14 +162106,23 @@ function parameterTypeIdentifiers(method) {
161747
162106
  for (const id of identifiersIn2(param.childForFieldName("type")))
161748
162107
  used.add(id);
161749
162108
  }
162109
+ for (const id of identifiersIn2(params.childForFieldName("type")))
162110
+ used.add(id);
161750
162111
  return used;
161751
162112
  }
161752
- var csharpGenericParameterNotInferableVisitor;
162113
+ var TYPE_DECL_TYPES2, csharpGenericParameterNotInferableVisitor;
161753
162114
  var init_generic_parameter_not_inferable = __esm({
161754
162115
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/generic-parameter-not-inferable.js"() {
161755
162116
  "use strict";
161756
162117
  init_types3();
161757
162118
  init_csharp_helpers();
162119
+ TYPE_DECL_TYPES2 = /* @__PURE__ */ new Set([
162120
+ "interface_declaration",
162121
+ "class_declaration",
162122
+ "struct_declaration",
162123
+ "record_declaration",
162124
+ "record_struct_declaration"
162125
+ ]);
161758
162126
  csharpGenericParameterNotInferableVisitor = {
161759
162127
  ruleKey: "code-quality/deterministic/generic-parameter-not-inferable",
161760
162128
  languages: ["csharp"],
@@ -161763,6 +162131,8 @@ var init_generic_parameter_not_inferable = __esm({
161763
162131
  const typeParams = node2.namedChildren.find((c) => c?.type === "type_parameter_list");
161764
162132
  if (!typeParams)
161765
162133
  return null;
162134
+ if (isInterfaceMember(node2))
162135
+ return null;
161766
162136
  const usedInParams = parameterTypeIdentifiers(node2);
161767
162137
  const nameNode = node2.childForFieldName("name");
161768
162138
  const usedInReturn = /* @__PURE__ */ new Set();
@@ -161774,15 +162144,13 @@ var init_generic_parameter_not_inferable = __esm({
161774
162144
  for (const id of identifiersIn2(child))
161775
162145
  usedInReturn.add(id);
161776
162146
  }
161777
- const usedInBody = identifiersIn2(node2.childForFieldName("body"));
161778
162147
  for (const tp of typeParams.namedChildren) {
161779
162148
  if (tp?.type !== "type_parameter")
161780
162149
  continue;
161781
162150
  const name = tp.namedChildren.find((c) => c?.type === "identifier")?.text;
161782
162151
  if (!name)
161783
162152
  continue;
161784
- const usedElsewhere = usedInReturn.has(name) || usedInBody.has(name);
161785
- if (!usedInParams.has(name) && usedElsewhere) {
162153
+ if (!usedInParams.has(name) && usedInReturn.has(name)) {
161786
162154
  return makeViolation(this.ruleKey, tp, filePath, "low", "Type parameter not inferable", `Type parameter \`${name}\` appears in no method parameter, so callers must always specify it explicitly.`, sourceCode, `Use \`${name}\` in a parameter type, or accept that callers must specify it and consider a non-generic design.`);
161787
162155
  }
161788
162156
  }
@@ -161895,6 +162263,9 @@ var init_native_method_not_wrapped = __esm({
161895
162263
  });
161896
162264
 
161897
162265
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/nested-generic-parameter.js
162266
+ function genericBaseName2(generic) {
162267
+ return generic.namedChildren.find((c) => c?.type === "identifier")?.text ?? "";
162268
+ }
161898
162269
  function asGenericName(typeNode) {
161899
162270
  if (!typeNode)
161900
162271
  return null;
@@ -161910,16 +162281,19 @@ function hasNestedGeneric(typeNode) {
161910
162281
  const generic = asGenericName(typeNode);
161911
162282
  if (!generic)
161912
162283
  return false;
162284
+ if (DELEGATE_OR_EXPRESSION_GENERICS.has(genericBaseName2(generic)))
162285
+ return false;
161913
162286
  const args = generic.namedChildren.find((c) => c?.type === "type_argument_list");
161914
162287
  if (!args)
161915
162288
  return false;
161916
162289
  return args.namedChildren.some((arg) => asGenericName(arg) !== null);
161917
162290
  }
161918
- var csharpNestedGenericParameterVisitor;
162291
+ var DELEGATE_OR_EXPRESSION_GENERICS, csharpNestedGenericParameterVisitor;
161919
162292
  var init_nested_generic_parameter = __esm({
161920
162293
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/nested-generic-parameter.js"() {
161921
162294
  "use strict";
161922
162295
  init_types3();
162296
+ DELEGATE_OR_EXPRESSION_GENERICS = /* @__PURE__ */ new Set(["Func", "Action", "Predicate", "Expression"]);
161923
162297
  csharpNestedGenericParameterVisitor = {
161924
162298
  ruleKey: "code-quality/deterministic/nested-generic-parameter",
161925
162299
  languages: ["csharp"],
@@ -162039,6 +162413,39 @@ function enclosingTypeBody(node2) {
162039
162413
  const body = node2.parent;
162040
162414
  return body?.type === "declaration_list" ? body : null;
162041
162415
  }
162416
+ function isZeroArgCallTo(expr, getName) {
162417
+ if (expr?.type !== "invocation_expression")
162418
+ return false;
162419
+ const fn = expr.childForFieldName("function") ?? expr.namedChildren[0];
162420
+ const fnText = fn?.text;
162421
+ if (fnText !== getName && fnText !== `this.${getName}`)
162422
+ return false;
162423
+ const args = expr.childForFieldName("arguments") ?? expr.namedChildren.find((c) => c?.type === "argument_list");
162424
+ const argCount = args?.namedChildren.filter((c) => c?.type === "argument").length ?? 0;
162425
+ return argCount === 0;
162426
+ }
162427
+ function getterDelegatesTo(prop, getName) {
162428
+ const propArrow = prop.namedChildren.find((c) => c?.type === "arrow_expression_clause");
162429
+ if (propArrow)
162430
+ return isZeroArgCallTo(propArrow.namedChildren.find(Boolean), getName);
162431
+ const accessorList = prop.namedChildren.find((c) => c?.type === "accessor_list");
162432
+ if (!accessorList)
162433
+ return false;
162434
+ const getter = accessorList.namedChildren.find((a) => a?.type === "accessor_declaration" && a.children.some((c) => c?.text === "get"));
162435
+ if (!getter)
162436
+ return false;
162437
+ const getArrow = getter.namedChildren.find((c) => c?.type === "arrow_expression_clause");
162438
+ if (getArrow)
162439
+ return isZeroArgCallTo(getArrow.namedChildren.find(Boolean), getName);
162440
+ const block = getter.namedChildren.find((c) => c?.type === "block");
162441
+ if (block) {
162442
+ const stmts = block.namedChildren.filter((c) => c?.type !== "comment");
162443
+ if (stmts.length === 1 && stmts[0]?.type === "return_statement") {
162444
+ return isZeroArgCallTo(stmts[0].namedChildren.find(Boolean), getName);
162445
+ }
162446
+ }
162447
+ return false;
162448
+ }
162042
162449
  var csharpPropertyNameMatchesGetMethodVisitor;
162043
162450
  var init_property_name_matches_get_method = __esm({
162044
162451
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/property-name-matches-get-method.js"() {
@@ -162066,6 +162473,8 @@ var init_property_name_matches_get_method = __esm({
162066
162473
  });
162067
162474
  if (!hasGetMethod)
162068
162475
  return null;
162476
+ if (getterDelegatesTo(node2, target))
162477
+ return null;
162069
162478
  return makeViolation(this.ruleKey, node2, filePath, "low", "Property name matches a Get method", `Property \`${propName}\` coexists with a parameterless \`${target}()\` method \u2014 a confusing duplication of how the value is exposed.`, sourceCode, `Keep one accessor: either the property \`${propName}\` or the method \`${target}\`, not both.`);
162070
162479
  }
162071
162480
  };
@@ -162073,6 +162482,50 @@ var init_property_name_matches_get_method = __esm({
162073
162482
  });
162074
162483
 
162075
162484
  // packages/analyzer/dist/rules/code-quality/visitors/csharp/public-const-versioning-hazard.js
162485
+ function constFieldNames(fieldDecl) {
162486
+ const names = [];
162487
+ const varDecl = fieldDecl.namedChildren.find((c) => c?.type === "variable_declaration");
162488
+ if (!varDecl)
162489
+ return names;
162490
+ for (const vd of varDecl.namedChildren) {
162491
+ if (vd?.type !== "variable_declarator")
162492
+ continue;
162493
+ const n = vd.childForFieldName("name")?.text ?? vd.namedChildren.find((c) => c?.type === "identifier")?.text;
162494
+ if (n)
162495
+ names.push(n);
162496
+ }
162497
+ return names;
162498
+ }
162499
+ function referencedBySiblingConst(node2, names) {
162500
+ let root = node2;
162501
+ while (root.parent)
162502
+ root = root.parent;
162503
+ let found = false;
162504
+ const scan3 = (n) => {
162505
+ if (found)
162506
+ return;
162507
+ if (n.type === "field_declaration" && n.id !== node2.id && hasCSharpModifier(n, "const")) {
162508
+ const varDecl = n.namedChildren.find((c) => c?.type === "variable_declaration");
162509
+ if (varDecl) {
162510
+ for (const vd of varDecl.namedChildren) {
162511
+ if (vd?.type !== "variable_declarator")
162512
+ continue;
162513
+ const declaredName = vd.childForFieldName("name");
162514
+ walkCSharp(vd, (id) => {
162515
+ if (id.type === "identifier" && id.id !== declaredName?.id && names.includes(id.text)) {
162516
+ found = true;
162517
+ }
162518
+ });
162519
+ }
162520
+ }
162521
+ }
162522
+ for (const c of n.namedChildren)
162523
+ if (c)
162524
+ scan3(c);
162525
+ };
162526
+ scan3(root);
162527
+ return found;
162528
+ }
162076
162529
  var csharpPublicConstVersioningHazardVisitor;
162077
162530
  var init_public_const_versioning_hazard = __esm({
162078
162531
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/public-const-versioning-hazard.js"() {
@@ -162088,6 +162541,9 @@ var init_public_const_versioning_hazard = __esm({
162088
162541
  return null;
162089
162542
  if (!hasCSharpModifier(node2, "public") && !hasCSharpModifier(node2, "protected"))
162090
162543
  return null;
162544
+ const names = constFieldNames(node2);
162545
+ if (names.length > 0 && referencedBySiblingConst(node2, names))
162546
+ return null;
162091
162547
  return makeViolation(this.ruleKey, node2, filePath, "low", "Public constant versioning hazard", "A `public const` value is inlined into consumers at compile time and will not update unless they recompile \u2014 prefer `static readonly`.", sourceCode, "Replace the `public const` with a `public static readonly` field.");
162092
162548
  }
162093
162549
  };
@@ -162763,12 +163219,12 @@ var init_missing_access_modifier = __esm({
162763
163219
  return null;
162764
163220
  if (node2.parent?.parent?.type === "interface_declaration")
162765
163221
  return null;
163222
+ if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
163223
+ return null;
162766
163224
  if (node2.type === "method_declaration") {
162767
163225
  const nm = node2.childForFieldName("name");
162768
163226
  if (nm?.type === "qualified_name" || (nm?.text ?? "").includes("."))
162769
163227
  return null;
162770
- if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
162771
- return null;
162772
163228
  }
162773
163229
  if (node2.type === "constructor_declaration" && mods.includes("static"))
162774
163230
  return null;
@@ -162832,7 +163288,7 @@ function topLevelTypeIdentifiers(root) {
162832
163288
  const body = m.namedChildren.find((c) => c?.type === "declaration_list");
162833
163289
  if (body)
162834
163290
  collectFrom(body);
162835
- } else if (TYPE_DECL_TYPES2.has(m.type)) {
163291
+ } else if (TYPE_DECL_TYPES3.has(m.type)) {
162836
163292
  const id = m.childForFieldName("name");
162837
163293
  if (id)
162838
163294
  out.push(id);
@@ -162845,6 +163301,8 @@ function topLevelTypeIdentifiers(root) {
162845
163301
  function nameMatches(fileName, typeName2) {
162846
163302
  if (fileName === typeName2)
162847
163303
  return true;
163304
+ if (typeName2 === `${fileName}Attribute`)
163305
+ return true;
162848
163306
  const brace = fileName.indexOf("{");
162849
163307
  if (brace > 0 && fileName.slice(0, brace) === typeName2)
162850
163308
  return true;
@@ -162853,12 +163311,12 @@ function nameMatches(fileName, typeName2) {
162853
163311
  return true;
162854
163312
  return false;
162855
163313
  }
162856
- var TYPE_DECL_TYPES2, csharpFilenameTypeMismatchVisitor;
163314
+ var TYPE_DECL_TYPES3, csharpFilenameTypeMismatchVisitor;
162857
163315
  var init_csharp_filename_type_mismatch = __esm({
162858
163316
  "packages/analyzer/dist/rules/code-quality/visitors/csharp/csharp-filename-type-mismatch.js"() {
162859
163317
  "use strict";
162860
163318
  init_types3();
162861
- TYPE_DECL_TYPES2 = /* @__PURE__ */ new Set([
163319
+ TYPE_DECL_TYPES3 = /* @__PURE__ */ new Set([
162862
163320
  "class_declaration",
162863
163321
  "struct_declaration",
162864
163322
  "interface_declaration",
@@ -162872,13 +163330,18 @@ var init_csharp_filename_type_mismatch = __esm({
162872
163330
  nodeTypes: ["compilation_unit"],
162873
163331
  visit(node2, filePath, sourceCode) {
162874
163332
  const base = filePath.split("/").pop() ?? "";
162875
- const fileName = base.replace(/\.[^.]*$/, "");
163333
+ const lower = base.toLowerCase();
163334
+ const compoundExt = [".cshtml.cs", ".razor.cs"].find((ext2) => lower.endsWith(ext2));
163335
+ const fileName = compoundExt ? base.slice(0, base.length - compoundExt.length) : base.replace(/\.[^.]*$/, "");
162876
163336
  if (!fileName)
162877
163337
  return null;
163338
+ const migrationName = fileName.match(/^\d{14}_(.+)$/)?.[1];
163339
+ const candidateNames2 = migrationName ? [fileName, migrationName] : [fileName];
162878
163340
  const identifiers = topLevelTypeIdentifiers(node2);
162879
163341
  if (identifiers.length === 0)
162880
163342
  return null;
162881
- if (identifiers.some((id) => nameMatches(fileName, id.text)))
163343
+ const matches = (id) => candidateNames2.some((fn) => nameMatches(fn, id.text)) || compoundExt !== void 0 && id.text === `${fileName}Model`;
163344
+ if (identifiers.some(matches))
162882
163345
  return null;
162883
163346
  const first2 = identifiers[0];
162884
163347
  return makeViolation(this.ruleKey, first2, filePath, "low", "File name does not match a contained type", `File '${fileName}' declares type '${first2.text}' but no top-level type matches the file name; name the file after a type so it is easy to locate.`, sourceCode, `Rename the file after a contained type, e.g. ${first2.text}.cs.`);
@@ -163331,7 +163794,7 @@ var init_csharp6 = __esm({
163331
163794
  init_cognitive_complexity3();
163332
163795
  init_collapsible_else_if2();
163333
163796
  init_collapsible_if3();
163334
- init_commented_out_code3();
163797
+ init_commented_out_code4();
163335
163798
  init_comparison_of_constant2();
163336
163799
  init_console_log2();
163337
163800
  init_contradictory_boolean_expression2();
@@ -163414,7 +163877,6 @@ var init_csharp6 = __esm({
163414
163877
  init_unnecessary_namespace_qualifier2();
163415
163878
  init_unsafe_any_usage2();
163416
163879
  init_unused_collection2();
163417
- init_unused_function_parameter2();
163418
163880
  init_unused_private_member2();
163419
163881
  init_unused_private_method2();
163420
163882
  init_unused_private_nested_class2();
@@ -163673,7 +164135,6 @@ var init_csharp6 = __esm({
163673
164135
  csharpUnnecessaryNamespaceQualifierVisitor,
163674
164136
  csharpUnsafeAnyUsageVisitor,
163675
164137
  csharpUnusedCollectionVisitor,
163676
- csharpUnusedFunctionParameterVisitor,
163677
164138
  csharpUnusedPrivateMemberVisitor,
163678
164139
  csharpUnusedPrivateMethodVisitor,
163679
164140
  csharpUnusedPrivateNestedClassVisitor,
@@ -165855,6 +166316,11 @@ function allElementsConstant(creation) {
165855
166316
  return false;
165856
166317
  return elements.every((e) => LITERAL_TYPES6.has(e.type));
165857
166318
  }
166319
+ function isGeneratedOrMigrationFile(filePath) {
166320
+ if (/(^|[\\/])Migrations[\\/]/.test(filePath))
166321
+ return true;
166322
+ return /\.(designer|g|g\.i|generated)\.cs$/i.test(filePath);
166323
+ }
165858
166324
  var LITERAL_TYPES6, csharpConstantArrayArgumentVisitor;
165859
166325
  var init_constant_array_argument = __esm({
165860
166326
  "packages/analyzer/dist/rules/performance/visitors/csharp/constant-array-argument.js"() {
@@ -165875,6 +166341,8 @@ var init_constant_array_argument = __esm({
165875
166341
  languages: ["csharp"],
165876
166342
  nodeTypes: ["array_creation_expression", "implicit_array_creation_expression"],
165877
166343
  visit(node2, filePath, sourceCode) {
166344
+ if (isGeneratedOrMigrationFile(filePath))
166345
+ return null;
165878
166346
  if (node2.parent?.type !== "argument")
165879
166347
  return null;
165880
166348
  if (!getCSharpEnclosingFunction(node2))
@@ -166663,7 +167131,36 @@ var init_missing_finally_cleanup2 = __esm({
166663
167131
  });
166664
167132
 
166665
167133
  // packages/analyzer/dist/rules/reliability/visitors/csharp/unsafe-json-parse.js
166666
- var JSON_PARSERS, csharpUnsafeJsonParseVisitor;
167134
+ function bodyDeclaresMember(body, name) {
167135
+ for (const member of body.namedChildren) {
167136
+ if (!member)
167137
+ continue;
167138
+ if (member.type === "property_declaration") {
167139
+ if (member.childForFieldName("name")?.text === name)
167140
+ return true;
167141
+ } else if (member.type === "field_declaration") {
167142
+ const decl = member.namedChildren.find((c) => c?.type === "variable_declaration");
167143
+ for (const d of decl?.namedChildren ?? []) {
167144
+ if (d?.type === "variable_declarator" && d.childForFieldName("name")?.text === name)
167145
+ return true;
167146
+ }
167147
+ }
167148
+ }
167149
+ return false;
167150
+ }
167151
+ function enclosingTypeDeclaresMember(node2, name) {
167152
+ let current = node2.parent;
167153
+ while (current) {
167154
+ if (TYPE_DECLS2.has(current.type)) {
167155
+ const body = current.childForFieldName("body");
167156
+ if (body && bodyDeclaresMember(body, name))
167157
+ return true;
167158
+ }
167159
+ current = current.parent;
167160
+ }
167161
+ return false;
167162
+ }
167163
+ var JSON_PARSERS, TYPE_DECLS2, csharpUnsafeJsonParseVisitor;
166667
167164
  var init_unsafe_json_parse3 = __esm({
166668
167165
  "packages/analyzer/dist/rules/reliability/visitors/csharp/unsafe-json-parse.js"() {
166669
167166
  "use strict";
@@ -166678,15 +167175,25 @@ var init_unsafe_json_parse3 = __esm({
166678
167175
  JsonDocument: /* @__PURE__ */ new Set(["Parse", "ParseAsync"]),
166679
167176
  JsonNode: /* @__PURE__ */ new Set(["Parse"])
166680
167177
  };
167178
+ TYPE_DECLS2 = /* @__PURE__ */ new Set([
167179
+ "class_declaration",
167180
+ "struct_declaration",
167181
+ "record_declaration",
167182
+ "record_struct_declaration"
167183
+ ]);
166681
167184
  csharpUnsafeJsonParseVisitor = {
166682
167185
  ruleKey: "reliability/deterministic/unsafe-json-parse",
166683
167186
  languages: ["csharp"],
166684
167187
  nodeTypes: ["invocation_expression"],
166685
167188
  visit(node2, filePath, sourceCode) {
166686
- const receiver = simpleTypeName5(getCSharpReceiver(node2));
167189
+ const rawReceiver = getCSharpReceiver(node2);
167190
+ const receiver = simpleTypeName5(rawReceiver);
166687
167191
  const method = getCSharpMethodName(node2);
166688
167192
  if (!JSON_PARSERS[receiver]?.has(method))
166689
167193
  return null;
167194
+ const bareReceiver = rawReceiver.startsWith("this.") ? rawReceiver.slice(5) : rawReceiver;
167195
+ if (!bareReceiver.includes(".") && enclosingTypeDeclaresMember(node2, bareReceiver))
167196
+ return null;
166690
167197
  const firstArg = getCSharpArguments(node2)[0];
166691
167198
  if (firstArg?.type === "invocation_expression") {
166692
167199
  const innerReceiver = simpleTypeName5(getCSharpReceiver(firstArg));
@@ -168393,18 +168900,10 @@ var init_logger_field_naming = __esm({
168393
168900
  csharpLoggerFieldNamingVisitor = {
168394
168901
  ruleKey: "style/deterministic/logger-field-naming",
168395
168902
  languages: ["csharp"],
168396
- nodeTypes: ["field_declaration", "property_declaration"],
168903
+ nodeTypes: ["field_declaration"],
168397
168904
  visit(node2, filePath, sourceCode) {
168398
168905
  if (isAutoGeneratedCSharpFile(sourceCode))
168399
168906
  return null;
168400
- if (node2.type === "property_declaration") {
168401
- if (!isLoggerType(node2.childForFieldName("type")))
168402
- return null;
168403
- const name = node2.childForFieldName("name");
168404
- if (!name || ACCEPTED.test(name.text))
168405
- return null;
168406
- return makeViolation(this.ruleKey, name, filePath, "low", "Logger does not follow the naming convention", `Logger '${name.text}' does not follow the convention for logger members (camelCase, optionally _-prefixed).`, sourceCode, `Rename the logger to a camelCase name such as '_logger'.`);
168407
- }
168408
168907
  const declaration = node2.namedChildren.find((c) => c?.type === "variable_declaration");
168409
168908
  if (!declaration)
168410
168909
  return null;
@@ -169212,15 +169711,28 @@ function isNestedInType(node2) {
169212
169711
  const parent = node2.parent;
169213
169712
  if (parent?.type !== "declaration_list")
169214
169713
  return false;
169215
- return parent.parent != null && TYPE_DECL_TYPES3.has(parent.parent.type);
169714
+ return parent.parent != null && TYPE_DECL_TYPES4.has(parent.parent.type);
169216
169715
  }
169217
- var TYPE_DECL_TYPES3, csharpNestedTypePubliclyVisibleVisitor;
169716
+ function isConstantContainer(node2) {
169717
+ if (node2.type !== "class_declaration")
169718
+ return false;
169719
+ if (!hasCSharpModifier(node2, "static"))
169720
+ return false;
169721
+ const body = node2.childForFieldName("body");
169722
+ if (!body)
169723
+ return false;
169724
+ const members = body.namedChildren.filter((c) => c && c.type !== "comment");
169725
+ if (members.length === 0)
169726
+ return false;
169727
+ return members.every((m) => m?.type === "field_declaration" && (hasCSharpModifier(m, "const") || hasCSharpModifier(m, "static") && hasCSharpModifier(m, "readonly")));
169728
+ }
169729
+ var TYPE_DECL_TYPES4, csharpNestedTypePubliclyVisibleVisitor;
169218
169730
  var init_nested_type_publicly_visible = __esm({
169219
169731
  "packages/analyzer/dist/rules/architecture/visitors/csharp/nested-type-publicly-visible.js"() {
169220
169732
  "use strict";
169221
169733
  init_types3();
169222
169734
  init_csharp_helpers();
169223
- TYPE_DECL_TYPES3 = /* @__PURE__ */ new Set([
169735
+ TYPE_DECL_TYPES4 = /* @__PURE__ */ new Set([
169224
169736
  "class_declaration",
169225
169737
  "struct_declaration",
169226
169738
  "interface_declaration",
@@ -169238,6 +169750,8 @@ var init_nested_type_publicly_visible = __esm({
169238
169750
  return null;
169239
169751
  if (getCSharpAttributeNames(node2).length > 0)
169240
169752
  return null;
169753
+ if (isConstantContainer(node2))
169754
+ return null;
169241
169755
  const name = node2.childForFieldName("name")?.text ?? "type";
169242
169756
  return makeViolation(this.ruleKey, node2, filePath, "low", "Publicly visible nested type", `Nested type '${name}' is publicly visible, exposing an implementation detail of its containing type.`, sourceCode, `Make '${name}' private or internal, or lift it out to its own top-level type if it is a real part of the API.`);
169243
169757
  }
@@ -169325,6 +169839,8 @@ var init_type_outside_namespace = __esm({
169325
169839
 
169326
169840
  // packages/analyzer/dist/rules/architecture/visitors/csharp/_uri-helpers.js
169327
169841
  function nameLooksLikeUri(name) {
169842
+ if (PREPOSITION_BEFORE_URI.test(name))
169843
+ return false;
169328
169844
  if (URI_NAME_PATTERN.test(name))
169329
169845
  return true;
169330
169846
  return /(Url|Uri|Endpoint|Webhook)$/.test(name);
@@ -169332,11 +169848,12 @@ function nameLooksLikeUri(name) {
169332
169848
  function isStringType(typeNode) {
169333
169849
  return typeNode?.type === "predefined_type" && typeNode.text === "string";
169334
169850
  }
169335
- var URI_NAME_PATTERN;
169851
+ var URI_NAME_PATTERN, PREPOSITION_BEFORE_URI;
169336
169852
  var init_uri_helpers = __esm({
169337
169853
  "packages/analyzer/dist/rules/architecture/visitors/csharp/_uri-helpers.js"() {
169338
169854
  "use strict";
169339
169855
  URI_NAME_PATTERN = /(^|[^a-z])(url|uri|uris|urls|endpoint|endpoints|webhook|webhookurl|callbackurl|redirecturi|redirecturl|hreflink|baseurl|baseaddress)$/i;
169856
+ PREPOSITION_BEFORE_URI = /(In|As|Of|At|By|Within)(Url|Uri|Uris|Urls|Endpoint|Endpoints|Webhook)$/;
169340
169857
  }
169341
169858
  });
169342
169859
 
@@ -169374,13 +169891,22 @@ var init_uri_parameter_as_string = __esm({
169374
169891
  });
169375
169892
 
169376
169893
  // packages/analyzer/dist/rules/architecture/visitors/csharp/uri-property-as-string.js
169377
- var csharpUriPropertyAsStringVisitor;
169894
+ function initializerProvesNotUri(valueNode) {
169895
+ if (!valueNode || !isCSharpStringNode(valueNode))
169896
+ return false;
169897
+ const text4 = getCSharpStringText(valueNode);
169898
+ if (text4 == null || text4.length === 0)
169899
+ return false;
169900
+ return !text4.includes("://");
169901
+ }
169902
+ var BINDING_ATTRIBUTES, csharpUriPropertyAsStringVisitor;
169378
169903
  var init_uri_property_as_string = __esm({
169379
169904
  "packages/analyzer/dist/rules/architecture/visitors/csharp/uri-property-as-string.js"() {
169380
169905
  "use strict";
169381
169906
  init_types3();
169382
169907
  init_csharp_helpers();
169383
169908
  init_uri_helpers();
169909
+ BINDING_ATTRIBUTES = /* @__PURE__ */ new Set(["BindProperty", "FromQuery", "FromRoute", "FromForm", "ModelBinder"]);
169384
169910
  csharpUriPropertyAsStringVisitor = {
169385
169911
  ruleKey: "architecture/deterministic/uri-property-as-string",
169386
169912
  languages: ["csharp"],
@@ -169393,6 +169919,10 @@ var init_uri_property_as_string = __esm({
169393
169919
  const name = node2.childForFieldName("name")?.text;
169394
169920
  if (!name || !nameLooksLikeUri(name))
169395
169921
  return null;
169922
+ if (getCSharpAttributeNames(node2).some((a) => BINDING_ATTRIBUTES.has(a)))
169923
+ return null;
169924
+ if (initializerProvesNotUri(node2.childForFieldName("value")))
169925
+ return null;
169396
169926
  return makeViolation(this.ruleKey, node2.childForFieldName("type"), filePath, "low", "URI property typed as string", `Property '${name}' holds a URI but is typed as string; use System.Uri to get parsing and validation.`, sourceCode, `Change the type of '${name}' from string to System.Uri.`);
169397
169927
  }
169398
169928
  };
@@ -169430,6 +169960,21 @@ function isModelType(classDecl) {
169430
169960
  const name = classDecl.childForFieldName("name")?.text ?? "";
169431
169961
  return MODEL_NAME_SUFFIX.test(name);
169432
169962
  }
169963
+ function isServerConstructed(classDecl, body) {
169964
+ for (const member of body.namedChildren) {
169965
+ if (member?.type === "constructor_declaration") {
169966
+ const params = member.childForFieldName("parameters");
169967
+ if (params && params.namedChildCount > 0)
169968
+ return true;
169969
+ }
169970
+ if (member?.type === "method_declaration" && hasCSharpModifier(member, "static")) {
169971
+ const mName = member.childForFieldName("name")?.text ?? "";
169972
+ if (mName === "Create" || mName.startsWith("From"))
169973
+ return true;
169974
+ }
169975
+ }
169976
+ return false;
169977
+ }
169433
169978
  function flaggablePropertyType(typeNode) {
169434
169979
  if (typeNode?.type !== "predefined_type")
169435
169980
  return null;
@@ -169442,7 +169987,7 @@ var init_value_type_action_param_under_posting = __esm({
169442
169987
  "use strict";
169443
169988
  init_types3();
169444
169989
  init_csharp_helpers();
169445
- MODEL_NAME_SUFFIX = /(Request|Dto|DTO|Model|ViewModel|Input|Form|Command|Query|Payload)$/;
169990
+ MODEL_NAME_SUFFIX = /(Request|Input|Form|Command|Query|Payload)$/;
169446
169991
  SCALAR_VALUE_TYPES = /* @__PURE__ */ new Set(["int", "long", "short", "byte", "uint", "ulong", "ushort", "sbyte", "decimal", "double", "float", "bool", "char"]);
169447
169992
  csharpValueTypeActionParamUnderPostingVisitor = {
169448
169993
  ruleKey: "architecture/deterministic/value-type-action-param-under-posting",
@@ -169454,6 +169999,8 @@ var init_value_type_action_param_under_posting = __esm({
169454
169999
  const body = node2.childForFieldName("body");
169455
170000
  if (!body)
169456
170001
  return null;
170002
+ if (isServerConstructed(node2, body))
170003
+ return null;
169457
170004
  for (const member of body.namedChildren) {
169458
170005
  if (member?.type !== "property_declaration")
169459
170006
  continue;
@@ -169481,6 +170028,22 @@ var init_value_type_action_param_under_posting = __esm({
169481
170028
  });
169482
170029
 
169483
170030
  // packages/analyzer/dist/rules/architecture/visitors/csharp/action-missing-producesresponsetype.js
170031
+ function excludedFromApiExplorer(node2) {
170032
+ for (const child of node2.children) {
170033
+ if (child?.type !== "attribute_list")
170034
+ continue;
170035
+ for (const attr of child.namedChildren) {
170036
+ if (attr?.type !== "attribute")
170037
+ continue;
170038
+ const name = (attr.childForFieldName("name")?.text ?? "").split(".").pop();
170039
+ if (name !== "ApiExplorerSettings")
170040
+ continue;
170041
+ if (/\bIgnoreApi\s*=\s*true\b/i.test(attr.text))
170042
+ return true;
170043
+ }
170044
+ }
170045
+ return false;
170046
+ }
169484
170047
  function attributeNames7(node2) {
169485
170048
  const names = [];
169486
170049
  for (const child of node2.children) {
@@ -169528,6 +170091,8 @@ var init_action_missing_producesresponsetype = __esm({
169528
170091
  const classAttrs = attributeNames7(cls);
169529
170092
  if (classAttrs.some((a) => a.startsWith("ProducesResponseType") || a === "ProducesDefaultResponseType"))
169530
170093
  return null;
170094
+ if (excludedFromApiExplorer(node2) || excludedFromApiExplorer(cls))
170095
+ return null;
169531
170096
  const name = node2.childForFieldName("name");
169532
170097
  return makeViolation(this.ruleKey, name ?? node2, filePath, "low", "Controller action has no [ProducesResponseType]", `Action '${name?.text ?? ""}' declares no [ProducesResponseType]; its response shapes and status codes are missing from the API description.`, sourceCode, "Annotate the action with [ProducesResponseType] for each status code it can return.");
169533
170098
  }
@@ -171470,52 +172035,96 @@ var init_pyproject_checker = __esm({
171470
172035
  // packages/analyzer/dist/roslyn-host-client.js
171471
172036
  import { spawn as spawn6 } from "child_process";
171472
172037
  import { existsSync as existsSync9 } from "fs";
172038
+ import { tmpdir } from "os";
171473
172039
  import { dirname as dirname8, resolve as resolve9 } from "path";
171474
172040
  import { fileURLToPath as fileURLToPath4 } from "url";
171475
172041
  function resolveRoslynHostBinary() {
171476
172042
  const override = process.env[HOST_BINARY_ENV];
171477
172043
  if (override)
171478
172044
  return existsSync9(override) ? override : null;
171479
- const here = dirname8(fileURLToPath4(import.meta.url));
171480
- const rel = "tools/csharp-roslyn-host/bin/Release/net8.0/csharp-roslyn-host";
171481
- for (const up of ["../../..", "../../../..", "../../../../.."]) {
171482
- const candidate = resolve9(here, up, rel);
171483
- if (existsSync9(candidate))
171484
- return candidate;
172045
+ const rels = [
172046
+ "roslyn-host/csharp-roslyn-host.dll",
172047
+ "tools/csharp-roslyn-host/bin/Release/net8.0/csharp-roslyn-host"
172048
+ ];
172049
+ let dir = dirname8(fileURLToPath4(import.meta.url));
172050
+ for (let i = 0; i < 12; i++) {
172051
+ for (const rel of rels) {
172052
+ const candidate = resolve9(dir, rel);
172053
+ if (existsSync9(candidate))
172054
+ return candidate;
172055
+ }
172056
+ const parent = dirname8(dir);
172057
+ if (parent === dir)
172058
+ break;
172059
+ dir = parent;
171485
172060
  }
171486
172061
  return null;
171487
172062
  }
171488
172063
  function invokeHost(bin, request) {
171489
172064
  return new Promise((resolvePromise, reject) => {
171490
- const child = spawn6(bin, [], { stdio: ["pipe", "pipe", "pipe"] });
172065
+ const viaDotnet = bin.endsWith(".dll");
172066
+ const cmd = viaDotnet ? "dotnet" : bin;
172067
+ const args = viaDotnet ? [bin] : [];
172068
+ const child = spawn6(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: tmpdir() });
171491
172069
  let stdout = "";
171492
172070
  let stderr = "";
172071
+ let settled = false;
172072
+ const fail = (err) => {
172073
+ if (settled)
172074
+ return;
172075
+ settled = true;
172076
+ reject(err);
172077
+ };
172078
+ const succeed = (violations) => {
172079
+ if (settled)
172080
+ return;
172081
+ settled = true;
172082
+ resolvePromise(violations);
172083
+ };
171493
172084
  child.stdout.setEncoding("utf8");
171494
172085
  child.stderr.setEncoding("utf8");
171495
172086
  child.stdout.on("data", (d) => stdout += d);
171496
172087
  child.stderr.on("data", (d) => stderr += d);
171497
- child.on("error", (e) => reject(new RoslynHostUnavailableError(`Failed to start the Roslyn host (${bin}): ${e.message}. Is the .NET runtime installed?`)));
171498
- child.on("close", () => {
171499
- const line = stdout.split("\n").find((l) => l.trim());
171500
- if (!line) {
171501
- reject(new Error(`Roslyn host produced no output.${stderr ? ` stderr: ${stderr}` : ""}`));
172088
+ child.on("error", (e) => fail(new RoslynHostUnavailableError(`Failed to start the Roslyn host (${viaDotnet ? `dotnet ${bin}` : bin}): ${e.message}. Is the .NET runtime installed?`)));
172089
+ child.stdin.on("error", () => {
172090
+ });
172091
+ child.on("close", (code, signalName) => {
172092
+ if (code !== 0 || signalName) {
172093
+ const reason = stderr.trim() || stdout.trim() || "(no output)";
172094
+ fail(new RoslynHostUnavailableError(`The Roslyn host exited ${signalName ? `via ${signalName}` : `with code ${code}`} before responding. Is a compatible .NET runtime installed? Host output: ${reason}`));
172095
+ return;
172096
+ }
172097
+ const lines = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
172098
+ if (lines.length === 0) {
172099
+ fail(new Error(`Roslyn host produced no output.${stderr ? ` stderr: ${stderr}` : ""}`));
171502
172100
  return;
171503
172101
  }
171504
172102
  let resp;
171505
- try {
171506
- resp = JSON.parse(line);
171507
- } catch {
171508
- reject(new Error(`Roslyn host returned invalid JSON: ${line}`));
172103
+ for (const l of lines) {
172104
+ try {
172105
+ const parsed = JSON.parse(l);
172106
+ if (parsed && typeof parsed.ok === "boolean") {
172107
+ resp = parsed;
172108
+ break;
172109
+ }
172110
+ } catch {
172111
+ }
172112
+ }
172113
+ if (!resp) {
172114
+ fail(new Error(`Roslyn host returned invalid JSON: ${lines[0]}`));
171509
172115
  return;
171510
172116
  }
171511
172117
  if (!resp.ok) {
171512
- reject(new Error(`Roslyn host error: ${resp.error ?? "unknown"}`));
172118
+ fail(new Error(`Roslyn host error: ${resp.error ?? "unknown"}`));
171513
172119
  return;
171514
172120
  }
171515
- resolvePromise(resp.violations ?? []);
172121
+ succeed(resp.violations ?? []);
171516
172122
  });
171517
- child.stdin.write(JSON.stringify(request) + "\n");
171518
- child.stdin.end();
172123
+ try {
172124
+ child.stdin.write(JSON.stringify(request) + "\n");
172125
+ child.stdin.end();
172126
+ } catch {
172127
+ }
171519
172128
  });
171520
172129
  }
171521
172130
  function runRoslynWorkspace(projectPath, rules) {
@@ -178074,7 +178683,7 @@ var init_schemas2 = __esm({
178074
178683
  // packages/core/dist/services/llm/cli-provider.js
178075
178684
  import { mkdirSync, writeFileSync } from "node:fs";
178076
178685
  import { join as join10 } from "node:path";
178077
- import { tmpdir } from "node:os";
178686
+ import { tmpdir as tmpdir2 } from "node:os";
178078
178687
  import { randomUUID as randomUUID23 } from "node:crypto";
178079
178688
  var import_cross_spawn, BaseCLIProvider, ClaudeCodeProvider;
178080
178689
  var init_cli_provider = __esm({
@@ -178144,7 +178753,7 @@ var init_cli_provider = __esm({
178144
178753
  constructor(transport) {
178145
178754
  this.transport = transport;
178146
178755
  if (process.env.TRUECOURSE_CLI_DEBUG) {
178147
- this.debugDir = join10(tmpdir(), "truecourse-cli-debug");
178756
+ this.debugDir = join10(tmpdir2(), "truecourse-cli-debug");
178148
178757
  mkdirSync(this.debugDir, { recursive: true });
178149
178758
  log.info(`[CLI] Debug output: ${this.debugDir}`);
178150
178759
  }
@@ -178257,8 +178866,13 @@ var init_cli_provider = __esm({
178257
178866
  unregisterChildProcess(this._repoId, child);
178258
178867
  reject(new Error(`[CLI] Failed to spawn ${this.binaryName}: ${err.message}`));
178259
178868
  });
178260
- child.stdin.write(prompt);
178261
- child.stdin.end();
178869
+ child.stdin.on("error", () => {
178870
+ });
178871
+ try {
178872
+ child.stdin.write(prompt);
178873
+ child.stdin.end();
178874
+ } catch {
178875
+ }
178262
178876
  });
178263
178877
  }
178264
178878
  /** Extract usage data from CLI JSON envelope if present. */
@@ -186525,7 +187139,7 @@ var DOMAIN_LABELS = {
186525
187139
  "database": "Database",
186526
187140
  "style": "Style"
186527
187141
  };
186528
- function buildAnalysisSteps(enabledCategories, enableLlmRules) {
187142
+ function buildAnalysisSteps(enabledCategories, enableLlmRules, hasCSharp) {
186529
187143
  const steps = [
186530
187144
  { key: "parse", label: "Parsing repository" }
186531
187145
  ];
@@ -186536,6 +187150,9 @@ function buildAnalysisSteps(enabledCategories, enableLlmRules) {
186536
187150
  for (const domain of activeDomains) {
186537
187151
  steps.push({ key: domain, label: `${DOMAIN_LABELS[domain]} checks` });
186538
187152
  }
187153
+ if (hasCSharp) {
187154
+ steps.push({ key: "csharp", label: "C# semantic analysis" });
187155
+ }
186539
187156
  steps.push({ key: "persist", label: "Saving results" });
186540
187157
  return steps;
186541
187158
  }
@@ -186562,6 +187179,23 @@ var StepTracker = class {
186562
187179
  this.emit();
186563
187180
  }
186564
187181
  }
187182
+ /**
187183
+ * Insert a step at runtime, just before `persist`, if it isn't already there.
187184
+ * Used for phases we can't declare up front — e.g. the C# semantic tier only
187185
+ * exists when the repo actually has C#, which isn't known until files are read.
187186
+ * No-op if the key already exists.
187187
+ */
187188
+ ensureStep(key4, label) {
187189
+ if (this.steps.some((s) => s.key === key4))
187190
+ return;
187191
+ const persistIdx = this.steps.findIndex((s) => s.key === "persist");
187192
+ const step = { key: key4, label, status: "pending" };
187193
+ if (persistIdx === -1)
187194
+ this.steps.push(step);
187195
+ else
187196
+ this.steps.splice(persistIdx, 0, step);
187197
+ this.emit();
187198
+ }
186565
187199
  setStatus(key4, status, detail) {
186566
187200
  const step = this.steps.find((s) => s.key === key4);
186567
187201
  if (step) {
@@ -195238,7 +195872,7 @@ import path17 from "node:path";
195238
195872
  function writeEvidence(params) {
195239
195873
  const dir = evidenceScenarioDir(params.repoRoot, params.runId, params.scenarioId);
195240
195874
  fs18.mkdirSync(dir, { recursive: true });
195241
- const failing = params.steps.find((s) => s.index === params.failingStep);
195875
+ const focus = params.failingStep != null ? params.steps.find((s) => s.index === params.failingStep) : params.steps[params.steps.length - 1];
195242
195876
  const invocation = {
195243
195877
  scenarioId: params.scenarioId,
195244
195878
  title: params.title,
@@ -195258,11 +195892,11 @@ function writeEvidence(params) {
195258
195892
  }))
195259
195893
  };
195260
195894
  writeFile(dir, "invocation.json", JSON.stringify(invocation, null, 2));
195261
- if (failing) {
195262
- writeFile(dir, "stdout.raw.txt", failing.rawStdout);
195263
- writeFile(dir, "stdout.txt", failing.normStdout);
195264
- writeFile(dir, "stderr.raw.txt", failing.rawStderr);
195265
- writeFile(dir, "stderr.txt", failing.normStderr);
195895
+ if (focus) {
195896
+ writeFile(dir, "stdout.raw.txt", focus.rawStdout);
195897
+ writeFile(dir, "stdout.txt", focus.normStdout);
195898
+ writeFile(dir, "stderr.raw.txt", focus.rawStderr);
195899
+ writeFile(dir, "stderr.txt", focus.normStderr);
195266
195900
  }
195267
195901
  const diffLines = [];
195268
195902
  if (params.outcome === "fail" && params.mismatch) {
@@ -195270,8 +195904,10 @@ function writeEvidence(params) {
195270
195904
  diffLines.push(`expected: ${params.mismatch.expected}`);
195271
195905
  diffLines.push(`actual: ${params.mismatch.actual}`, "");
195272
195906
  diffLines.push(...params.mismatch.detail);
195273
- } else if (params.infraMessage) {
195907
+ } else if (params.outcome === "error" && params.infraMessage) {
195274
195908
  diffLines.push(`step ${params.failingStep} \u2014 infrastructure error`, "", params.infraMessage);
195909
+ } else if (params.outcome === "pass") {
195910
+ diffLines.push(`all ${params.steps.length} step${params.steps.length === 1 ? "" : "s"} met their expectations`);
195275
195911
  }
195276
195912
  writeFile(dir, "diff.txt", diffLines.join("\n") + "\n");
195277
195913
  writeFile(dir, "files.txt", listSandboxFiles(params.sandboxCwd).join("\n") + "\n");
@@ -195385,7 +196021,7 @@ async function runScenario(scenario, ctx) {
195385
196021
  if (capture.spawnError || capture.timedOut) {
195386
196022
  const infra = capture.timedOut ? `step timed out after ${ctx.stepTimeoutMs}ms` : `failed to spawn: ${capture.spawnError}`;
195387
196023
  records.push(toRecord(stepIndex, argv, step.stdin, repeat3, iteration, capture, normText));
195388
- const evidencePath = writeEvidence({
196024
+ const evidencePath2 = writeEvidence({
195389
196025
  repoRoot: ctx.repoRoot,
195390
196026
  runId: ctx.runId,
195391
196027
  scenarioId: scenario.id,
@@ -195403,7 +196039,7 @@ async function runScenario(scenario, ctx) {
195403
196039
  outcome: "error",
195404
196040
  durationMs: Date.now() - start,
195405
196041
  failure: { step: stepIndex, expected: "the step to run", actual: infra },
195406
- evidencePath
196042
+ evidencePath: evidencePath2
195407
196043
  };
195408
196044
  }
195409
196045
  const normStdout = normText(capture.stdout);
@@ -195418,7 +196054,7 @@ async function runScenario(scenario, ctx) {
195418
196054
  });
195419
196055
  if (mismatch) {
195420
196056
  records.push(toRecord(stepIndex, argv, step.stdin, repeat3, iteration, capture, normText));
195421
- const evidencePath = writeEvidence({
196057
+ const evidencePath2 = writeEvidence({
195422
196058
  repoRoot: ctx.repoRoot,
195423
196059
  runId: ctx.runId,
195424
196060
  scenarioId: scenario.id,
@@ -195440,7 +196076,7 @@ async function runScenario(scenario, ctx) {
195440
196076
  expected: mismatch.expected,
195441
196077
  actual: mismatch.actual
195442
196078
  },
195443
- evidencePath
196079
+ evidencePath: evidencePath2
195444
196080
  };
195445
196081
  }
195446
196082
  }
@@ -195448,7 +196084,18 @@ async function runScenario(scenario, ctx) {
195448
196084
  records.push(toRecord(stepIndex, argv, step.stdin, repeat3, repeat3, lastCapture, normText));
195449
196085
  }
195450
196086
  }
195451
- return { ...base, outcome: "pass", durationMs: Date.now() - start };
196087
+ const evidencePath = ctx.capturePassEvidence ? writeEvidence({
196088
+ repoRoot: ctx.repoRoot,
196089
+ runId: ctx.runId,
196090
+ scenarioId: scenario.id,
196091
+ title: scenario.title,
196092
+ binds: scenario.binds,
196093
+ outcome: "pass",
196094
+ steps: records,
196095
+ sandboxCwd: sandbox.cwd,
196096
+ envPins: ENV_PINS
196097
+ }) : void 0;
196098
+ return { ...base, outcome: "pass", durationMs: Date.now() - start, ...evidencePath ? { evidencePath } : {} };
195452
196099
  } finally {
195453
196100
  sandbox.cleanup();
195454
196101
  }
@@ -195741,13 +196388,15 @@ async function runGuard(opts) {
195741
196388
  settled += 1;
195742
196389
  opts.onScenarioSettled?.(settled, selected.length, result);
195743
196390
  }
196391
+ const capturePassEvidence = opts.persist !== false;
195744
196392
  const executed = await mapWithConcurrency(executable, concurrency, async ({ scenario, resolution }) => {
195745
196393
  const outcome = await runScenario(scenario, {
195746
196394
  repoRoot,
195747
196395
  runId,
195748
196396
  resolvedEntry,
195749
196397
  recipeEnv: loaded.recipe.env,
195750
- stepTimeoutMs
196398
+ stepTimeoutMs,
196399
+ capturePassEvidence
195751
196400
  });
195752
196401
  const result = resolution.kind === "remap" ? { ...outcome, remappedTo: resolution.section.anchor } : outcome;
195753
196402
  settled += 1;
@@ -205321,7 +205970,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
205321
205970
  "obj",
205322
205971
  ".vs"
205323
205972
  ]);
205324
- function isTestFile2(filePath) {
205973
+ function isTestFile3(filePath) {
205325
205974
  const parts = filePath.replace(/\\/g, "/").split("/");
205326
205975
  const fileName = parts[parts.length - 1] ?? "";
205327
205976
  if (/\.(test|spec)\.[mc]?[jt]sx?$/.test(fileName))
@@ -205355,7 +206004,7 @@ async function eachParsedSource(rootDir, visit) {
205355
206004
  }
205356
206005
  if (!entry.isFile())
205357
206006
  continue;
205358
- if (isTestFile2(full))
206007
+ if (isTestFile3(full))
205359
206008
  continue;
205360
206009
  const lang = EXT_TO_LANG[path33.extname(entry.name)];
205361
206010
  if (!lang)
@@ -205435,19 +206084,19 @@ function readCsharpString(node2, source) {
205435
206084
  }
205436
206085
  if (node2.type === "interpolated_string_expression") {
205437
206086
  let text4 = "";
205438
- let hasInterpolation2 = false;
206087
+ let hasInterpolation = false;
205439
206088
  for (let i = 0; i < node2.namedChildCount; i++) {
205440
206089
  const c = node2.namedChild(i);
205441
206090
  if (!c)
205442
206091
  continue;
205443
206092
  if (c.type === "interpolation") {
205444
206093
  text4 += " /*INTERP*/ ";
205445
- hasInterpolation2 = true;
206094
+ hasInterpolation = true;
205446
206095
  } else {
205447
206096
  text4 += source.slice(c.startIndex, c.endIndex);
205448
206097
  }
205449
206098
  }
205450
- return { text: text4, hasInterpolation: hasInterpolation2 };
206099
+ return { text: text4, hasInterpolation };
205451
206100
  }
205452
206101
  if (node2.type === "binary_expression") {
205453
206102
  const op = node2.childForFieldName("operator");
@@ -209370,14 +210019,14 @@ function getSqlText(node2, source) {
209370
210019
  return { text: source.slice(node2.startIndex, node2.endIndex), hasInterpolation: false };
209371
210020
  }
209372
210021
  function extractFromSqlSource(sqlNode, filePath, source) {
209373
- const { text: text4, hasInterpolation: hasInterpolation2 } = getSqlText(sqlNode, source);
209374
- return buildQueriesFromSqlText(text4, hasInterpolation2, {
210022
+ const { text: text4, hasInterpolation } = getSqlText(sqlNode, source);
210023
+ return buildQueriesFromSqlText(text4, hasInterpolation, {
209375
210024
  filePath,
209376
210025
  lineStart: sqlNode.startPosition.row + 1,
209377
210026
  lineEnd: sqlNode.endPosition.row + 1
209378
210027
  });
209379
210028
  }
209380
- function buildQueriesFromSqlText(text4, hasInterpolation2, loc, adapterName = "raw-sql") {
210029
+ function buildQueriesFromSqlText(text4, hasInterpolation, loc, adapterName = "raw-sql") {
209381
210030
  if (!text4 || !/\bSELECT\b/i.test(text4))
209382
210031
  return [];
209383
210032
  const blocks = splitSelectBlocks(text4);
@@ -209396,7 +210045,7 @@ function buildQueriesFromSqlText(text4, hasInterpolation2, loc, adapterName = "r
209396
210045
  else
209397
210046
  unparseable.push({ reason: "unrecognised SQL predicate", raw: piece });
209398
210047
  }
209399
- if (hasInterpolation2) {
210048
+ if (hasInterpolation) {
209400
210049
  unparseable.push({ reason: "string-interpolation substitution skipped", raw: "${...}" });
209401
210050
  }
209402
210051
  out.push({
@@ -209761,7 +210410,7 @@ function pySqlText(node2, source) {
209761
210410
  function readPyStringNode(node2, source) {
209762
210411
  if (node2.type === "concatenated_string") {
209763
210412
  let text5 = "";
209764
- let hasInterpolation3 = false;
210413
+ let hasInterpolation2 = false;
209765
210414
  for (let i = 0; i < node2.namedChildCount; i++) {
209766
210415
  const c = node2.namedChild(i);
209767
210416
  if (!c)
@@ -209769,15 +210418,15 @@ function readPyStringNode(node2, source) {
209769
210418
  const part = readPyStringNode(c, source);
209770
210419
  if (part) {
209771
210420
  text5 += part.text;
209772
- hasInterpolation3 = hasInterpolation3 || part.hasInterpolation;
210421
+ hasInterpolation2 = hasInterpolation2 || part.hasInterpolation;
209773
210422
  }
209774
210423
  }
209775
- return { text: text5, hasInterpolation: hasInterpolation3 };
210424
+ return { text: text5, hasInterpolation: hasInterpolation2 };
209776
210425
  }
209777
210426
  if (node2.type !== "string")
209778
210427
  return null;
209779
210428
  let text4 = "";
209780
- let hasInterpolation2 = false;
210429
+ let hasInterpolation = false;
209781
210430
  for (let i = 0; i < node2.namedChildCount; i++) {
209782
210431
  const c = node2.namedChild(i);
209783
210432
  if (!c)
@@ -209786,10 +210435,10 @@ function readPyStringNode(node2, source) {
209786
210435
  text4 += source.slice(c.startIndex, c.endIndex);
209787
210436
  else if (c.type === "interpolation") {
209788
210437
  text4 += " /*INTERP*/ ";
209789
- hasInterpolation2 = true;
210438
+ hasInterpolation = true;
209790
210439
  }
209791
210440
  }
209792
- return { text: text4, hasInterpolation: hasInterpolation2 };
210441
+ return { text: text4, hasInterpolation };
209793
210442
  }
209794
210443
 
209795
210444
  // packages/contract-verifier/dist/extractor/query/shared/python-orm.js
@@ -216899,7 +217548,7 @@ async function buildCodebaseScan(codeDir) {
216899
217548
 
216900
217549
  // packages/contract-verifier/dist/extractor/index.js
216901
217550
  var TS_EXT4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
216902
- function isTestFile3(filePath) {
217551
+ function isTestFile4(filePath) {
216903
217552
  const parts = filePath.replace(/\\/g, "/").split("/");
216904
217553
  const fileName = parts[parts.length - 1];
216905
217554
  if (/\.(test|spec)\.(t|j)sx?$/.test(fileName))
@@ -216929,7 +217578,7 @@ async function extractOperationsFromDir(rootDir) {
216929
217578
  const ext2 = path44.extname(entry.name);
216930
217579
  if (!TS_EXT4.has(ext2))
216931
217580
  continue;
216932
- if (isTestFile3(full))
217581
+ if (isTestFile4(full))
216933
217582
  continue;
216934
217583
  const source = fs43.readFileSync(full, "utf-8");
216935
217584
  const lang = ext2 === ".ts" || ext2 === ".tsx" ? ext2 === ".tsx" ? "tsx" : "typescript" : "javascript";
@@ -226618,7 +227267,7 @@ var PROMPT_OVERHEAD_TOKENS = 500;
226618
227267
  var TOKENS_PER_RULE = 50;
226619
227268
  var TOKENS_PER_FILE_PATH = 25;
226620
227269
  var MAX_CHARS_PER_BATCH = 1e5;
226621
- function isTestFile4(filePath) {
227270
+ function isTestFile5(filePath) {
226622
227271
  return TEST_PATTERNS.some((p) => filePath.includes(p));
226623
227272
  }
226624
227273
  function matchesFileFilter(fa, filter2, fileContent) {
@@ -226653,7 +227302,7 @@ function matchesFileFilter(fa, filter2, fileContent) {
226653
227302
  return false;
226654
227303
  }
226655
227304
  if (filter2.isTestFile !== void 0) {
226656
- if (filter2.isTestFile !== isTestFile4(fa.filePath))
227305
+ if (filter2.isTestFile !== isTestFile5(fa.filePath))
226657
227306
  return false;
226658
227307
  }
226659
227308
  if (filter2.languages) {
@@ -231405,21 +232054,21 @@ function node(getModule) {
231405
232054
  if (lineMatch) {
231406
232055
  let object;
231407
232056
  let method;
231408
- let functionName;
232057
+ let functionName2;
231409
232058
  let typeName2;
231410
232059
  let methodName3;
231411
232060
  if (lineMatch[1]) {
231412
- functionName = lineMatch[1];
231413
- let methodStart = functionName.lastIndexOf(".");
231414
- if (functionName[methodStart - 1] === ".") {
232061
+ functionName2 = lineMatch[1];
232062
+ let methodStart = functionName2.lastIndexOf(".");
232063
+ if (functionName2[methodStart - 1] === ".") {
231415
232064
  methodStart--;
231416
232065
  }
231417
232066
  if (methodStart > 0) {
231418
- object = functionName.slice(0, methodStart);
231419
- method = functionName.slice(methodStart + 1);
232067
+ object = functionName2.slice(0, methodStart);
232068
+ method = functionName2.slice(methodStart + 1);
231420
232069
  const objectEnd = object.indexOf(".Module");
231421
232070
  if (objectEnd > 0) {
231422
- functionName = functionName.slice(objectEnd + 1);
232071
+ functionName2 = functionName2.slice(objectEnd + 1);
231423
232072
  object = object.slice(0, objectEnd);
231424
232073
  }
231425
232074
  }
@@ -231431,11 +232080,11 @@ function node(getModule) {
231431
232080
  }
231432
232081
  if (method === "<anonymous>") {
231433
232082
  methodName3 = void 0;
231434
- functionName = void 0;
232083
+ functionName2 = void 0;
231435
232084
  }
231436
- if (functionName === void 0) {
232085
+ if (functionName2 === void 0) {
231437
232086
  methodName3 = methodName3 || UNKNOWN_FUNCTION;
231438
- functionName = typeName2 ? `${typeName2}.${methodName3}` : methodName3;
232087
+ functionName2 = typeName2 ? `${typeName2}.${methodName3}` : methodName3;
231439
232088
  }
231440
232089
  let filename = lineMatch[2]?.startsWith("file://") ? lineMatch[2].slice(7) : lineMatch[2];
231441
232090
  const isNative = lineMatch[5] === "native";
@@ -231448,7 +232097,7 @@ function node(getModule) {
231448
232097
  return {
231449
232098
  filename: filename ? decodeURI(filename) : void 0,
231450
232099
  module: getModule ? getModule(filename) : void 0,
231451
- function: functionName,
232100
+ function: functionName2,
231452
232101
  lineno: _parseIntOrUndefined(lineMatch[3]),
231453
232102
  colno: _parseIntOrUndefined(lineMatch[4]),
231454
232103
  in_app: filenameIsInApp(filename || "", isNative),
@@ -231645,7 +232294,7 @@ function readToolVersion() {
231645
232294
  if (cachedVersion)
231646
232295
  return cachedVersion;
231647
232296
  if (true) {
231648
- cachedVersion = "0.7.0-next.4";
232297
+ cachedVersion = "0.7.0-next.5";
231649
232298
  return cachedVersion;
231650
232299
  }
231651
232300
  try {
@@ -234043,7 +234692,7 @@ function findCSharpProjectFiles(repoPath) {
234043
234692
  continue;
234044
234693
  walk20(path63.join(dir, e.name), depth + 1);
234045
234694
  } else if (e.isFile()) {
234046
- if (e.name.endsWith(".sln"))
234695
+ if (e.name.endsWith(".sln") || e.name.endsWith(".slnx"))
234047
234696
  solutions.push(path63.join(dir, e.name));
234048
234697
  else if (e.name.endsWith(".csproj"))
234049
234698
  projects.push(path63.join(dir, e.name));
@@ -234306,6 +234955,29 @@ async function runViolationPipeline(input) {
234306
234955
  }
234307
234956
  }
234308
234957
  }
234958
+ {
234959
+ const detByDomain = /* @__PURE__ */ new Map();
234960
+ for (const v of allCodeViolations) {
234961
+ const d = v.ruleKey.split("/")[0];
234962
+ detByDomain.set(d, (detByDomain.get(d) ?? 0) + 1);
234963
+ }
234964
+ const archDet = serviceViolationResults.length + moduleViolationResults.length + methodViolationResults.length;
234965
+ if (archDet > 0)
234966
+ detByDomain.set("architecture", (detByDomain.get("architecture") ?? 0) + archDet);
234967
+ for (const domain of DOMAIN_ORDER) {
234968
+ const count = detByDomain.get(domain) ?? 0;
234969
+ tracker?.done(domain, count > 0 ? `${count} violations` : "Clean");
234970
+ }
234971
+ }
234972
+ const detViolationCount = allCodeViolations.length;
234973
+ let csharpStepActive = false;
234974
+ const startCsharpStep = (detail) => {
234975
+ if (!csharpStepActive) {
234976
+ tracker?.ensureStep("csharp", "C# semantic analysis");
234977
+ csharpStepActive = true;
234978
+ }
234979
+ tracker?.start("csharp", detail);
234980
+ };
234309
234981
  const enabledHostRules = enabledCodeRules.filter((r) => r.engine === "roslyn-host");
234310
234982
  if (enabledHostRules.length > 0) {
234311
234983
  const csharpFiles = [];
@@ -234319,6 +234991,7 @@ async function runViolationPipeline(input) {
234319
234991
  csharpFiles.push({ path: key4, text: fc.content });
234320
234992
  }
234321
234993
  if (csharpFiles.length > 0) {
234994
+ startCsharpStep(`Analyzing ${csharpFiles.length} C# files\u2026`);
234322
234995
  const ruleByKey = new Map(enabledHostRules.map((r) => [r.key, r]));
234323
234996
  const hostViolations = await runRoslynHost(csharpFiles, enabledHostRules.map((r) => r.key));
234324
234997
  for (const v of hostViolations) {
@@ -234345,19 +235018,20 @@ async function runViolationPipeline(input) {
234345
235018
  if (enabledWorkspaceRules.length > 0 && filesToScan.some((f2) => detectLanguage(f2.filePath) === "csharp")) {
234346
235019
  const projectFiles = findCSharpProjectFiles(repoPath);
234347
235020
  if (projectFiles.length === 0) {
234348
- log.info("[Pipeline] C# present but no .csproj/.sln found \u2014 project-aware C# rules skipped");
235021
+ log.info("[Pipeline] C# present but no .csproj/.sln/.slnx found \u2014 project-aware C# rules skipped");
234349
235022
  } else {
234350
235023
  const ruleByKey = new Map(enabledWorkspaceRules.map((r) => [r.key, r]));
234351
235024
  const ruleKeys = enabledWorkspaceRules.map((r) => r.key);
234352
- for (const projectFile of projectFiles) {
235025
+ for (const [pi, projectFile] of projectFiles.entries()) {
235026
+ startCsharpStep(projectFiles.length > 1 ? `Loading project ${pi + 1}/${projectFiles.length}: ${path63.basename(projectFile)}\u2026` : `Loading project ${path63.basename(projectFile)}\u2026`);
234353
235027
  let wsViolations;
234354
235028
  try {
234355
235029
  wsViolations = await runRoslynWorkspace(projectFile, ruleKeys);
234356
235030
  } catch (err) {
235031
+ tracker?.error("csharp", `Project load failed: ${path63.basename(projectFile)}`);
234357
235032
  if (err instanceof RoslynHostUnavailableError)
234358
235033
  throw err;
234359
- log.warn(`[Pipeline] Project-aware C# rules skipped for ${path63.basename(projectFile)}: ${err.message}. Restore the project (\`dotnet restore\`) for full C# coverage.`);
234360
- continue;
235034
+ throw new Error(`Project-aware C# analysis failed for ${path63.basename(projectFile)}: ${err.message}. Restore the project (\`dotnet restore\`) and ensure its pinned .NET SDK is installed.`);
234361
235035
  }
234362
235036
  for (const v of wsViolations) {
234363
235037
  const rule = ruleByKey.get(v.ruleKey);
@@ -234389,6 +235063,10 @@ async function runViolationPipeline(input) {
234389
235063
  }
234390
235064
  }
234391
235065
  }
235066
+ if (csharpStepActive) {
235067
+ const csharpFindings = allCodeViolations.length - detViolationCount;
235068
+ tracker?.done("csharp", csharpFindings > 0 ? `${csharpFindings} violations` : "Clean");
235069
+ }
234392
235070
  log.info(`[Pipeline] Code scan: ${allCodeViolations.length} violations from ${filesToScan.length} files (${enabledCodeRules.length} det rules, ${enabledLlmCodeRules.length} LLM rules)`);
234393
235071
  if (enabledCodeRules.some((r) => r.key === "bugs/deterministic/invalid-pyproject-toml")) {
234394
235072
  const pyprojectPath = path63.join(repoPath, "pyproject.toml");
@@ -234434,10 +235112,6 @@ async function runViolationPipeline(input) {
234434
235112
  }
234435
235113
  const totalDet = [...violationsByDomain.values()].reduce((a, b) => a + b, 0);
234436
235114
  log.info(`[Pipeline] Totals: ${DOMAIN_ORDER.filter((d) => violationsByDomain.has(d)).map((d) => `${d}=${violationsByDomain.get(d)}`).join(", ")} (${totalDet})`);
234437
- for (const domain of DOMAIN_ORDER) {
234438
- const count = violationsByDomain.get(domain) ?? 0;
234439
- tracker?.done(domain, count > 0 ? `${count} violations` : "Clean");
234440
- }
234441
235115
  onProgress?.({ step: "analyzing", percent: 84, detail: "Code checks done" });
234442
235116
  const prevLlmCodeByFile = /* @__PURE__ */ new Map();
234443
235117
  for (const cv of previousActiveCodeViolations) {