@sarj/eslint-plugin 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,7 +31,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  default: () => index_default,
34
- rules: () => rules
34
+ recommendedRules: () => recommendedRules,
35
+ rules: () => rules,
36
+ strictRules: () => strictRules
35
37
  });
36
38
  module.exports = __toCommonJS(index_exports);
37
39
 
@@ -41,7 +43,8 @@ var import_utils = require("@typescript-eslint/utils");
41
43
  // src/rules/_paths.ts
42
44
  var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
43
45
  var STORY_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
44
- var GENERATED_FILE_RE = /([\\/](?:generated|openapi-gen|graphql[\\/]types)[\\/])|(\.gen\.[cm]?[jt]sx?$)|(\.generated\.[cm]?[jt]sx?$)|(\.d\.[cm]?ts$)|(\.types\.[cm]?ts$)/;
46
+ var STORY_DIR_RE = /(^|\/)stories(?:[_-][^/]*)?\//i;
47
+ var GENERATED_FILE_RE = /([\\/](?:generated|openapi-gen|graphql[\\/]types|vendor|vendored|external|third[-_]?party)[\\/])|(\.gen\.[cm]?[jt]sx?$)|(\.generated\.[cm]?[jt]sx?$)|(\.d\.[cm]?ts$)|(\.types\.[cm]?ts$)/;
45
48
  var GENERATED_MARKER_RE = /(?:@generated\b|generated (?:with|by)|generated (?:graphql )?types|do not edit(?: directly| manually)?)/i;
46
49
  function isTestFile(filename) {
47
50
  const normalized = filename.replaceAll("\\", "/");
@@ -49,10 +52,13 @@ function isTestFile(filename) {
49
52
  if (/[.\-_](test|spec|e2e)\.[cm]?[jt]sx?$/.test(base) || /\.integration\.[cm]?[jt]sx?$/.test(base)) {
50
53
  return true;
51
54
  }
52
- return /(^|\/)(tests?|__tests__|__mocks__|fixtures|e2e|integration)\//.test(normalized);
55
+ return /(^|\/)(tests?|__tests__|__mocks__|__fixtures__|__testfixtures__|fixtures?|e2e|integration)\//.test(
56
+ normalized
57
+ );
53
58
  }
54
59
  function isStoryFile(filename) {
55
- return STORY_FILE_RE.test(filename);
60
+ const normalized = filename.replaceAll("\\", "/");
61
+ return STORY_FILE_RE.test(normalized) || STORY_DIR_RE.test(normalized);
56
62
  }
57
63
  function isGeneratedFile(filename, sourceText = "") {
58
64
  return GENERATED_FILE_RE.test(filename.replaceAll("\\", "/")) || GENERATED_MARKER_RE.test(sourceText.slice(0, 2048));
@@ -7689,8 +7695,190 @@ var trailing_value_narration_default = import_utils48.ESLintUtils.RuleCreator(
7689
7695
  }
7690
7696
  });
7691
7697
 
7692
- // src/rules/no-tautological-expect.ts
7698
+ // src/rules/no-type-member-comment-wall.ts
7693
7699
  var import_utils49 = require("@typescript-eslint/utils");
