Your rule only has to implement one method: check().
Everything else (suppression handling, wildcard “*”, conversion to RuleResult) is done for you by RuleCommon.execute().
The Flow object gives you three main collections (already parsed and typed):
Common filters you will use:
Compiler.traverseFlow(flow, startName, callback, optionalEndName) walks the flow exactly like the runtime does (iterative DFS, respects fault connectors, loop “noMoreValuesConnector”, etc.).
Most complex rules (fault paths, unconnected elements, DML-in-loop, etc.) use this helper.
new Compiler().traverseFlow(
flow, // your Flow instance
flow.startReference, // name of the start element (or startElementReference)
(element: FlowNode) => {
// callback executed on every reachable element
// your logic here – element is a FlowNode with .name, .subtype, .element, .connectors
},
optionalEndName // (optional) stop traversal when this name is reached (used for loops)
);
import * as core from "../internals/internals";
import { RuleCommon } from "../models/RuleCommon";
import { IRuleDefinition } from "../interfaces/IRuleDefinition";
export class HardcodedReferences extends RuleCommon implements IRuleDefinition {
constructor() {
super({
name: "HardcodedReferences",
label: "Hard-coded Record References",
description:
"Detects Get Records or other elements that use hard-coded Ids instead of variables.",
supportedTypes: core.FlowType.allTypes(),
docRefs: [],
isConfigurable: false,
autoFixable: false,
});
}
protected check(
flow: core.Flow,
_options: object | undefined,
_suppressions: Set<string>
): core.Violation[] {
const violations: core.Violation[] = [];
const lookups = flow.elements?.filter((e) => e.subtype === "recordLookups") ?? [];
for (const node of lookups) {
const filterLogic = node.element.filterLogic;
const conditions = node.element.conditions ?? node.element.objectConditions;
// naive check – real rule would parse the condition properly
if (JSON.stringify(conditions).match(/[a-zA-Z0-9]{15}|[a-zA-Z0-9]{18}/)) {
violations.push(new core.Violation(node));
}
}
return violations; // suppression handled automatically by base class
}
}