Skip to content

Commit c6551f2

Browse files
authored
Merge branch 'dev' into ng-bulk-suppressions-core
2 parents 33d5592 + 0371bd4 commit c6551f2

17 files changed

Lines changed: 3840 additions & 20 deletions

package-lock.json

Lines changed: 1724 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import eslint from '@eslint/js';
2+
import tseslint from 'typescript-eslint';
3+
4+
export default tseslint.config(
5+
eslint.configs.recommended,
6+
...tseslint.configs.recommended,
7+
{
8+
rules: {
9+
"@typescript-eslint/no-unused-vars": ["error", {
10+
"argsIgnorePattern": "^_",
11+
"varsIgnorePattern": "^_",
12+
"caughtErrorsIgnorePattern": "^_"
13+
}]
14+
}
15+
}
16+
);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"name": "@salesforce/code-analyzer-apexguru-engine",
3+
"description": "ApexGuru Engine Package for the Salesforce Code Analyzer",
4+
"version": "0.36.0-SNAPSHOT",
5+
"author": "The Salesforce Code Analyzer Team",
6+
"license": "BSD-3-Clause",
7+
"homepage": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/overview",
8+
"repository": {
9+
"type": "git",
10+
"url": "git+https://github.com/forcedotcom/code-analyzer-core.git",
11+
"directory": "packages/code-analyzer-apexguru-engine"
12+
},
13+
"main": "dist/index.js",
14+
"types": "dist/index.d.ts",
15+
"dependencies": {
16+
"@salesforce/code-analyzer-engine-api": "0.36.0",
17+
"@salesforce/core": "^8.0.0"
18+
},
19+
"devDependencies": {
20+
"@eslint/js": "^9.39.2",
21+
"@types/jest": "^30.0.0",
22+
"@types/node": "^20.0.0",
23+
"eslint": "^9.39.2",
24+
"jest": "^30.3.0",
25+
"rimraf": "^6.1.3",
26+
"ts-jest": "^29.4.6",
27+
"typescript": "^5.9.3",
28+
"typescript-eslint": "^8.57.1"
29+
},
30+
"engines": {
31+
"node": ">=20.0.0"
32+
},
33+
"files": [
34+
"dist",
35+
"LICENSE",
36+
"package.json"
37+
],
38+
"scripts": {
39+
"build": "tsc --build tsconfig.build.json --verbose",
40+
"test": "tsc --build tsconfig.json && jest --coverage",
41+
"lint": "eslint src/**/*.ts",
42+
"package": "npm pack",
43+
"all": "npm run build && npm run lint && npm run test && npm run package",
44+
"clean": "tsc --build tsconfig.build.json --clean",
45+
"postclean": "rimraf dist && rimraf coverage && rimraf ./*.tgz",
46+
"scrub": "npm run clean && rimraf node_modules",
47+
"showcoverage": "open ./coverage/lcov-report/index.html"
48+
},
49+
"jest": {
50+
"testTimeout": 60000,
51+
"preset": "ts-jest",
52+
"testEnvironment": "node",
53+
"testMatch": [
54+
"**/*.test.ts"
55+
],
56+
"testPathIgnorePatterns": [
57+
"/node_modules/",
58+
"/dist/"
59+
],
60+
"collectCoverageFrom": [
61+
"src/**/*.ts",
62+
"!src/index.ts"
63+
]
64+
}
65+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
2+
3+
import { RuleDescription, SeverityLevel, COMMON_TAGS } from '@salesforce/code-analyzer-engine-api';
4+
5+
/**
6+
* Known ApexGuru rules with descriptions and metadata.
7+
*
8+
* This list should be updated when Salesforce adds new ApexGuru rules.
9+
* Violations for rules NOT in this list will be mapped to the fallback "apexguru-other" rule.
10+
*/
11+
export const APEXGURU_RULES: RuleDescription[] = [
12+
// =================================================================================================================
13+
// PERFORMANCE RULES - HIGH SEVERITY (CRITICAL - RECOMMENDED)
14+
// =================================================================================================================
15+
16+
{
17+
name: 'SoqlInALoop',
18+
severityLevel: SeverityLevel.High,
19+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
20+
description: 'SOQL query inside a loop causes performance issues and can hit governor limits',
21+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop.htm&type=5']
22+
},
23+
24+
{
25+
name: 'DmlInALoop',
26+
severityLevel: SeverityLevel.High,
27+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
28+
description: 'DML statement inside a loop causes performance issues and can hit governor limits',
29+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_dml_in_loop.htm&type=5']
30+
},
31+
32+
// =================================================================================================================
33+
// PERFORMANCE RULES - MODERATE SEVERITY (CRITICAL - RECOMMENDED)
34+
// =================================================================================================================
35+
36+
{
37+
name: 'SoqlWithoutAWhereClauseOrLimitStatement',
38+
severityLevel: SeverityLevel.Moderate,
39+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
40+
description: 'SOQL query without WHERE clause or LIMIT statement can cause performance issues and heap size exceptions',
41+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_where_clause_or_limit_statement.htm&type=5']
42+
},
43+
44+
{
45+
name: 'SoqlWithWildcardFilter',
46+
severityLevel: SeverityLevel.Moderate,
47+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
48+
description: 'SOQL query using LIKE with leading wildcard is inefficient and cannot use indexes',
49+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_wildcard_filter.htm&type=5']
50+
},
51+
52+
{
53+
name: 'SchemaGetGlobalDescribeNotEfficient',
54+
severityLevel: SeverityLevel.Moderate,
55+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
56+
description: 'Using Schema.getGlobalDescribe() causes unnecessary overhead and decreases performance',
57+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_schema_getglobaldescribe_not_efficient.htm&type=5']
58+
},
59+
60+
// =================================================================================================================
61+
// PERFORMANCE RULES - MODERATE SEVERITY (PERFORMANCE ONLY - NOT RECOMMENDED)
62+
// =================================================================================================================
63+
64+
{
65+
name: 'Soql Aggregation',
66+
severityLevel: SeverityLevel.Moderate,
67+
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
68+
description: 'Manual aggregation in Apex instead of using SOQL aggregate functions causes performance issues',
69+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_aggregating_in_apex.htm&type=5']
70+
},
71+
72+
{
73+
name: 'SoqlWithApexFilter',
74+
severityLevel: SeverityLevel.Moderate,
75+
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
76+
description: 'Filtering records in Apex instead of using SOQL WHERE clause causes performance issues',
77+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_apex_filter.htm&type=5']
78+
},
79+
80+
{
81+
name: 'CopyingListOrSetElementsUsingAForLoop',
82+
severityLevel: SeverityLevel.Moderate,
83+
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
84+
description: 'Copying list or set elements using a for loop is inefficient - use addAll() instead',
85+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_copying_elements_with_for_loop.htm&type=5']
86+
},
87+
88+
{
89+
name: 'Redundant Soql',
90+
severityLevel: SeverityLevel.Moderate,
91+
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
92+
description: 'Multiple identical SOQL queries cause unnecessary database round trips',
93+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_redundant_soql.htm&type=5']
94+
},
95+
96+
{
97+
name: 'SoqlWithNegativeExpressions',
98+
severityLevel: SeverityLevel.Moderate,
99+
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
100+
description: 'SOQL queries using negative expressions (NOT IN, !=) don\'t use indexes and cause full table scans',
101+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_negative_expressions.htm&type=5']
102+
},
103+
104+
{
105+
name: 'SObjectMapInAForLoop',
106+
severityLevel: SeverityLevel.Moderate,
107+
tags: [COMMON_TAGS.CATEGORIES.PERFORMANCE, COMMON_TAGS.LANGUAGES.APEX],
108+
description: 'Building Map<Id, SObject> using .put() in a for loop is inefficient - use map constructor or putAll()',
109+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sobject_map_in_for_loop.htm&type=5']
110+
},
111+
112+
// =================================================================================================================
113+
// BEST PRACTICES - LOW SEVERITY (RECOMMENDED)
114+
// =================================================================================================================
115+
116+
{
117+
name: 'UsingTheTestMethodKeyword',
118+
severityLevel: SeverityLevel.Low,
119+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
120+
description: 'The testMethod keyword is deprecated - use @isTest annotation instead',
121+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_using_testmethod.htm&type=5']
122+
},
123+
124+
// =================================================================================================================
125+
// BEST PRACTICES - LOW SEVERITY (NOT RECOMMENDED)
126+
// =================================================================================================================
127+
128+
{
129+
name: 'SortingInApex',
130+
severityLevel: SeverityLevel.Low,
131+
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
132+
description: 'Sorting records in Apex wastes CPU time and can exceed governor limits - use ORDER BY in SOQL',
133+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sorting_in_apex.htm&type=5']
134+
},
135+
136+
{
137+
name: 'BusyLoopDelay',
138+
severityLevel: SeverityLevel.Low,
139+
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
140+
description: 'Using empty loops to delay execution wastes CPU time - use System.enqueueJob with delay parameter',
141+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_busy_loop_delay.htm&type=5']
142+
},
143+
144+
{
145+
name: 'SoqlWithUnusedFields',
146+
severityLevel: SeverityLevel.Low,
147+
tags: [COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
148+
description: 'SOQL query selecting unused fields increases resource consumption unnecessarily',
149+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_unused_fields.htm&type=5']
150+
},
151+
152+
// =================================================================================================================
153+
// FALLBACK RULE
154+
// =================================================================================================================
155+
156+
{
157+
name: 'apexguru-other',
158+
severityLevel: SeverityLevel.Moderate,
159+
tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.BEST_PRACTICES, COMMON_TAGS.LANGUAGES.APEX],
160+
description: 'Other ApexGuru rules - covers new rules added by Salesforce that are not yet explicitly declared',
161+
resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru.htm']
162+
}
163+
];
164+
165+
/**
166+
* Helper to check if a rule name is known
167+
*/
168+
export function isKnownRule(ruleName: string): boolean {
169+
return APEXGURU_RULES.some(rule => rule.name === ruleName);
170+
}
171+
172+
/**
173+
* Fallback rule name for unknown violations
174+
*/
175+
export const FALLBACK_RULE_NAME = 'apexguru-other';
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { ConfigDescription, ConfigValueExtractor } from '@salesforce/code-analyzer-engine-api';
2+
3+
/**
4+
* Configuration for ApexGuru Engine
5+
* Currently minimal - authentication is handled via SF CLI
6+
*/
7+
export type ApexGuruEngineConfig = {
8+
/**
9+
* Maximum time to wait for ApexGuru API response (in milliseconds)
10+
* Default: 120000 (2 minutes)
11+
*/
12+
api_timeout_ms: number;
13+
14+
/**
15+
* Initial retry delay for polling (in milliseconds)
16+
* Default: 2000 (2 seconds)
17+
*/
18+
api_initial_retry_ms: number;
19+
20+
/**
21+
* Maximum retry delay for polling (in milliseconds)
22+
* Exponential backoff will not exceed this value
23+
* Default: 60000 (60 seconds)
24+
*/
25+
api_max_retry_ms: number;
26+
27+
/**
28+
* Backoff multiplier for exponential backoff polling
29+
* Each retry delay is multiplied by this value (e.g., 2x = 2s, 4s, 8s, 16s...)
30+
* Default: 2
31+
*/
32+
api_backoff_multiplier: number;
33+
};
34+
35+
/**
36+
* Default configuration values
37+
*/
38+
export const DEFAULT_APEXGURU_ENGINE_CONFIG: ApexGuruEngineConfig = {
39+
api_timeout_ms: 120000, // 2 minutes
40+
api_initial_retry_ms: 2000, // 2 seconds
41+
api_max_retry_ms: 60000, // 60 seconds
42+
api_backoff_multiplier: 2 // 2x exponential backoff
43+
};
44+
45+
/**
46+
* Configuration schema description for ApexGuru Engine
47+
*/
48+
export const APEXGURU_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = {
49+
overview: 'Configuration for ApexGuru Engine. Authentication is handled via Salesforce CLI (sf org login web).',
50+
fieldDescriptions: {
51+
api_timeout_ms: {
52+
descriptionText: 'Maximum time to wait for ApexGuru API response (in milliseconds). Default: 120000 (2 minutes)',
53+
valueType: 'number',
54+
defaultValue: 120000
55+
},
56+
api_initial_retry_ms: {
57+
descriptionText: 'Initial retry delay for polling ApexGuru API (in milliseconds). Default: 2000 (2 seconds)',
58+
valueType: 'number',
59+
defaultValue: 2000
60+
},
61+
api_max_retry_ms: {
62+
descriptionText: 'Maximum retry delay for polling (in milliseconds). Exponential backoff will not exceed this value. Default: 60000 (60 seconds)',
63+
valueType: 'number',
64+
defaultValue: 60000
65+
},
66+
api_backoff_multiplier: {
67+
descriptionText: 'Backoff multiplier for exponential backoff polling. Each retry delay is multiplied by this value. Default: 2',
68+
valueType: 'number',
69+
defaultValue: 2
70+
}
71+
}
72+
};
73+
74+
/**
75+
* Validates and normalizes ApexGuru engine configuration
76+
*/
77+
export async function validateAndNormalizeConfig(
78+
configValueExtractor: ConfigValueExtractor
79+
): Promise<ApexGuruEngineConfig> {
80+
// Validate only expected keys are present
81+
configValueExtractor.validateContainsOnlySpecifiedKeys([
82+
'api_timeout_ms',
83+
'api_initial_retry_ms',
84+
'api_max_retry_ms',
85+
'api_backoff_multiplier'
86+
]);
87+
88+
// Extract and validate timeout
89+
const apiTimeoutMs: number = configValueExtractor.extractNumber(
90+
'api_timeout_ms',
91+
DEFAULT_APEXGURU_ENGINE_CONFIG.api_timeout_ms
92+
) ?? DEFAULT_APEXGURU_ENGINE_CONFIG.api_timeout_ms;
93+
94+
if (apiTimeoutMs <= 0) {
95+
throw new Error('api_timeout_ms must be greater than 0');
96+
}
97+
98+
// Extract and validate initial retry delay
99+
const apiInitialRetryMs: number = configValueExtractor.extractNumber(
100+
'api_initial_retry_ms',
101+
DEFAULT_APEXGURU_ENGINE_CONFIG.api_initial_retry_ms
102+
) ?? DEFAULT_APEXGURU_ENGINE_CONFIG.api_initial_retry_ms;
103+
104+
if (apiInitialRetryMs <= 0) {
105+
throw new Error('api_initial_retry_ms must be greater than 0');
106+
}
107+
108+
// Extract and validate max retry delay
109+
const apiMaxRetryMs: number = configValueExtractor.extractNumber(
110+
'api_max_retry_ms',
111+
DEFAULT_APEXGURU_ENGINE_CONFIG.api_max_retry_ms
112+
) ?? DEFAULT_APEXGURU_ENGINE_CONFIG.api_max_retry_ms;
113+
114+
if (apiMaxRetryMs <= 0) {
115+
throw new Error('api_max_retry_ms must be greater than 0');
116+
}
117+
118+
if (apiMaxRetryMs < apiInitialRetryMs) {
119+
throw new Error('api_max_retry_ms must be greater than or equal to api_initial_retry_ms');
120+
}
121+
122+
// Extract and validate backoff multiplier
123+
const apiBackoffMultiplier: number = configValueExtractor.extractNumber(
124+
'api_backoff_multiplier',
125+
DEFAULT_APEXGURU_ENGINE_CONFIG.api_backoff_multiplier
126+
) ?? DEFAULT_APEXGURU_ENGINE_CONFIG.api_backoff_multiplier;
127+
128+
if (apiBackoffMultiplier < 1) {
129+
throw new Error('api_backoff_multiplier must be greater than or equal to 1');
130+
}
131+
132+
return {
133+
api_timeout_ms: apiTimeoutMs,
134+
api_initial_retry_ms: apiInitialRetryMs,
135+
api_max_retry_ms: apiMaxRetryMs,
136+
api_backoff_multiplier: apiBackoffMultiplier
137+
};
138+
}

0 commit comments

Comments
 (0)