7700
+ var DEFAULTS = {
7701
+ // Below three rows "a wall" is not a fair description of what the reader sees.
7702
+ minCommentedMembers: 3,
7703
+ // A minority of commented members is a GROUP LABEL, not a wall.
7704
+ minCommentedRatio: 0.6,
7705
+ // Room for one substantive row in four; a type where a quarter of the comments
7706
+ // say something real is a type someone was documenting, not decorating.
7707
+ minRestatedRatio: 0.75,
7708
+ // One word beyond the member's own text. Zero is `jsdoc-restates-signature`'s
7709
+ // test and is already covered there; two admits definitions ("Partial match"
7710
+ // beside "Exact match"), which the evidence file counts.
7711
+ maxNovelWords: 1
7712
+ };
7713
+ var VALUE_TAG_RE = /@(?:deprecated|see|example|throws|remarks|since|default|defaultvalue|link|internal|alpha|beta|experimental|template|typeparam|inheritdoc|todo|fixme|override)\b/i;
7714
+ var DEFAULT_RE = /^\s*@?default\b|\bdefaults? (?:to|:)/i;
7715
+ var DIGIT_RE = /\d/;
7716
+ var UNIT_WORD_RE = /\b(?:ms|milliseconds?|seconds?|minutes?|hours?|days?|weeks?|months?|years?|bytes?|kb|mb|gb|percent|pixels?|px|utc|epoch)\b/i;
7717
+ var EXAMPLE_RE = /["'`]|\be\.g\.|\bi\.e\./;
7718
+ var BANNER_RE = /[=\-─-╿*#~_.]{3,}/;
7719
+ var NON_ASCII_LETTER_RE2 = /[^\p{ASCII}\p{N}\p{P}\p{Z}]/u;
7720
+ var STOPWORDS4 = new Set(
7721
+ `the a an of to for in on with and or as at by is are was be been being
7722
+ this that it its if whether when where which what will would can could should
7723
+ must may into from over about not no does do done has have had used use uses
7724
+ using given provided specified current new existing all any each per via
7725
+ instance object value values data item
7726
+ items element callback handler prop props param arg return
7727
+ returns returning result optional required true false null undefined
7728
+ string number boolean array list promise set map record type name`.split(/\s+/)
7729
+ );
7730
+ var WORD_RE4 = /[A-Za-z][A-Za-z0-9]*/g;
7731
+ var BARE_LABEL_RE = /^[A-Za-z][A-Za-z0-9]*$/;
7732
+ function labelStems(body) {
7733
+ return splitIdentifier(body).map(stem).join(" ");
7734
+ }
7735
+ function isNamedMember(node) {
7736
+ return (node.type === import_utils49.AST_NODE_TYPES.TSPropertySignature || node.type === import_utils49.AST_NODE_TYPES.TSMethodSignature) && !node.computed;
7737
+ }
7738
+ function commentBody(comment) {
7739
+ return comment.value.replace(/^\*+/, "").replace(/^[ \t]*\*[ \t]?/gm, "").trim();
7740
+ }
7741
+ function carriesValue(body) {
7742
+ return isProtected(body) || VALUE_TAG_RE.test(body) || DEFAULT_RE.test(body) || DIGIT_RE.test(body) || UNIT_WORD_RE.test(body) || EXAMPLE_RE.test(body) || BANNER_RE.test(body) || NON_ASCII_LETTER_RE2.test(body);
7743
+ }
7744
+ function knownTokens(source) {
7745
+ const tokens = /* @__PURE__ */ new Set();
7746
+ for (const identifier of source.match(/[A-Za-z_$][\w$]*/g) ?? []) {
7747
+ for (const part of splitIdentifier(identifier)) {
7748
+ tokens.add(part);
7749
+ tokens.add(stem(part));
7750
+ }
7751
+ }
7752
+ return tokens;
7753
+ }
7754
+ function novelWords(body, known) {
7755
+ let novel = 0;
7756
+ for (const word of body.match(WORD_RE4) ?? []) {
7757
+ const lower = word.toLowerCase();
7758
+ if (lower.length < 2 || STOPWORDS4.has(lower)) continue;
7759
+ if (!known.has(lower) && !known.has(stem(lower))) novel += 1;
7760
+ }
7761
+ return novel;
7762
+ }
7763
+ var no_type_member_comment_wall_default = import_utils49.ESLintUtils.RuleCreator(
7764
+ (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7765
+ )({
7766
+ name: "no-type-member-comment-wall",
7767
+ meta: {
7768
+ type: "suggestion",
7769
+ docs: {
7770
+ description: "Flag an object type whose member comments mostly re-spell the members' own names and types."
7771
+ },
7772
+ schema: [
7773
+ {
7774
+ type: "object",
7775
+ additionalProperties: false,
7776
+ properties: {
7777
+ minCommentedMembers: {
7778
+ type: "integer",
7779
+ minimum: 2,
7780
+ description: "Fewest commented members that can count as a wall."
7781
+ },
7782
+ minCommentedRatio: {
7783
+ type: "number",
7784
+ minimum: 0,
7785
+ maximum: 1,
7786
+ description: "Least share of the type's members that must be commented; below it the comments are group labels."
7787
+ },
7788
+ minRestatedRatio: {
7789
+ type: "number",
7790
+ minimum: 0,
7791
+ maximum: 1,
7792
+ description: "Least share of the member comments that must be restatements."
7793
+ },
7794
+ maxNovelWords: {
7795
+ type: "integer",
7796
+ minimum: 0,
7797
+ description: "Most content words a comment may add beyond its member's own source and still count as a restatement."
7798
+ }
7799
+ }
7800
+ }
7801
+ ],
7802
+ messages: {
7803
+ commentWall: "{{restated}} of this type's {{commented}} member comments only re-spell the member's own name and type \u2014 delete them, and keep the rows that say what the name cannot."
7804
+ }
7805
+ },
7806
+ defaultOptions: [DEFAULTS],
7807
+ create(context, [provided]) {
7808
+ const options = { ...DEFAULTS, ...provided };
7809
+ const sourceCode = context.sourceCode;
7810
+ if (isGeneratedFile(context.filename, sourceCode.text) || isTestFile(context.filename) || isStoryFile(context.filename)) {
7811
+ return {};
7812
+ }
7813
+ const endingOn = /* @__PURE__ */ new Map();
7814
+ const startingOn = /* @__PURE__ */ new Map();
7815
+ for (const comment of sourceCode.getAllComments()) {
7816
+ endingOn.set(comment.loc.end.line, comment);
7817
+ if (!startingOn.has(comment.loc.start.line)) startingOn.set(comment.loc.start.line, comment);
7818
+ }
7819
+ function documentingComment(member) {
7820
+ const beforeMember = sourceCode.getTokenBefore(member, { includeComments: false });
7821
+ const ownsItsLine = beforeMember === null || beforeMember.loc.end.line < member.loc.start.line;
7822
+ const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
7823
+ if (lead !== void 0) {
7824
+ const before = sourceCode.getTokenBefore(lead, { includeComments: false });
7825
+ if (before === null || before.loc.end.line < lead.loc.start.line) return lead;
7826
+ }
7827
+ const trail = startingOn.get(member.loc.end.line);
7828
+ return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
7829
+ }
7830
+ function isGroupLabel(comment, member, headsRun) {
7831
+ if (comment.loc.end.line >= member.loc.start.line) return false;
7832
+ const body = commentBody(comment);
7833
+ if (!BARE_LABEL_RE.test(body)) return false;
7834
+ if (labelStems(body) === labelStems(sourceCode.getText(member.key))) return false;
7835
+ const lineAbove = sourceCode.lines[comment.loc.start.line - 2];
7836
+ return headsRun || lineAbove !== void 0 && lineAbove.trim().length === 0;
7837
+ }
7838
+ function check(node) {
7839
+ const members = node.type === import_utils49.AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members;
7840
+ const named = members.filter(isNamedMember);
7841
+ if (named.length === 0) return;
7842
+ const documented = named.map((member) => ({ member, comment: documentingComment(member) }));
7843
+ let commented = 0;
7844
+ let restated = 0;
7845
+ const claimed = /* @__PURE__ */ new Set();
7846
+ for (const [index, { member, comment }] of documented.entries()) {
7847
+ if (comment === void 0 || claimed.has(comment)) continue;
7848
+ const next = documented[index + 1];
7849
+ if (isGroupLabel(comment, member, next !== void 0 && next.comment === void 0)) {
7850
+ continue;
7851
+ }
7852
+ claimed.add(comment);
7853
+ commented += 1;
7854
+ const body = commentBody(comment);
7855
+ if (body.length === 0 || carriesValue(body)) continue;
7856
+ if (novelWords(body, knownTokens(sourceCode.getText(member))) <= options.maxNovelWords) {
7857
+ restated += 1;
7858
+ }
7859
+ }
7860
+ if (commented < options.minCommentedMembers || commented / named.length < options.minCommentedRatio || restated / commented < options.minRestatedRatio) {
7861
+ return;
7862
+ }
7863
+ context.report({
7864
+ node,
7865
+ loc: {
7866
+ start: node.loc.start,
7867
+ end: { line: node.loc.start.line, column: node.loc.start.column + 1 }
7868
+ },
7869
+ messageId: "commentWall",
7870
+ data: { restated: String(restated), commented: String(commented) }
7871
+ });
7872
+ }
7873
+ return {
7874
+ TSInterfaceBody: check,
7875
+ TSTypeLiteral: check
7876
+ };
7877
+ }
7878
+ });
7879
+
7880
+ // src/rules/no-tautological-expect.ts
7881
+ var import_utils50 = require("@typescript-eslint/utils");
7694
7882
  var EQUALITY_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
7695
7883
  var ZERO_ARG_MATCHERS = /* @__PURE__ */ new Set([
7696
7884
  "toBeDefined",
@@ -7704,17 +7892,17 @@ var OPERAND_PREVIEW_CHARS = 40;
7704
7892
  var NUMERIC_SIGNS = /* @__PURE__ */ new Set(["-", "+"]);
7705
7893
  function isLiteral(node) {
7706
7894
  switch (node.type) {
7707
- case import_utils49.AST_NODE_TYPES.Literal:
7895
+ case import_utils50.AST_NODE_TYPES.Literal:
7708
7896
  return true;
7709
- case import_utils49.AST_NODE_TYPES.TemplateLiteral:
7897
+ case import_utils50.AST_NODE_TYPES.TemplateLiteral:
7710
7898
  return node.expressions.length === 0;
7711
- case import_utils49.AST_NODE_TYPES.UnaryExpression:
7899
+ case import_utils50.AST_NODE_TYPES.UnaryExpression:
7712
7900
  return NUMERIC_SIGNS.has(node.operator) && isLiteral(node.argument);
7713
- case import_utils49.AST_NODE_TYPES.ArrayExpression:
7901
+ case import_utils50.AST_NODE_TYPES.ArrayExpression:
7714
7902
  return node.elements.every((element) => element !== null && isLiteral(element));
7715
- case import_utils49.AST_NODE_TYPES.ObjectExpression:
7903
+ case import_utils50.AST_NODE_TYPES.ObjectExpression:
7716
7904
  return node.properties.every(
7717
- (property) => property.type === import_utils49.AST_NODE_TYPES.Property && !property.computed && isLiteral(property.value)
7905
+ (property) => property.type === import_utils50.AST_NODE_TYPES.Property && !property.computed && isLiteral(property.value)
7718
7906
  );
7719
7907
  default:
7720
7908
  return false;
@@ -7722,12 +7910,12 @@ function isLiteral(node) {
7722
7910
  }
7723
7911
  function expectOperand(callee) {
7724
7912
  const receiver = callee.object;
7725
- if (receiver.type !== import_utils49.AST_NODE_TYPES.CallExpression || receiver.callee.type !== import_utils49.AST_NODE_TYPES.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
7913
+ if (receiver.type !== import_utils50.AST_NODE_TYPES.CallExpression || receiver.callee.type !== import_utils50.AST_NODE_TYPES.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
7726
7914
  return null;
7727
7915
  }
7728
7916
  return receiver.arguments[0] ?? null;
7729
7917
  }
7730
- var no_tautological_expect_default = import_utils49.ESLintUtils.RuleCreator(
7918
+ var no_tautological_expect_default = import_utils50.ESLintUtils.RuleCreator(
7731
7919
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7732
7920
  )({
7733
7921
  name: "no-tautological-expect",
@@ -7754,10 +7942,10 @@ var no_tautological_expect_default = import_utils49.ESLintUtils.RuleCreator(
7754
7942
  return {
7755
7943
  CallExpression(node) {
7756
7944
  const callee = node.callee;
7757
- if (callee.type !== import_utils49.AST_NODE_TYPES.MemberExpression || callee.computed) {
7945
+ if (callee.type !== import_utils50.AST_NODE_TYPES.MemberExpression || callee.computed) {
7758
7946
  return;
7759
7947
  }
7760
- if (callee.property.type !== import_utils49.AST_NODE_TYPES.Identifier) {
7948
+ if (callee.property.type !== import_utils50.AST_NODE_TYPES.Identifier) {
7761
7949
  return;
7762
7950
  }
7763
7951
  const matcher = callee.property.name;
@@ -7791,26 +7979,26 @@ var no_tautological_expect_default = import_utils49.ESLintUtils.RuleCreator(
7791
7979
  });
7792
7980
 
7793
7981
  // src/rules/require-interface-for-injected-service.ts
7794
- var import_utils50 = require("@typescript-eslint/utils");
7982
+ var import_utils51 = require("@typescript-eslint/utils");
7795
7983
  var CONFIGISH_TYPE_RE = /(?:Options|Opts|Config|Configuration|Settings|Params|Props|Args|Env|Environment|Callbacks|Flags)$/;
7796
7984
  var CONFIGISH_NAME_RE = /^(?:options|opts|config|configuration|settings|params|props|args|env|environment|callbacks|flags|logger|log|clock)$/i;
7797
7985
  var HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;
7798
7986
  var TRANSPORT_WRAPPER_NAME_RE = /Client$/;
7799
7987
  var ROUTER_FACTORY_NAME = "Router";
7800
7988
  var FRAMEWORK_HTTP_TYPES = /* @__PURE__ */ new Set(["Request", "Response", "NextFunction"]);
7801
- var isExportedClass = (node) => node.parent.type === import_utils50.AST_NODE_TYPES.ExportNamedDeclaration || node.parent.type === import_utils50.AST_NODE_TYPES.ExportDefaultDeclaration;
7802
- var qualifiedName = (name) => name.type === import_utils50.AST_NODE_TYPES.Identifier ? name.name : name.type === import_utils50.AST_NODE_TYPES.TSQualifiedName ? `${qualifiedName(name.left)}.${name.right.name}` : "";
7989
+ var isExportedClass = (node) => node.parent.type === import_utils51.AST_NODE_TYPES.ExportNamedDeclaration || node.parent.type === import_utils51.AST_NODE_TYPES.ExportDefaultDeclaration;
7990
+ var qualifiedName = (name) => name.type === import_utils51.AST_NODE_TYPES.Identifier ? name.name : name.type === import_utils51.AST_NODE_TYPES.TSQualifiedName ? `${qualifiedName(name.left)}.${name.right.name}` : "";
7803
7991
  var typeReferenceName = (annotated) => {
7804
7992
  let target = annotated;
7805
- if (target.type === import_utils50.AST_NODE_TYPES.TSParameterProperty) target = target.parameter;
7806
- if (target.type === import_utils50.AST_NODE_TYPES.AssignmentPattern) target = target.left;
7807
- if (target.type !== import_utils50.AST_NODE_TYPES.Identifier) return null;
7993
+ if (target.type === import_utils51.AST_NODE_TYPES.TSParameterProperty) target = target.parameter;
7994
+ if (target.type === import_utils51.AST_NODE_TYPES.AssignmentPattern) target = target.left;
7995
+ if (target.type !== import_utils51.AST_NODE_TYPES.Identifier) return null;
7808
7996
  const annotation = target.typeAnnotation?.typeAnnotation;
7809
- if (annotation === void 0 || annotation.type !== import_utils50.AST_NODE_TYPES.TSTypeReference) {
7997
+ if (annotation === void 0 || annotation.type !== import_utils51.AST_NODE_TYPES.TSTypeReference) {
7810
7998
  return null;
7811
7999
  }
7812
8000
  const { typeName } = annotation;
7813
- const rightmost = typeName.type === import_utils50.AST_NODE_TYPES.Identifier ? typeName.name : typeName.type === import_utils50.AST_NODE_TYPES.TSQualifiedName ? typeName.right.name : null;
8001
+ const rightmost = typeName.type === import_utils51.AST_NODE_TYPES.Identifier ? typeName.name : typeName.type === import_utils51.AST_NODE_TYPES.TSQualifiedName ? typeName.right.name : null;
7814
8002
  if (rightmost === null) return null;
7815
8003
  return { name: target.name, typeName: rightmost, display: qualifiedName(typeName) };
7816
8004
  };
@@ -7820,17 +8008,17 @@ var readConstructor = (ctor) => {
7820
8008
  let constructedFields = 0;
7821
8009
  if (body !== null && body !== void 0) {
7822
8010
  for (const statement of body.body) {
7823
- if (statement.type !== import_utils50.AST_NODE_TYPES.ExpressionStatement) continue;
8011
+ if (statement.type !== import_utils51.AST_NODE_TYPES.ExpressionStatement) continue;
7824
8012
  const expression = statement.expression;
7825
- if (expression.type !== import_utils50.AST_NODE_TYPES.AssignmentExpression || expression.operator !== "=" || expression.left.type !== import_utils50.AST_NODE_TYPES.MemberExpression || expression.left.object.type !== import_utils50.AST_NODE_TYPES.ThisExpression) {
8013
+ if (expression.type !== import_utils51.AST_NODE_TYPES.AssignmentExpression || expression.operator !== "=" || expression.left.type !== import_utils51.AST_NODE_TYPES.MemberExpression || expression.left.object.type !== import_utils51.AST_NODE_TYPES.ThisExpression) {
7826
8014
  continue;
7827
8015
  }
7828
8016
  const source = expression.right;
7829
- if (source.type === import_utils50.AST_NODE_TYPES.NewExpression) {
8017
+ if (source.type === import_utils51.AST_NODE_TYPES.NewExpression) {
7830
8018
  constructedFields += 1;
7831
- } else if (source.type === import_utils50.AST_NODE_TYPES.Identifier) {
8019
+ } else if (source.type === import_utils51.AST_NODE_TYPES.Identifier) {
7832
8020
  storedFrom.add(source.name);
7833
- } else if (source.type === import_utils50.AST_NODE_TYPES.MemberExpression && source.object.type === import_utils50.AST_NODE_TYPES.Identifier) {
8021
+ } else if (source.type === import_utils51.AST_NODE_TYPES.MemberExpression && source.object.type === import_utils51.AST_NODE_TYPES.Identifier) {
7834
8022
  storedFrom.add(source.object.name);
7835
8023
  }
7836
8024
  }
@@ -7839,7 +8027,7 @@ var readConstructor = (ctor) => {
7839
8027
  for (const parameter of ctor.value.params) {
7840
8028
  const reference = typeReferenceName(parameter);
7841
8029
  if (reference === null) continue;
7842
- const stored = parameter.type === import_utils50.AST_NODE_TYPES.TSParameterProperty || storedFrom.has(reference.name);
8030
+ const stored = parameter.type === import_utils51.AST_NODE_TYPES.TSParameterProperty || storedFrom.has(reference.name);
7843
8031
  if (!stored) continue;
7844
8032
  if (CONFIGISH_TYPE_RE.test(reference.typeName)) continue;
7845
8033
  if (CONFIGISH_NAME_RE.test(reference.name)) continue;
@@ -7869,19 +8057,19 @@ var subtreeHas = (root, found) => {
7869
8057
  return hit;
7870
8058
  };
7871
8059
  var isFrameworkWiring = (body) => subtreeHas(body, (node) => {
7872
- if (node.type === import_utils50.AST_NODE_TYPES.CallExpression) {
8060
+ if (node.type === import_utils51.AST_NODE_TYPES.CallExpression) {
7873
8061
  const { callee } = node;
7874
- if (callee.type === import_utils50.AST_NODE_TYPES.Identifier) return callee.name === ROUTER_FACTORY_NAME;
7875
- return callee.type === import_utils50.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils50.AST_NODE_TYPES.Identifier && callee.property.name === ROUTER_FACTORY_NAME;
8062
+ if (callee.type === import_utils51.AST_NODE_TYPES.Identifier) return callee.name === ROUTER_FACTORY_NAME;
8063
+ return callee.type === import_utils51.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils51.AST_NODE_TYPES.Identifier && callee.property.name === ROUTER_FACTORY_NAME;
7876
8064
  }
7877
- return node.type === import_utils50.AST_NODE_TYPES.TSTypeReference && node.typeName.type === import_utils50.AST_NODE_TYPES.TSQualifiedName && FRAMEWORK_HTTP_TYPES.has(node.typeName.right.name);
8065
+ return node.type === import_utils51.AST_NODE_TYPES.TSTypeReference && node.typeName.type === import_utils51.AST_NODE_TYPES.TSQualifiedName && FRAMEWORK_HTTP_TYPES.has(node.typeName.right.name);
7878
8066
  });
7879
8067
  var stem2 = (name) => name.replace(/^I(?=[A-Z])/, "").replace(/Impl$/, "");
7880
8068
  var fileInterfaceNames = (program) => {
7881
8069
  const names = [];
7882
8070
  for (const statement of program.body) {
7883
- const declaration = statement.type === import_utils50.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
7884
- if (declaration?.type === import_utils50.AST_NODE_TYPES.TSInterfaceDeclaration) names.push(declaration.id.name);
8071
+ const declaration = statement.type === import_utils51.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
8072
+ if (declaration?.type === import_utils51.AST_NODE_TYPES.TSInterfaceDeclaration) names.push(declaration.id.name);
7885
8073
  }
7886
8074
  return names;
7887
8075
  };
@@ -7899,16 +8087,16 @@ var isTransportWrapper = (className, collaborators, program) => {
7899
8087
  var publicMethodNames = (body) => {
7900
8088
  const names = [];
7901
8089
  for (const member of body.body) {
7902
- if (member.type !== import_utils50.AST_NODE_TYPES.MethodDefinition) continue;
8090
+ if (member.type !== import_utils51.AST_NODE_TYPES.MethodDefinition) continue;
7903
8091
  if (member.kind !== "method" || member.static) continue;
7904
8092
  if (member.accessibility === "private" || member.accessibility === "protected") continue;
7905
- if (member.key.type === import_utils50.AST_NODE_TYPES.PrivateIdentifier) continue;
7906
- if (member.key.type === import_utils50.AST_NODE_TYPES.Identifier) names.push(member.key.name);
8093
+ if (member.key.type === import_utils51.AST_NODE_TYPES.PrivateIdentifier) continue;
8094
+ if (member.key.type === import_utils51.AST_NODE_TYPES.Identifier) names.push(member.key.name);
7907
8095
  else names.push("\u2026");
7908
8096
  }
7909
8097
  return names;
7910
8098
  };
7911
- var require_interface_for_injected_service_default = import_utils50.ESLintUtils.RuleCreator(
8099
+ var require_interface_for_injected_service_default = import_utils51.ESLintUtils.RuleCreator(
7912
8100
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7913
8101
  )({
7914
8102
  name: "require-interface-for-injected-service",
@@ -7936,7 +8124,7 @@ var require_interface_for_injected_service_default = import_utils50.ESLintUtils.
7936
8124
  if (node.implements.length > 0) return;
7937
8125
  if (node.decorators.length > 0) return;
7938
8126
  const ctor = node.body.body.find(
7939
- (member) => member.type === import_utils50.AST_NODE_TYPES.MethodDefinition && member.kind === "constructor"
8127
+ (member) => member.type === import_utils51.AST_NODE_TYPES.MethodDefinition && member.kind === "constructor"
7940
8128
  );
7941
8129
  if (ctor === void 0) return;
7942
8130
  const { collaborators, constructedFields } = readConstructor(ctor);
@@ -7961,26 +8149,26 @@ var require_interface_for_injected_service_default = import_utils50.ESLintUtils.
7961
8149
  });
7962
8150
 
7963
8151
  // src/rules/prefer-non-nullable-collection.ts
7964
- var import_utils51 = require("@typescript-eslint/utils");
8152
+ var import_utils52 = require("@typescript-eslint/utils");
7965
8153
  var ARRAY_TYPE_NAMES = /* @__PURE__ */ new Set(["Array", "ReadonlyArray"]);
7966
8154
  function propertyName(node) {
7967
8155
  const key = node.key;
7968
- if (key.type === import_utils51.AST_NODE_TYPES.Identifier) return key.name;
7969
- if (key.type === import_utils51.AST_NODE_TYPES.Literal) return String(key.value);
8156
+ if (key.type === import_utils52.AST_NODE_TYPES.Identifier) return key.name;
8157
+ if (key.type === import_utils52.AST_NODE_TYPES.Literal) return String(key.value);
7970
8158
  return "collection";
7971
8159
  }
7972
8160
  function isArrayType(node) {
7973
- if (node.type === import_utils51.AST_NODE_TYPES.TSArrayType) return true;
7974
- return node.type === import_utils51.AST_NODE_TYPES.TSTypeReference && node.typeName.type === import_utils51.AST_NODE_TYPES.Identifier && ARRAY_TYPE_NAMES.has(node.typeName.name);
8161
+ if (node.type === import_utils52.AST_NODE_TYPES.TSArrayType) return true;
8162
+ return node.type === import_utils52.AST_NODE_TYPES.TSTypeReference && node.typeName.type === import_utils52.AST_NODE_TYPES.Identifier && ARRAY_TYPE_NAMES.has(node.typeName.name);
7975
8163
  }
7976
8164
  function isNullishType(node) {
7977
- return node.type === import_utils51.AST_NODE_TYPES.TSNullKeyword || node.type === import_utils51.AST_NODE_TYPES.TSUndefinedKeyword;
8165
+ return node.type === import_utils52.AST_NODE_TYPES.TSNullKeyword || node.type === import_utils52.AST_NODE_TYPES.TSUndefinedKeyword;
7978
8166
  }
7979
8167
  function isNullableArrayOnly(node) {
7980
8168
  const values = node.types.filter((member) => !isNullishType(member));
7981
8169
  return values.length > 0 && values.length < node.types.length && values.every(isArrayType);
7982
8170
  }
7983
- var prefer_non_nullable_collection_default = import_utils51.ESLintUtils.RuleCreator(
8171
+ var prefer_non_nullable_collection_default = import_utils52.ESLintUtils.RuleCreator(
7984
8172
  (name) => `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`
7985
8173
  )({
7986
8174
  name: "prefer-non-nullable-collection",
@@ -8003,7 +8191,7 @@ var prefer_non_nullable_collection_default = import_utils51.ESLintUtils.RuleCrea
8003
8191
  function checkOptionalProperty(node) {
8004
8192
  const annotation = node.typeAnnotation?.typeAnnotation;
8005
8193
  if (annotation === void 0) return;
8006
- if (annotation.type !== import_utils51.AST_NODE_TYPES.TSUnionType || !isNullableArrayOnly(annotation)) {
8194
+ if (annotation.type !== import_utils52.AST_NODE_TYPES.TSUnionType || !isNullableArrayOnly(annotation)) {
8007
8195
  return;
8008
8196
  }
8009
8197
  context.report({
@@ -8016,7 +8204,7 @@ var prefer_non_nullable_collection_default = import_utils51.ESLintUtils.RuleCrea
8016
8204
  TSPropertySignature: checkOptionalProperty,
8017
8205
  PropertyDefinition: checkOptionalProperty,
8018
8206
  TSTypeAliasDeclaration(node) {
8019
- if (node.typeAnnotation.type !== import_utils51.AST_NODE_TYPES.TSUnionType) return;
8207
+ if (node.typeAnnotation.type !== import_utils52.AST_NODE_TYPES.TSUnionType) return;
8020
8208
  if (!isNullableArrayOnly(node.typeAnnotation)) return;
8021
8209
  context.report({
8022
8210
  node,
@@ -8029,7 +8217,7 @@ var prefer_non_nullable_collection_default = import_utils51.ESLintUtils.RuleCrea
8029
8217
  });
8030
8218
 
8031
8219
  // src/rules/strict-test-assertions.ts
8032
- var import_utils52 = require("@typescript-eslint/utils");
8220
+ var import_utils53 = require("@typescript-eslint/utils");
8033
8221
  var MERGEABLE_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
8034
8222
  var ARRAY_MATCHERS = /* @__PURE__ */ new Set(["toEqual", "toStrictEqual"]);
8035
8223
  var COLLECTION_PROPERTIES = /* @__PURE__ */ new Set(["length", "size"]);
@@ -8037,11 +8225,11 @@ var NUMERIC_SIGNS2 = /* @__PURE__ */ new Set(["-", "+"]);
8037
8225
  var MIN_RUN_LENGTH = 2;
8038
8226
  function literalText(node, getText) {
8039
8227
  switch (node.type) {
8040
- case import_utils52.AST_NODE_TYPES.Literal:
8228
+ case import_utils53.AST_NODE_TYPES.Literal:
8041
8229
  return "regex" in node ? null : getText(node);
8042
- case import_utils52.AST_NODE_TYPES.TemplateLiteral:
8230
+ case import_utils53.AST_NODE_TYPES.TemplateLiteral:
8043
8231
  return node.expressions.length === 0 ? getText(node) : null;
8044
- case import_utils52.AST_NODE_TYPES.UnaryExpression:
8232
+ case import_utils53.AST_NODE_TYPES.UnaryExpression:
8045
8233
  return NUMERIC_SIGNS2.has(node.operator) && literalText(node.argument, getText) !== null ? getText(node) : null;
8046
8234
  default:
8047
8235
  return null;
@@ -8049,15 +8237,15 @@ function literalText(node, getText) {
8049
8237
  }
8050
8238
  function isPureReceiver(node) {
8051
8239
  switch (node.type) {
8052
- case import_utils52.AST_NODE_TYPES.Identifier:
8053
- case import_utils52.AST_NODE_TYPES.ThisExpression:
8240
+ case import_utils53.AST_NODE_TYPES.Identifier:
8241
+ case import_utils53.AST_NODE_TYPES.ThisExpression:
8054
8242
  return true;
8055
- case import_utils52.AST_NODE_TYPES.MemberExpression:
8243
+ case import_utils53.AST_NODE_TYPES.MemberExpression:
8056
8244
  if (node.optional) {
8057
8245
  return false;
8058
8246
  }
8059
8247
  if (node.computed) {
8060
- return node.property.type === import_utils52.AST_NODE_TYPES.Literal && isPureReceiver(node.object);
8248
+ return node.property.type === import_utils53.AST_NODE_TYPES.Literal && isPureReceiver(node.object);
8061
8249
  }
8062
8250
  return isPureReceiver(node.object);
8063
8251
  default:
@@ -8065,12 +8253,12 @@ function isPureReceiver(node) {
8065
8253
  }
8066
8254
  }
8067
8255
  function literalIndex(node) {
8068
- if (node.type !== import_utils52.AST_NODE_TYPES.Literal || typeof node.value !== "number") {
8256
+ if (node.type !== import_utils53.AST_NODE_TYPES.Literal || typeof node.value !== "number") {
8069
8257
  return null;
8070
8258
  }
8071
8259
  return Number.isInteger(node.value) && node.value >= 0 ? node.value : null;
8072
8260
  }
8073
- var strict_test_assertions_default = import_utils52.ESLintUtils.RuleCreator(
8261
+ var strict_test_assertions_default = import_utils53.ESLintUtils.RuleCreator(
8074
8262
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
8075
8263
  )({
8076
8264
  name: "strict-test-assertions",
@@ -8093,24 +8281,24 @@ var strict_test_assertions_default = import_utils52.ESLintUtils.RuleCreator(
8093
8281
  }
8094
8282
  const { sourceCode } = context;
8095
8283
  function parseAssertion(statement) {
8096
- if (statement.type !== import_utils52.AST_NODE_TYPES.ExpressionStatement) {
8284
+ if (statement.type !== import_utils53.AST_NODE_TYPES.ExpressionStatement) {
8097
8285
  return null;
8098
8286
  }
8099
8287
  const call = statement.expression;
8100
- if (call.type !== import_utils52.AST_NODE_TYPES.CallExpression) {
8288
+ if (call.type !== import_utils53.AST_NODE_TYPES.CallExpression) {
8101
8289
  return null;
8102
8290
  }
8103
8291
  const callee = call.callee;
8104
- if (callee.type !== import_utils52.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils52.AST_NODE_TYPES.Identifier) {
8292
+ if (callee.type !== import_utils53.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils53.AST_NODE_TYPES.Identifier) {
8105
8293
  return null;
8106
8294
  }
8107
8295
  const matcher = callee.property.name;
8108
8296
  const expectCall = callee.object;
8109
- if (expectCall.type !== import_utils52.AST_NODE_TYPES.CallExpression || expectCall.callee.type !== import_utils52.AST_NODE_TYPES.Identifier || expectCall.callee.name !== "expect" || expectCall.arguments.length !== 1) {
8297
+ if (expectCall.type !== import_utils53.AST_NODE_TYPES.CallExpression || expectCall.callee.type !== import_utils53.AST_NODE_TYPES.Identifier || expectCall.callee.name !== "expect" || expectCall.arguments.length !== 1) {
8110
8298
  return null;
8111
8299
  }
8112
8300
  const actual = expectCall.arguments[0];
8113
- if (actual === void 0 || actual.type !== import_utils52.AST_NODE_TYPES.MemberExpression || actual.optional) {
8301
+ if (actual === void 0 || actual.type !== import_utils53.AST_NODE_TYPES.MemberExpression || actual.optional) {
8114
8302
  return null;
8115
8303
  }
8116
8304
  if (!isPureReceiver(actual.object)) {
@@ -8124,7 +8312,7 @@ var strict_test_assertions_default = import_utils52.ESLintUtils.RuleCreator(
8124
8312
  }
8125
8313
  key = { kind: "index", index };
8126
8314
  } else {
8127
- if (actual.property.type !== import_utils52.AST_NODE_TYPES.Identifier || COLLECTION_PROPERTIES.has(actual.property.name)) {
8315
+ if (actual.property.type !== import_utils53.AST_NODE_TYPES.Identifier || COLLECTION_PROPERTIES.has(actual.property.name)) {
8128
8316
  return null;
8129
8317
  }
8130
8318
  key = { kind: "property", name: actual.property.name };
@@ -8136,7 +8324,7 @@ var strict_test_assertions_default = import_utils52.ESLintUtils.RuleCreator(
8136
8324
  return null;
8137
8325
  }
8138
8326
  const expected = call.arguments[0];
8139
- if (call.arguments.length !== 1 || expected === void 0 || expected.type === import_utils52.AST_NODE_TYPES.SpreadElement) {
8327
+ if (call.arguments.length !== 1 || expected === void 0 || expected.type === import_utils53.AST_NODE_TYPES.SpreadElement) {
8140
8328
  return null;
8141
8329
  }
8142
8330
  const literal = literalText(expected, (node) => sourceCode.getText(node));
@@ -8246,8 +8434,8 @@ var strict_test_assertions_default = import_utils52.ESLintUtils.RuleCreator(
8246
8434
  });
8247
8435
 
8248
8436
  // src/rules/no-async-callback-in-waitfor.ts
8249
- var import_utils53 = require("@typescript-eslint/utils");
8250
- var no_async_callback_in_waitfor_default = import_utils53.ESLintUtils.RuleCreator(
8437
+ var import_utils54 = require("@typescript-eslint/utils");
8438
+ var no_async_callback_in_waitfor_default = import_utils54.ESLintUtils.RuleCreator(
8251
8439
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
8252
8440
  )({
8253
8441
  name: "no-async-callback-in-waitfor",
@@ -8268,9 +8456,9 @@ var no_async_callback_in_waitfor_default = import_utils53.ESLintUtils.RuleCreato
8268
8456
  }
8269
8457
  return {
8270
8458
  CallExpression(node) {
8271
- if (node.callee.type === import_utils53.AST_NODE_TYPES.Identifier && node.callee.name === "waitFor") {
8459
+ if (node.callee.type === import_utils54.AST_NODE_TYPES.Identifier && node.callee.name === "waitFor") {
8272
8460
  const callback = node.arguments[0];
8273
- if (callback && (callback.type === import_utils53.AST_NODE_TYPES.ArrowFunctionExpression || callback.type === import_utils53.AST_NODE_TYPES.FunctionExpression) && callback.async) {
8461
+ if (callback && (callback.type === import_utils54.AST_NODE_TYPES.ArrowFunctionExpression || callback.type === import_utils54.AST_NODE_TYPES.FunctionExpression) && callback.async) {
8274
8462
  context.report({
8275
8463
  node: callback,
8276
8464
  messageId: "noAsyncCallbackInWaitFor"
@@ -8283,7 +8471,7 @@ var no_async_callback_in_waitfor_default = import_utils53.ESLintUtils.RuleCreato
8283
8471
  });
8284
8472
 
8285
8473
  // src/rules/no-hand-rolled-sleep.ts
8286
- var import_utils54 = require("@typescript-eslint/utils");
8474
+ var import_utils55 = require("@typescript-eslint/utils");
8287
8475
  var GLOBAL_OBJECTS2 = /* @__PURE__ */ new Set([
8288
8476
  "globalThis",
8289
8477
  "window",
@@ -8302,66 +8490,66 @@ function matchesAnyPattern3(filename, patterns) {
8302
8490
  return false;
8303
8491
  }
8304
8492
  function isSetTimeoutCallee(callee) {
8305
- if (callee.type === import_utils54.AST_NODE_TYPES.Identifier) {
8493
+ if (callee.type === import_utils55.AST_NODE_TYPES.Identifier) {
8306
8494
  return callee.name === "setTimeout";
8307
8495
  }
8308
- return callee.type === import_utils54.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils54.AST_NODE_TYPES.Identifier && callee.property.name === "setTimeout" && callee.object.type === import_utils54.AST_NODE_TYPES.Identifier && GLOBAL_OBJECTS2.has(callee.object.name);
8496
+ return callee.type === import_utils55.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils55.AST_NODE_TYPES.Identifier && callee.property.name === "setTimeout" && callee.object.type === import_utils55.AST_NODE_TYPES.Identifier && GLOBAL_OBJECTS2.has(callee.object.name);
8309
8497
  }
8310
8498
  function soleCall(fn) {
8311
- if (fn.body.type !== import_utils54.AST_NODE_TYPES.BlockStatement) {
8312
- return fn.body.type === import_utils54.AST_NODE_TYPES.CallExpression ? fn.body : null;
8499
+ if (fn.body.type !== import_utils55.AST_NODE_TYPES.BlockStatement) {
8500
+ return fn.body.type === import_utils55.AST_NODE_TYPES.CallExpression ? fn.body : null;
8313
8501
  }
8314
8502
  if (fn.body.body.length !== 1) {
8315
8503
  return null;
8316
8504
  }
8317
8505
  const [only] = fn.body.body;
8318
- if (only?.type !== import_utils54.AST_NODE_TYPES.ExpressionStatement) {
8506
+ if (only?.type !== import_utils55.AST_NODE_TYPES.ExpressionStatement) {
8319
8507
  return null;
8320
8508
  }
8321
- return only.expression.type === import_utils54.AST_NODE_TYPES.CallExpression ? only.expression : null;
8509
+ return only.expression.type === import_utils55.AST_NODE_TYPES.CallExpression ? only.expression : null;
8322
8510
  }
8323
8511
  function isTimedDelay(delay) {
8324
8512
  if (delay === void 0) {
8325
8513
  return false;
8326
8514
  }
8327
- if (delay.type === import_utils54.AST_NODE_TYPES.Literal && typeof delay.value === "number") {
8515
+ if (delay.type === import_utils55.AST_NODE_TYPES.Literal && typeof delay.value === "number") {
8328
8516
  return delay.value !== 0;
8329
8517
  }
8330
8518
  return true;
8331
8519
  }
8332
8520
  function settlesWithoutValue(callback, name) {
8333
- if (callback.type === import_utils54.AST_NODE_TYPES.Identifier) {
8521
+ if (callback.type === import_utils55.AST_NODE_TYPES.Identifier) {
8334
8522
  return callback.name === name;
8335
8523
  }
8336
- if (callback.type !== import_utils54.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils54.AST_NODE_TYPES.FunctionExpression) {
8524
+ if (callback.type !== import_utils55.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils55.AST_NODE_TYPES.FunctionExpression) {
8337
8525
  return false;
8338
8526
  }
8339
8527
  const call = soleCall(callback);
8340
- return call !== null && call.arguments.length === 0 && call.callee.type === import_utils54.AST_NODE_TYPES.Identifier && call.callee.name === name;
8528
+ return call !== null && call.arguments.length === 0 && call.callee.type === import_utils55.AST_NODE_TYPES.Identifier && call.callee.name === name;
8341
8529
  }
8342
8530
  function rejectsInCallback(callback, name) {
8343
- if (callback.type === import_utils54.AST_NODE_TYPES.Identifier) {
8531
+ if (callback.type === import_utils55.AST_NODE_TYPES.Identifier) {
8344
8532
  return callback.name === name;
8345
8533
  }
8346
- if (callback.type !== import_utils54.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils54.AST_NODE_TYPES.FunctionExpression) {
8534
+ if (callback.type !== import_utils55.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils55.AST_NODE_TYPES.FunctionExpression) {
8347
8535
  return false;
8348
8536
  }
8349
8537
  const call = soleCall(callback);
8350
- return call !== null && call.callee.type === import_utils54.AST_NODE_TYPES.Identifier && call.callee.name === name;
8538
+ return call !== null && call.callee.type === import_utils55.AST_NODE_TYPES.Identifier && call.callee.name === name;
8351
8539
  }
8352
8540
  function parameterName(fn, index) {
8353
8541
  const parameter = fn.params[index];
8354
- return parameter?.type === import_utils54.AST_NODE_TYPES.Identifier ? parameter.name : null;
8542
+ return parameter?.type === import_utils55.AST_NODE_TYPES.Identifier ? parameter.name : null;
8355
8543
  }
8356
8544
  function isRaceArm(node) {
8357
8545
  const array = node.parent;
8358
- if (array?.type !== import_utils54.AST_NODE_TYPES.ArrayExpression) {
8546
+ if (array?.type !== import_utils55.AST_NODE_TYPES.ArrayExpression) {
8359
8547
  return false;
8360
8548
  }
8361
8549
  const call = array.parent;
8362
- return call?.type === import_utils54.AST_NODE_TYPES.CallExpression && call.arguments[0] === array && call.callee.type === import_utils54.AST_NODE_TYPES.MemberExpression && !call.callee.computed && call.callee.object.type === import_utils54.AST_NODE_TYPES.Identifier && call.callee.object.name === "Promise" && call.callee.property.type === import_utils54.AST_NODE_TYPES.Identifier && RACE_METHODS.has(call.callee.property.name);
8550
+ return call?.type === import_utils55.AST_NODE_TYPES.CallExpression && call.arguments[0] === array && call.callee.type === import_utils55.AST_NODE_TYPES.MemberExpression && !call.callee.computed && call.callee.object.type === import_utils55.AST_NODE_TYPES.Identifier && call.callee.object.name === "Promise" && call.callee.property.type === import_utils55.AST_NODE_TYPES.Identifier && RACE_METHODS.has(call.callee.property.name);
8363
8551
  }
8364
- var no_hand_rolled_sleep_default = import_utils54.ESLintUtils.RuleCreator(
8552
+ var no_hand_rolled_sleep_default = import_utils55.ESLintUtils.RuleCreator(
8365
8553
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
8366
8554
  )({
8367
8555
  name: "no-hand-rolled-sleep",
@@ -8409,10 +8597,10 @@ var no_hand_rolled_sleep_default = import_utils54.ESLintUtils.RuleCreator(
8409
8597
  }
8410
8598
  const program = sourceCode.ast;
8411
8599
  for (const statement of program.body) {
8412
- if (statement.type === import_utils54.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils54.AST_NODE_TYPES.Literal && statement.expression.value === "use client") {
8600
+ if (statement.type === import_utils55.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils55.AST_NODE_TYPES.Literal && statement.expression.value === "use client") {
8413
8601
  return true;
8414
8602
  }
8415
- if (statement.type === import_utils54.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && CLIENT_ONLY_MODULES.test(statement.source.value)) {
8603
+ if (statement.type === import_utils55.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && CLIENT_ONLY_MODULES.test(statement.source.value)) {
8416
8604
  return true;
8417
8605
  }
8418
8606
  }
@@ -8428,11 +8616,11 @@ var no_hand_rolled_sleep_default = import_utils54.ESLintUtils.RuleCreator(
8428
8616
  };
8429
8617
  return {
8430
8618
  NewExpression(node) {
8431
- if (node.callee.type !== import_utils54.AST_NODE_TYPES.Identifier || node.callee.name !== "Promise") {
8619
+ if (node.callee.type !== import_utils55.AST_NODE_TYPES.Identifier || node.callee.name !== "Promise") {
8432
8620
  return;
8433
8621
  }
8434
8622
  const executor = node.arguments[0];
8435
- if (executor?.type !== import_utils54.AST_NODE_TYPES.ArrowFunctionExpression && executor?.type !== import_utils54.AST_NODE_TYPES.FunctionExpression) {
8623
+ if (executor?.type !== import_utils55.AST_NODE_TYPES.ArrowFunctionExpression && executor?.type !== import_utils55.AST_NODE_TYPES.FunctionExpression) {
8436
8624
  return;
8437
8625
  }
8438
8626
  const call = soleCall(executor);
@@ -8505,200 +8693,226 @@ var rules = {
8505
8693
  "jsdoc-restates-signature": jsdoc_restates_signature_default,
8506
8694
  "no-restated-comment": no_restated_comment_default,
8507
8695
  "trailing-value-narration": trailing_value_narration_default,
8696
+ "no-type-member-comment-wall": no_type_member_comment_wall_default,
8508
8697
  "no-tautological-expect": no_tautological_expect_default,
8509
8698
  "require-interface-for-injected-service": require_interface_for_injected_service_default,
8510
8699
  "strict-test-assertions": strict_test_assertions_default,
8511
8700
  "prefer-non-nullable-collection": prefer_non_nullable_collection_default,
8512
8701
  "no-async-callback-in-waitfor": no_async_callback_in_waitfor_default
8513
8702
  };
8703
+ var meta = {
8704
+ name: "@sarj/eslint-plugin",
8705
+ version: "6.0.0"
8706
+ };
8707
+ var recommendedRules = {
8708
+ "@sarj/zod-naming-convention": "warn",
8709
+ "@sarj/require-assert-never": "error",
8710
+ "@sarj/require-zod-form-validation": "error",
8711
+ "@sarj/enforce-file-structure": "warn",
8712
+ "@sarj/no-client-side-data-fetching": "warn",
8713
+ "@sarj/prefer-server-actions": "warn",
8714
+ "@sarj/no-unnecessary-use-client": "warn",
8715
+ "@sarj/prefer-schema-for-api-payload": "warn",
8716
+ // Distilled from sarj-audit skills — warn in recommended, error in strict.
8717
+ "@sarj/no-sentinel-return-on-catch": "warn",
8718
+ "@sarj/no-log-only-catch": "warn",
8719
+ "@sarj/no-insecure-random-id": "warn",
8720
+ "@sarj/no-json-stringify-error": "warn",
8721
+ "@sarj/no-string-concat-in-loop": "warn",
8722
+ "@sarj/prefer-discriminated-union": "warn",
8723
+ "@sarj/no-comment-cruft": "warn",
8724
+ // Frontend / styling — distilled from frontend PR-review mining.
8725
+ "@sarj/prefer-semantic-colors": [
8726
+ "warn",
8727
+ { requireSemanticTokens: true }
8728
+ ],
8729
+ // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
8730
+ "@sarj/no-fat-try-blocks": "warn",
8731
+ "@sarj/no-cors-wildcard-with-credentials": "warn",
8732
+ "@sarj/no-secret-in-log": "warn",
8733
+ "@sarj/no-unsafe-mock-casting": "warn",
8734
+ "@sarj/prefer-string-literal-union": "warn",
8735
+ "@sarj/prefer-zod-enum": "warn",
8736
+ // A type hand-written beside the Zod schema it restates drifts the
8737
+ // moment the schema gains a field. 30,759-file, 17-repo sweep
8738
+ // (2026-07): 5 reports, 5 true positives, all in public repos.
8739
+ // `requireIdenticalShape: false` widens it to 8 reports, 1 of them noise.
8740
+ "@sarj/prefer-zod-infer": "warn",
8741
+ // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
8742
+ "@sarj/require-fetch-timeout": "warn",
8743
+ "@sarj/no-silent-promise-catch": "warn",
8744
+ // Second SARJ port wave — the TS/Python parity gap. Each targets a
8745
+ // defect class seen in production Workers code: timing-leaky secret
8746
+ // compares, non-idempotent store writes under queue redelivery,
8747
+ // O(N) pagination, implicit row contracts, flaky timed tests.
8748
+ "@sarj/prefer-constant-time-secret-compare": "error",
8749
+ "@sarj/store-insert-requires-on-conflict": "warn",
8750
+ "@sarj/no-offset-pagination": "warn",
8751
+ "@sarj/no-select-star": "warn",
8752
+ // Uncancellable hand-rolled timers. Verified against the shipped
8753
+ // strict config (205 enabled rules) that nothing already reports this
8754
+ // position; `unicorn` 72 has no promisified-timer rule at all.
8755
+ "@sarj/no-hand-rolled-sleep": "warn",
8756
+ "@sarj/no-sleep-in-test-body": "warn",
8757
+ "@sarj/no-conditional-in-test": "warn",
8758
+ "@sarj/no-repeated-string-literal": "warn",
8759
+ "@sarj/no-positional-tuple-return": "warn",
8760
+ // Injection guard — low FP, applies to any repo touching SQL.
8761
+ "@sarj/no-dynamic-sql": "warn",
8762
+ // Mined from two years of PR review (SARJ-928). Schema-layer sibling of
8763
+ // `no-enum`; autofixable for inline string-literal objects.
8764
+ "@sarj/no-zod-native-enum": "warn",
8765
+ // Mined from two years of PR review — the single most frequent uncovered
8766
+ // theme (~37 PRs). Measured 17 hits / 1085 real TS files, all true
8767
+ // positives, so it is safe to run everywhere.
8768
+ "@sarj/prefer-module-level-constant": "warn",
8769
+ // Anti-comment-verbosity family (2026-07), from a 37,918-comment,
8770
+ // nine-repo measurement study. Each is a deletion-class finding, so each
8771
+ // was validated against pydantic / trio / attrs as well as the maintained
8772
+ // repos: `no-restated-comment` 0 hits in the flagship first-party
8773
+ // repo and 4 in the three famous
8774
+ // corpora combined; `trailing-value-narration` 18 hits, 18 true
8775
+ // positives; `jsdoc-restates-signature` 36 hits, 0 measured false
8776
+ // positives, and it offers a suggestion rather than a `--fix` because a
8777
+ // wrong deletion is silent information loss.
8778
+ "@sarj/no-restated-comment": "warn",
8779
+ "@sarj/jsdoc-restates-signature": "warn",
8780
+ "@sarj/trailing-value-narration": "warn",
8781
+ // The VOLUME arm of the same family (2026-07). Its siblings judge one
8782
+ // comment at a time and can only condemn one that adds nothing; this
8783
+ // one judges a TYPE, so it can report ten rows that each add a word.
8784
+ // 33 OSS TS repos / 46,861 files: 22 findings, all read, 0 false; zero
8785
+ // across ten first-party repos, where the generated-file sniff alone
8786
+ // removed 321 of the 407 raw hits. Measurements and the six false
8787
+ // positives that shaped the guards: docs/rules/no-type-member-comment-wall.md
8788
+ "@sarj/no-type-member-comment-wall": "warn",
8789
+ // The TS half of SARJ057 (2026-07). Python has caught the
8790
+ // assertion-FREE test since 0.15.0 (SARJ043) and had no TS
8791
+ // counterpart, which is how `expect(true).toBe(true); // placeholder`
8792
+ // survived in a first-party repo: the file HAS an assertion.
8793
+ // Measured across 5,819 .ts/.tsx files (1,003 of them test files) in
8794
+ // six internal repos plus got / hono / swr / trpc: 3 hits, 3 true
8795
+ // positives, 0 false positives.
8796
+ "@sarj/no-tautological-expect": "warn",
8797
+ // Substitutability: an exported service class with injected
8798
+ // collaborators and no interface above it can only be tested by
8799
+ // mocking. 11-repo sweep: 229 exported classes, 82% already carry a
8800
+ // port, 29 fire, 28 of them true positives.
8801
+ "@sarj/require-interface-for-injected-service": "warn",
8802
+ "@sarj/prefer-non-nullable-collection": "warn",
8803
+ "@sarj/no-async-callback-in-waitfor": "warn",
8804
+ // `strict-test-assertions` shipped in `rules` but in NEITHER preset, so a
8805
+ // consumer using `configs.recommended`/`configs.strict` instead of the shared
8806
+ // `eslint.strict.mjs` had silently never run it. `flat-presets.test.ts`
8807
+ // asserts strict wires every rule the plugin ships, so it cannot recur.
8808
+ "@sarj/strict-test-assertions": "warn"
8809
+ };
8810
+ var strictRules = {
8811
+ "@sarj/zod-naming-convention": "error",
8812
+ "@sarj/require-assert-never": "error",
8813
+ "@sarj/require-zod-form-validation": "error",
8814
+ "@sarj/enforce-file-structure": "error",
8815
+ "@sarj/no-raw-env": "error",
8816
+ "@sarj/no-enum": "error",
8817
+ "@sarj/no-client-side-data-fetching": "error",
8818
+ "@sarj/prefer-server-actions": "error",
8819
+ "@sarj/no-unnecessary-use-client": "error",
8820
+ "@sarj/prefer-schema-for-api-payload": "error",
8821
+ // Distilled from sarj-audit skills.
8822
+ "@sarj/no-sentinel-return-on-catch": "error",
8823
+ "@sarj/no-log-only-catch": "error",
8824
+ "@sarj/no-insecure-random-id": "error",
8825
+ "@sarj/no-json-stringify-error": "error",
8826
+ "@sarj/no-string-concat-in-loop": "error",
8827
+ "@sarj/prefer-discriminated-union": "error",
8828
+ "@sarj/no-comment-cruft": "error",
8829
+ // Frontend / styling — distilled from frontend PR-review mining. Stylistic,
8830
+ // no autofix → warn (rollout should prove the FP rate before raising it).
8831
+ "@sarj/prefer-semantic-colors": [
8832
+ "error",
8833
+ { requireSemanticTokens: true }
8834
+ ],
8835
+ // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
8836
+ "@sarj/no-fat-try-blocks": "error",
8837
+ "@sarj/no-cors-wildcard-with-credentials": "error",
8838
+ "@sarj/no-secret-in-log": "error",
8839
+ "@sarj/no-unsafe-mock-casting": "error",
8840
+ // Promoted to error 2026-07-25 — strict means strict (user directive).
8841
+ "@sarj/prefer-string-literal-union": "error",
8842
+ "@sarj/prefer-zod-enum": "error",
8843
+ // See the `recommended` block for the measured counts.
8844
+ "@sarj/prefer-zod-infer": "error",
8845
+ // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
8846
+ "@sarj/require-fetch-timeout": "error",
8847
+ "@sarj/no-silent-promise-catch": "error",
8848
+ // Second SARJ port wave — the TS/Python parity gap.
8849
+ "@sarj/prefer-constant-time-secret-compare": "error",
8850
+ "@sarj/store-insert-requires-on-conflict": "error",
8851
+ "@sarj/no-offset-pagination": "error",
8852
+ "@sarj/no-select-star": "error",
8853
+ // Uncancellable hand-rolled timers. Verified against the shipped
8854
+ // strict config (205 enabled rules) that nothing already reports this
8855
+ // position; `unicorn` 72 has no promisified-timer rule at all.
8856
+ "@sarj/no-hand-rolled-sleep": "error",
8857
+ "@sarj/no-sleep-in-test-body": "error",
8858
+ "@sarj/no-conditional-in-test": "error",
8859
+ "@sarj/no-repeated-string-literal": "error",
8860
+ // API-shape advice rather than a runtime defect — a corpus sweep found its
8861
+ // only hits are parser `[value, cursor]` returns, which are conventional.
8862
+ // Warn even in strict until a rollout justifies more.
8863
+ "@sarj/no-positional-tuple-return": "error",
8864
+ "@sarj/no-dynamic-sql": "error",
8865
+ // Architectural: both need per-repo config to be meaningful, so they
8866
+ // are strict-only. `no-storage-in-stateless-modules` is a no-op until
8867
+ // its `modules` option names the directories a team declared stateless;
8868
+ // `no-raw-fetch-outside-clients` defaults to the `clients/` convention
8869
+ // and takes an `allow` list for repos that lay their client layer out
8870
+ // differently.
8871
+ "@sarj/no-raw-fetch-outside-clients": "error",
8872
+ "@sarj/no-storage-in-stateless-modules": "error",
8873
+ // Mined from two years of PR review (SARJ-928).
8874
+ "@sarj/no-zod-native-enum": "error",
8875
+ "@sarj/prefer-module-level-constant": "error",
8876
+ // Anti-comment-verbosity family (2026-07) — see the `recommended` block
8877
+ // for the measured hit counts and false-positive rates.
8878
+ "@sarj/no-restated-comment": "error",
8879
+ "@sarj/jsdoc-restates-signature": "error",
8880
+ "@sarj/trailing-value-narration": "error",
8881
+ "@sarj/no-type-member-comment-wall": "error",
8882
+ // TS half of SARJ057 — see the `recommended` block for the measurement.
8883
+ "@sarj/no-tautological-expect": "error",
8884
+ // Substitutability: the TS sibling of the Python `prefer-real-store-in-tests`
8885
+ // / `prefer-library-fake` wave. The convention already exists in the
8886
+ // corpus (175 `implements` clauses vs 29 hits), so strict enforces it.
8887
+ "@sarj/require-interface-for-injected-service": "error",
8888
+ "@sarj/prefer-non-nullable-collection": "error",
8889
+ "@sarj/no-async-callback-in-waitfor": "error",
8890
+ // See the `recommended` block.
8891
+ "@sarj/strict-test-assertions": "error"
8892
+ };
8514
8893
  var plugin = {
8515
- meta: {
8516
- name: "@sarj/eslint-plugin",
8517
- version: "5.0.0"
8518
- },
8894
+ meta,
8519
8895
  rules,
8520
8896
  configs: {
8521
- recommended: {
8522
- plugins: ["@sarj"],
8523
- rules: {
8524
- "@sarj/zod-naming-convention": "warn",
8525
- "@sarj/require-assert-never": "error",
8526
- "@sarj/require-zod-form-validation": "error",
8527
- "@sarj/enforce-file-structure": "warn",
8528
- "@sarj/no-client-side-data-fetching": "warn",
8529
- "@sarj/prefer-server-actions": "warn",
8530
- "@sarj/no-unnecessary-use-client": "warn",
8531
- "@sarj/prefer-schema-for-api-payload": "warn",
8532
- // Distilled from sarj-audit skills — warn in recommended, error in strict.
8533
- "@sarj/no-sentinel-return-on-catch": "warn",
8534
- "@sarj/no-log-only-catch": "warn",
8535
- "@sarj/no-insecure-random-id": "warn",
8536
- "@sarj/no-json-stringify-error": "warn",
8537
- "@sarj/no-string-concat-in-loop": "warn",
8538
- "@sarj/prefer-discriminated-union": "warn",
8539
- "@sarj/no-comment-cruft": "warn",
8540
- // Frontend / styling — distilled from frontend PR-review mining.
8541
- "@sarj/prefer-semantic-colors": [
8542
- "warn",
8543
- { requireSemanticTokens: true }
8544
- ],
8545
- // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
8546
- "@sarj/no-fat-try-blocks": "warn",
8547
- "@sarj/no-cors-wildcard-with-credentials": "warn",
8548
- "@sarj/no-secret-in-log": "warn",
8549
- "@sarj/no-unsafe-mock-casting": "warn",
8550
- "@sarj/prefer-string-literal-union": "warn",
8551
- "@sarj/prefer-zod-enum": "warn",
8552
- // A type hand-written beside the Zod schema it restates drifts the
8553
- // moment the schema gains a field. 30,759-file, 17-repo sweep
8554
- // (2026-07): 5 reports, 5 true positives, all in public repos.
8555
- // `requireIdenticalShape: false` widens it to 8 reports, 1 of them noise.
8556
- "@sarj/prefer-zod-infer": "warn",
8557
- // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
8558
- "@sarj/require-fetch-timeout": "warn",
8559
- "@sarj/no-silent-promise-catch": "warn",
8560
- // Second SARJ port wave — the TS/Python parity gap. Each targets a
8561
- // defect class seen in production Workers code: timing-leaky secret
8562
- // compares, non-idempotent store writes under queue redelivery,
8563
- // O(N) pagination, implicit row contracts, flaky timed tests.
8564
- "@sarj/prefer-constant-time-secret-compare": "error",
8565
- "@sarj/store-insert-requires-on-conflict": "warn",
8566
- "@sarj/no-offset-pagination": "warn",
8567
- "@sarj/no-select-star": "warn",
8568
- // Uncancellable hand-rolled timers. Verified against the shipped
8569
- // strict config (205 enabled rules) that nothing already reports this
8570
- // position; `unicorn` 72 has no promisified-timer rule at all.
8571
- "@sarj/no-hand-rolled-sleep": "warn",
8572
- "@sarj/no-sleep-in-test-body": "warn",
8573
- "@sarj/no-conditional-in-test": "warn",
8574
- "@sarj/no-repeated-string-literal": "warn",
8575
- "@sarj/no-positional-tuple-return": "warn",
8576
- // Injection guard — low FP, applies to any repo touching SQL.
8577
- "@sarj/no-dynamic-sql": "warn",
8578
- // Mined from two years of PR review (SARJ-928). Schema-layer sibling of
8579
- // `no-enum`; autofixable for inline string-literal objects.
8580
- "@sarj/no-zod-native-enum": "warn",
8581
- // Mined from two years of PR review — the single most frequent uncovered
8582
- // theme (~37 PRs). Measured 17 hits / 1085 real TS files, all true
8583
- // positives, so it is safe to run everywhere.
8584
- "@sarj/prefer-module-level-constant": "warn",
8585
- // Anti-comment-verbosity family (2026-07), from a 37,918-comment,
8586
- // nine-repo measurement study. Each is a deletion-class finding, so each
8587
- // was validated against pydantic / trio / attrs as well as the maintained
8588
- // repos: `no-restated-comment` 0 hits in the flagship first-party
8589
- // repo and 4 in the three famous
8590
- // corpora combined; `trailing-value-narration` 18 hits, 18 true
8591
- // positives; `jsdoc-restates-signature` 36 hits, 0 measured false
8592
- // positives, and it offers a suggestion rather than a `--fix` because a
8593
- // wrong deletion is silent information loss.
8594
- "@sarj/no-restated-comment": "warn",
8595
- "@sarj/jsdoc-restates-signature": "warn",
8596
- "@sarj/trailing-value-narration": "warn",
8597
- // The TS half of SARJ057 (2026-07). Python has caught the
8598
- // assertion-FREE test since 0.15.0 (SARJ043) and had no TS
8599
- // counterpart, which is how `expect(true).toBe(true); // placeholder`
8600
- // survived in a first-party repo: the file HAS an assertion.
8601
- // Measured across 5,819 .ts/.tsx files (1,003 of them test files) in
8602
- // six internal repos plus got / hono / swr / trpc: 3 hits, 3 true
8603
- // positives, 0 false positives.
8604
- "@sarj/no-tautological-expect": "warn",
8605
- // Substitutability: an exported service class with injected
8606
- // collaborators and no interface above it can only be tested by
8607
- // mocking. 11-repo sweep: 229 exported classes, 82% already carry a
8608
- // port, 29 fire, 28 of them true positives.
8609
- "@sarj/require-interface-for-injected-service": "warn",
8610
- "@sarj/prefer-non-nullable-collection": "warn",
8611
- "@sarj/no-async-callback-in-waitfor": "warn"
8612
- }
8613
- },
8614
- strict: {
8615
- plugins: ["@sarj"],
8616
- rules: {
8617
- "@sarj/zod-naming-convention": "error",
8618
- "@sarj/require-assert-never": "error",
8619
- "@sarj/require-zod-form-validation": "error",
8620
- "@sarj/enforce-file-structure": "error",
8621
- "@sarj/no-raw-env": "error",
8622
- "@sarj/no-enum": "error",
8623
- "@sarj/no-client-side-data-fetching": "error",
8624
- "@sarj/prefer-server-actions": "error",
8625
- "@sarj/no-unnecessary-use-client": "error",
8626
- "@sarj/prefer-schema-for-api-payload": "error",
8627
- // Distilled from sarj-audit skills.
8628
- "@sarj/no-sentinel-return-on-catch": "error",
8629
- "@sarj/no-log-only-catch": "error",
8630
- "@sarj/no-insecure-random-id": "error",
8631
- "@sarj/no-json-stringify-error": "error",
8632
- "@sarj/no-string-concat-in-loop": "error",
8633
- "@sarj/prefer-discriminated-union": "error",
8634
- "@sarj/no-comment-cruft": "error",
8635
- // Frontend / styling — distilled from frontend PR-review mining. Stylistic,
8636
- // no autofix → warn (rollout should prove the FP rate before raising it).
8637
- "@sarj/prefer-semantic-colors": [
8638
- "error",
8639
- { requireSemanticTokens: true }
8640
- ],
8641
- // Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
8642
- "@sarj/no-fat-try-blocks": "error",
8643
- "@sarj/no-cors-wildcard-with-credentials": "error",
8644
- "@sarj/no-secret-in-log": "error",
8645
- "@sarj/no-unsafe-mock-casting": "error",
8646
- // Promoted to error 2026-07-25 — strict means strict (user directive).
8647
- "@sarj/prefer-string-literal-union": "error",
8648
- "@sarj/prefer-zod-enum": "error",
8649
- // See the `recommended` block for the measured counts.
8650
- "@sarj/prefer-zod-infer": "error",
8651
- // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
8652
- "@sarj/require-fetch-timeout": "error",
8653
- "@sarj/no-silent-promise-catch": "error",
8654
- // Second SARJ port wave — the TS/Python parity gap.
8655
- "@sarj/prefer-constant-time-secret-compare": "error",
8656
- "@sarj/store-insert-requires-on-conflict": "error",
8657
- "@sarj/no-offset-pagination": "error",
8658
- "@sarj/no-select-star": "error",
8659
- // Uncancellable hand-rolled timers. Verified against the shipped
8660
- // strict config (205 enabled rules) that nothing already reports this
8661
- // position; `unicorn` 72 has no promisified-timer rule at all.
8662
- "@sarj/no-hand-rolled-sleep": "error",
8663
- "@sarj/no-sleep-in-test-body": "error",
8664
- "@sarj/no-conditional-in-test": "error",
8665
- "@sarj/no-repeated-string-literal": "error",
8666
- // API-shape advice rather than a runtime defect — a corpus sweep found its
8667
- // only hits are parser `[value, cursor]` returns, which are conventional.
8668
- // Warn even in strict until a rollout justifies more.
8669
- "@sarj/no-positional-tuple-return": "error",
8670
- "@sarj/no-dynamic-sql": "error",
8671
- // Architectural: both need per-repo config to be meaningful, so they
8672
- // are strict-only. `no-storage-in-stateless-modules` is a no-op until
8673
- // its `modules` option names the directories a team declared stateless;
8674
- // `no-raw-fetch-outside-clients` defaults to the `clients/` convention
8675
- // and takes an `allow` list for repos that lay their client layer out
8676
- // differently.
8677
- "@sarj/no-raw-fetch-outside-clients": "error",
8678
- "@sarj/no-storage-in-stateless-modules": "error",
8679
- // Mined from two years of PR review (SARJ-928).
8680
- "@sarj/no-zod-native-enum": "error",
8681
- "@sarj/prefer-module-level-constant": "error",
8682
- // Anti-comment-verbosity family (2026-07) — see the `recommended` block
8683
- // for the measured hit counts and false-positive rates.
8684
- "@sarj/no-restated-comment": "error",
8685
- "@sarj/jsdoc-restates-signature": "error",
8686
- "@sarj/trailing-value-narration": "error",
8687
- // TS half of SARJ057 — see the `recommended` block for the measurement.
8688
- "@sarj/no-tautological-expect": "error",
8689
- // Substitutability: the TS sibling of the Python `prefer-real-store-in-tests`
8690
- // / `prefer-library-fake` wave. The convention already exists in the
8691
- // corpus (175 `implements` clauses vs 29 hits), so strict enforces it.
8692
- "@sarj/require-interface-for-injected-service": "error",
8693
- "@sarj/prefer-non-nullable-collection": "error",
8694
- "@sarj/no-async-callback-in-waitfor": "error"
8695
- }
8696
- }
8897
+ recommended: {},
8898
+ strict: {}
8697
8899
  }
8698
8900
  };
8901
+ plugin.configs.recommended = {
8902
+ name: "@sarj/recommended",
8903
+ plugins: { "@sarj": plugin },
8904
+ rules: recommendedRules
8905
+ };
8906
+ plugin.configs.strict = {
8907
+ name: "@sarj/strict",
8908
+ plugins: { "@sarj": plugin },
8909
+ rules: strictRules
8910
+ };
8699
8911
  var index_default = plugin;
8700
8912
  // Annotate the CommonJS export names for ESM import in node:
8701
8913
  0 && (module.exports = {
8702
- rules
8914
+ recommendedRules,
8915
+ rules,
8916
+ strictRules
8703
8917
  });
8704
8918
  //# sourceMappingURL=index.cjs.map