@sarj/eslint-plugin 13.0.0 → 14.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.js CHANGED
@@ -622,77 +622,8 @@ var duplicate_test_body_default = createRule({
622
622
  }
623
623
  });
624
624
 
625
- // src/rules/no-async-callback-in-wait-for.ts
626
- import { AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
627
- var noAsyncCallbackInWaitForDocumentation = {
628
- summary: "Disallow async callbacks in `waitFor` to prevent swallowed promise rejections.",
629
- rationale: "`waitFor` retries synchronous assertions; an async callback changes that contract and can hide a rejected assertion promise.",
630
- remediation: "Remove `async` and keep the assertions inside `waitFor` synchronous.",
631
- category: "testing",
632
- aliases: ["no-async-callback-in-waitfor"],
633
- limitations: [
634
- "The rule checks inline first-argument callbacks to bare or non-computed `.waitFor` calls in test files."
635
- ],
636
- examples: [
637
- {
638
- id: "synchronous-wait-for-callback",
639
- title: "waitFor retries a synchronous assertion",
640
- outcome: "no-match",
641
- files: [{ path: "src/component.test.ts", source: "it('works', async () => { await waitFor(() => expect(foo).toBe(true)); });" }],
642
- focusPath: "src/component.test.ts",
643
- expectedCount: 0,
644
- public: true
645
- },
646
- {
647
- id: "async-wait-for-callback",
648
- title: "waitFor receives an async callback",
649
- outcome: "match",
650
- files: [{ path: "src/component.test.ts", source: "it('fails', async () => { await waitFor(async () => expect(foo).toBe(true)); });" }],
651
- focusPath: "src/component.test.ts",
652
- expectedCount: 1,
653
- public: true
654
- }
655
- ]
656
- };
657
- var isWaitForCallee = (callee) => {
658
- if (callee.type === AST_NODE_TYPES3.Identifier) return callee.name === "waitFor";
659
- return callee.type === AST_NODE_TYPES3.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES3.Identifier && callee.property.name === "waitFor";
660
- };
661
- var no_async_callback_in_wait_for_default = createRule({
662
- name: "no-async-callback-in-wait-for",
663
- documentation: noAsyncCallbackInWaitForDocumentation,
664
- meta: {
665
- type: "problem",
666
- docs: {
667
- description: "Disallow async callbacks in `waitFor` to prevent swallowed promise rejections."
668
- },
669
- schema: [],
670
- messages: {
671
- noAsyncCallbackInWaitFor: "The callback to `waitFor` should not be async. It expects synchronous assertions and runs the callback repeatedly."
672
- }
673
- },
674
- defaultOptions: [],
675
- create(context) {
676
- if (!isTestFile(context.filename)) {
677
- return {};
678
- }
679
- return {
680
- CallExpression(node) {
681
- if (!isWaitForCallee(node.callee)) return;
682
- const callback = node.arguments[0];
683
- if (callback && (callback.type === AST_NODE_TYPES3.ArrowFunctionExpression || callback.type === AST_NODE_TYPES3.FunctionExpression) && callback.async) {
684
- context.report({
685
- node: callback,
686
- messageId: "noAsyncCallbackInWaitFor"
687
- });
688
- }
689
- }
690
- };
691
- }
692
- });
693
-
694
625
  // src/rules/no-client-side-data-fetching.ts
695
- import { AST_NODE_TYPES as AST_NODE_TYPES4 } from "@typescript-eslint/utils";
626
+ import { AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
696
627
  var noClientSideDataFetchingDocumentation = {
697
628
  summary: "Disallow direct data fetching inside `useEffect` or `useLayoutEffect`.",
698
629
  rationale: "Effect-driven reads begin after rendering and can create request waterfalls, duplicate fetches, and loading-state layout shifts.",
@@ -745,33 +676,33 @@ var ANALYTICS_SEGMENTS = /* @__PURE__ */ new Set([
745
676
  ]);
746
677
  function isEffectHookCall(node) {
747
678
  const callee = node.callee;
748
- if (callee.type === AST_NODE_TYPES4.Identifier) {
679
+ if (callee.type === AST_NODE_TYPES3.Identifier) {
749
680
  return callee.name === "useEffect" || callee.name === "useLayoutEffect";
750
681
  }
751
- if (callee.type === AST_NODE_TYPES4.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES4.Identifier && callee.object.name === "React" && callee.property.type === AST_NODE_TYPES4.Identifier) {
682
+ if (callee.type === AST_NODE_TYPES3.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES3.Identifier && callee.object.name === "React" && callee.property.type === AST_NODE_TYPES3.Identifier) {
752
683
  return callee.property.name === "useEffect" || callee.property.name === "useLayoutEffect";
753
684
  }
754
685
  return false;
755
686
  }
756
687
  function isFetchCall(node) {
757
688
  const callee = node.callee;
758
- if (callee.type === AST_NODE_TYPES4.Identifier && callee.name === "fetch") {
689
+ if (callee.type === AST_NODE_TYPES3.Identifier && callee.name === "fetch") {
759
690
  const method = readMethodProperty(node.arguments[1]);
760
691
  if (method !== null && method !== "GET") {
761
692
  return false;
762
693
  }
763
694
  return true;
764
695
  }
765
- if (callee.type === AST_NODE_TYPES4.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES4.Identifier && FETCH_LIBS.has(callee.object.name) && callee.property.type === AST_NODE_TYPES4.Identifier) {
696
+ if (callee.type === AST_NODE_TYPES3.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES3.Identifier && FETCH_LIBS.has(callee.object.name) && callee.property.type === AST_NODE_TYPES3.Identifier) {
766
697
  return HTTP_METHOD_NAMES.has(callee.property.name);
767
698
  }
768
- if (callee.type === AST_NODE_TYPES4.Identifier && (callee.name === "axios" || callee.name === "ky")) {
699
+ if (callee.type === AST_NODE_TYPES3.Identifier && (callee.name === "axios" || callee.name === "ky")) {
769
700
  const firstArg = node.arguments[0];
770
701
  const secondArg = node.arguments[1];
771
702
  let configArg;
772
- if (firstArg?.type === AST_NODE_TYPES4.ObjectExpression) {
703
+ if (firstArg?.type === AST_NODE_TYPES3.ObjectExpression) {
773
704
  configArg = firstArg;
774
- } else if (secondArg?.type === AST_NODE_TYPES4.ObjectExpression) {
705
+ } else if (secondArg?.type === AST_NODE_TYPES3.ObjectExpression) {
775
706
  configArg = secondArg;
776
707
  }
777
708
  const method = readMethodProperty(configArg);
@@ -783,16 +714,16 @@ function isFetchCall(node) {
783
714
  return false;
784
715
  }
785
716
  function readMethodProperty(optionsArg) {
786
- if (!optionsArg || optionsArg.type !== AST_NODE_TYPES4.ObjectExpression) {
717
+ if (!optionsArg || optionsArg.type !== AST_NODE_TYPES3.ObjectExpression) {
787
718
  return null;
788
719
  }
789
720
  for (const prop of optionsArg.properties) {
790
- if (prop.type !== AST_NODE_TYPES4.Property) continue;
721
+ if (prop.type !== AST_NODE_TYPES3.Property) continue;
791
722
  if (prop.computed) continue;
792
723
  const key = prop.key;
793
- const matchesMethodKey = key.type === AST_NODE_TYPES4.Identifier && key.name === "method" || key.type === AST_NODE_TYPES4.Literal && key.value === "method";
724
+ const matchesMethodKey = key.type === AST_NODE_TYPES3.Identifier && key.name === "method" || key.type === AST_NODE_TYPES3.Literal && key.value === "method";
794
725
  if (!matchesMethodKey) continue;
795
- if (prop.value.type === AST_NODE_TYPES4.Literal && typeof prop.value.value === "string") {
726
+ if (prop.value.type === AST_NODE_TYPES3.Literal && typeof prop.value.value === "string") {
796
727
  return prop.value.value.toUpperCase();
797
728
  }
798
729
  return null;
@@ -807,13 +738,13 @@ function isAnalyticsCall(node) {
807
738
  function extractUrlString(node) {
808
739
  const firstArg = node.arguments[0];
809
740
  if (!firstArg) return "";
810
- if (firstArg.type === AST_NODE_TYPES4.Literal && typeof firstArg.value === "string") {
741
+ if (firstArg.type === AST_NODE_TYPES3.Literal && typeof firstArg.value === "string") {
811
742
  return firstArg.value;
812
743
  }
813
- if (firstArg.type === AST_NODE_TYPES4.TemplateLiteral) {
744
+ if (firstArg.type === AST_NODE_TYPES3.TemplateLiteral) {
814
745
  return firstArg.quasis.map((q) => q.value.cooked).join("");
815
746
  }
816
- if (firstArg.type === AST_NODE_TYPES4.Identifier) {
747
+ if (firstArg.type === AST_NODE_TYPES3.Identifier) {
817
748
  return firstArg.name;
818
749
  }
819
750
  return "";
@@ -855,10 +786,10 @@ var no_client_side_data_fetching_default = createRule({
855
786
  });
856
787
 
857
788
  // src/rules/no-comment-cruft.ts
858
- import { AST_NODE_TYPES as AST_NODE_TYPES6 } from "@typescript-eslint/utils";
789
+ import { AST_NODE_TYPES as AST_NODE_TYPES5 } from "@typescript-eslint/utils";
859
790
 
860
791
  // src/rules/_comments.ts
861
- import { AST_NODE_TYPES as AST_NODE_TYPES5 } from "@typescript-eslint/utils";
792
+ import { AST_NODE_TYPES as AST_NODE_TYPES4 } from "@typescript-eslint/utils";
862
793
  var REF_RE = /https?:\/\/|\bRFC[- ]?\d+|\bPEP[- ]?\d+|\bCVE-\d{4}|\b(?!UTF-|SHA-|ISO-|AES-|CRC-|MD-|PCM-|EOF-|API-|BASE-)[A-Z][A-Z0-9]{1,9}-\d[A-Z0-9]{0,5}\b|(?<![&\w])#\d{2,6}\b|@[a-z][\w.-]*\.(?:us|com|ai|io|net|org|dev)\b/;
863
794
  var VERSION_RE = /(?:>=|<=|==|<|>)\s*v?\d+\.\d+|\bv\d+\.\d+|\b(?:since|until|as of)\s+(?:v?\d+\.\d+|Python\s*\d)/i;
864
795
  var UNITS_RE = /[~<>]?\d+(?:\.\d+)?\s?(?:ms|s\b|sec\b|seconds?\b|min\b|minutes?\b|hours?\b|days?\b|KB|MB|MiB|GiB|kHz|Hz|bytes?\b|bit\b|-bit\b|%|px\b|rps\b|qps\b)|\b[1-5]xx\b|\b(?:301|302|304|307|308|400|401|403|404|405|409|410|412|422|425|429|500|501|502|503|504)\b/;
@@ -944,10 +875,10 @@ var NARRATION_MAX_WORDS = 6;
944
875
  var NARRATION_MIN_CONTENT = 1;
945
876
  var TOKEN_PLURAL_MIN = 4;
946
877
  var RESTATABLE_STATEMENTS = /* @__PURE__ */ new Set([
947
- AST_NODE_TYPES5.ExpressionStatement,
948
- AST_NODE_TYPES5.ReturnStatement,
949
- AST_NODE_TYPES5.ThrowStatement,
950
- AST_NODE_TYPES5.VariableDeclaration
878
+ AST_NODE_TYPES4.ExpressionStatement,
879
+ AST_NODE_TYPES4.ReturnStatement,
880
+ AST_NODE_TYPES4.ThrowStatement,
881
+ AST_NODE_TYPES4.VariableDeclaration
951
882
  ]);
952
883
  var NARRATION_VERB_RE = /^(?:add|append|assign|build|calculate|call|check|clear|close|compute|convert|copy|count|create|declare|decrement|define|delete|extract|fetch|filter|find|format|generate|get|handle|increment|init|initialise|initialize|insert|iterate|join|load|log|loop|make|map|merge|open|parse|print|process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|start|stop|store|update|validate|wrap|write)(?:s|es|d|ed|ing)?$/i;
953
884
  var NARRATION_STOPWORDS = /* @__PURE__ */ new Set([
@@ -1002,12 +933,12 @@ function normalizeToken(word) {
1002
933
  function restatableStatementBelow(comment, sourceCode) {
1003
934
  const token = sourceCode.getTokenAfter(comment, { includeComments: false });
1004
935
  if (token === null || token.loc.start.line !== comment.loc.end.line + 1) return null;
1005
- for (let node = sourceCode.getNodeByRangeIndex(token.range[0]); node != null && node.type !== AST_NODE_TYPES5.Program; node = node.parent) {
936
+ for (let node = sourceCode.getNodeByRangeIndex(token.range[0]); node != null && node.type !== AST_NODE_TYPES4.Program; node = node.parent) {
1006
937
  if (!RESTATABLE_STATEMENTS.has(node.type)) continue;
1007
938
  if (node.loc.start.line !== token.loc.start.line || node.loc.end.line !== node.loc.start.line) {
1008
939
  return null;
1009
940
  }
1010
- if (node.type === AST_NODE_TYPES5.VariableDeclaration && isTrivialInitializer(node)) {
941
+ if (node.type === AST_NODE_TYPES4.VariableDeclaration && isTrivialInitializer(node)) {
1011
942
  return null;
1012
943
  }
1013
944
  return sourceCode.getText(node);
@@ -1017,8 +948,8 @@ function restatableStatementBelow(comment, sourceCode) {
1017
948
  function isTrivialInitializer(node) {
1018
949
  return node.declarations.every((declarator) => {
1019
950
  const init = declarator.init;
1020
- if (init == null || init.type === AST_NODE_TYPES5.Literal) return true;
1021
- return init.type === AST_NODE_TYPES5.ArrayExpression && init.elements.length === 0 || init.type === AST_NODE_TYPES5.ObjectExpression && init.properties.length === 0;
951
+ if (init == null || init.type === AST_NODE_TYPES4.Literal) return true;
952
+ return init.type === AST_NODE_TYPES4.ArrayExpression && init.elements.length === 0 || init.type === AST_NODE_TYPES4.ObjectExpression && init.properties.length === 0;
1022
953
  });
1023
954
  }
1024
955
  function restatesStatementHead(body2, statement) {
@@ -1099,6 +1030,7 @@ var STEP_NARRATION_RE = /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|a
1099
1030
  var META_COMMENTARY_RE = /\b(?:keeping (?:it|this) simple|could be (?:refactored|improved|cleaned up|simplified)|refactor(?:ed|ing)? (?:later|this)|not sure (?:if|whether|why|how)|quick[- ](?:and[- ]dirty|fix)|(?:a |bit of a )?hacky|is a hack|temporary (?:solution|workaround|fix|hack)|revisit (?:this|later|below)|clean (?:this|it) up|not ideal|placeholder for now)\b/i;
1100
1031
  var EDITORIAL_PLACEHOLDER_RE = /^(?:(?:implementation omitted|existing code here|your code here|rest of (?:the )?code (?:is )?unchanged|same as above|placeholder implementation)\s*[.!]?|in a real (?:app(?:lication)?|implementation),?\s+(?:this|we|you|it)\s+would\s+(?:call|fetch|generate|download|persist|save|send|store|write)\b[^,;]*[.!]?)$/i;
1101
1032
  var FOR_NOW_RE = /\bfor now\b/i;
1033
+ var JSDOC_DEBT_RE = /^@?(?:todo|fixme)\b/i;
1102
1034
  var DEFERRAL_STOPWORDS = /* @__PURE__ */ new Set([
1103
1035
  "a",
1104
1036
  "an",
@@ -1227,6 +1159,7 @@ var DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;
1227
1159
  var CODE_KEYWORD_RE = /^(import |export |const |let |var |function\b|class |interface |type \w|enum |return\b|throw |await |async |if\s*\(|for\s*\(|while\s*\(|switch\s*\(|new |console\.)/;
1228
1160
  var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
1229
1161
  var ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$/;
1162
+ var DECLARATION_RE = /^(?:export\s+)?(?:declare\s+)?(?:const|let|var)\s+[A-Za-z_$][\w$]*(?:\s*:\s*[^=]+)?\s*=\s*(?:[A-Za-z_$][\w.$]*(?:\s*\(|\s*$)|["'`]|\[|\{|\d|true\b|false\b|null\b|undefined\b|new\b|await\b|async\b|function\b|class\b)/;
1230
1163
  var CALL_RE = /^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
1231
1164
  var ASSERTION_CODE_RE = /^(?:await\s+)?(?:expect(?:TypeOf)?|assert(?:\.\w+)?)\s*\(/;
1232
1165
  var HTTP_CONTRACT_RE = /\b(?:GET|HEAD|OPTIONS|PATCH|POST|PUT|DELETE)\s+(?:https?:\/\/|\/|\{[A-Za-z_$])/;
@@ -1254,6 +1187,7 @@ function looksLikeCode(text, allowCall = true) {
1254
1187
  if (!t) return false;
1255
1188
  if (PROSE_ASSIGNMENT_RE.test(t)) return false;
1256
1189
  if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;
1190
+ if (DECLARATION_RE.test(t)) return true;
1257
1191
  if (ASSIGN_RE.test(t)) return true;
1258
1192
  if (ASSERTION_CODE_RE.test(t)) return true;
1259
1193
  return allowCall && CALL_RE.test(t);
@@ -1317,13 +1251,13 @@ function restatesWholeStatement(body2, statement) {
1317
1251
  return statement !== null && restatesStatementHead(body2, statement.replaceAll("(", " "));
1318
1252
  }
1319
1253
  var STATEMENT_CONTAINERS = /* @__PURE__ */ new Set([
1320
- AST_NODE_TYPES6.Program,
1321
- AST_NODE_TYPES6.BlockStatement,
1322
- AST_NODE_TYPES6.ClassBody,
1323
- AST_NODE_TYPES6.StaticBlock,
1324
- AST_NODE_TYPES6.SwitchCase,
1325
- AST_NODE_TYPES6.TSModuleBlock,
1326
- AST_NODE_TYPES6.TSInterfaceBody
1254
+ AST_NODE_TYPES5.Program,
1255
+ AST_NODE_TYPES5.BlockStatement,
1256
+ AST_NODE_TYPES5.ClassBody,
1257
+ AST_NODE_TYPES5.StaticBlock,
1258
+ AST_NODE_TYPES5.SwitchCase,
1259
+ AST_NODE_TYPES5.TSModuleBlock,
1260
+ AST_NODE_TYPES5.TSInterfaceBody
1327
1261
  ]);
1328
1262
  function statementAttachmentBelow(comment, sourceCode) {
1329
1263
  const token = sourceCode.getTokenAfter(comment, { includeComments: false });
@@ -1341,30 +1275,30 @@ function statementAttachmentBelow(comment, sourceCode) {
1341
1275
  return null;
1342
1276
  }
1343
1277
  var WALL_STATEMENTS = /* @__PURE__ */ new Set([
1344
- AST_NODE_TYPES6.ExpressionStatement,
1345
- AST_NODE_TYPES6.ReturnStatement,
1346
- AST_NODE_TYPES6.ThrowStatement,
1347
- AST_NODE_TYPES6.VariableDeclaration,
1348
- AST_NODE_TYPES6.IfStatement,
1349
- AST_NODE_TYPES6.ForStatement,
1350
- AST_NODE_TYPES6.ForOfStatement,
1351
- AST_NODE_TYPES6.ForInStatement,
1352
- AST_NODE_TYPES6.WhileStatement,
1353
- AST_NODE_TYPES6.DoWhileStatement,
1354
- AST_NODE_TYPES6.SwitchStatement,
1355
- AST_NODE_TYPES6.TryStatement
1278
+ AST_NODE_TYPES5.ExpressionStatement,
1279
+ AST_NODE_TYPES5.ReturnStatement,
1280
+ AST_NODE_TYPES5.ThrowStatement,
1281
+ AST_NODE_TYPES5.VariableDeclaration,
1282
+ AST_NODE_TYPES5.IfStatement,
1283
+ AST_NODE_TYPES5.ForStatement,
1284
+ AST_NODE_TYPES5.ForOfStatement,
1285
+ AST_NODE_TYPES5.ForInStatement,
1286
+ AST_NODE_TYPES5.WhileStatement,
1287
+ AST_NODE_TYPES5.DoWhileStatement,
1288
+ AST_NODE_TYPES5.SwitchStatement,
1289
+ AST_NODE_TYPES5.TryStatement
1356
1290
  ]);
1357
1291
  function directStatements(container) {
1358
1292
  switch (container.type) {
1359
- case AST_NODE_TYPES6.Program:
1360
- case AST_NODE_TYPES6.BlockStatement:
1361
- case AST_NODE_TYPES6.ClassBody:
1362
- case AST_NODE_TYPES6.StaticBlock:
1363
- case AST_NODE_TYPES6.TSModuleBlock:
1293
+ case AST_NODE_TYPES5.Program:
1294
+ case AST_NODE_TYPES5.BlockStatement:
1295
+ case AST_NODE_TYPES5.ClassBody:
1296
+ case AST_NODE_TYPES5.StaticBlock:
1297
+ case AST_NODE_TYPES5.TSModuleBlock:
1364
1298
  return container.body;
1365
- case AST_NODE_TYPES6.SwitchCase:
1299
+ case AST_NODE_TYPES5.SwitchCase:
1366
1300
  return container.consequent;
1367
- case AST_NODE_TYPES6.TSInterfaceBody:
1301
+ case AST_NODE_TYPES5.TSInterfaceBody:
1368
1302
  return container.body;
1369
1303
  default:
1370
1304
  return [];
@@ -1383,8 +1317,8 @@ function isWeakWalkthroughComment(body2, statement) {
1383
1317
  return matched / described.length >= 0.5 && described.length - matched <= WALL_MAX_NOVEL_WORDS;
1384
1318
  }
1385
1319
  var TYPE_MEMBER_CONTAINERS = /* @__PURE__ */ new Set([
1386
- AST_NODE_TYPES6.TSInterfaceBody,
1387
- AST_NODE_TYPES6.TSTypeLiteral
1320
+ AST_NODE_TYPES5.TSInterfaceBody,
1321
+ AST_NODE_TYPES5.TSTypeLiteral
1388
1322
  ]);
1389
1323
  function runCitesAReference(comments, index) {
1390
1324
  for (let i = index; i >= 0; i--) {
@@ -1478,10 +1412,10 @@ var no_comment_cruft_default = createRule({
1478
1412
  for (let node = sourceCode.getNodeByRangeIndex(
1479
1413
  comment.range[0]
1480
1414
  ); node != null; node = node.parent) {
1481
- if (node.type === AST_NODE_TYPES6.JSXExpressionContainer) {
1482
- return node.expression.type === AST_NODE_TYPES6.JSXEmptyExpression;
1415
+ if (node.type === AST_NODE_TYPES5.JSXExpressionContainer) {
1416
+ return node.expression.type === AST_NODE_TYPES5.JSXEmptyExpression;
1483
1417
  }
1484
- if (node.type === AST_NODE_TYPES6.Program) return false;
1418
+ if (node.type === AST_NODE_TYPES5.Program) return false;
1485
1419
  }
1486
1420
  return false;
1487
1421
  }
@@ -1583,6 +1517,11 @@ var no_comment_cruft_default = createRule({
1583
1517
  }
1584
1518
  if (wallMembers.has(comment)) continue;
1585
1519
  if (isJsDoc(comment)) {
1520
+ const debt = comment.value.split("\n").map(stripCommentMarker).find((line) => JSDOC_DEBT_RE.test(line));
1521
+ if (debt !== void 0 && !runCitesAReference(comments, i)) {
1522
+ context.report({ node: comment, messageId: "untrackedTodo" });
1523
+ continue;
1524
+ }
1586
1525
  if (isStandalone(comment) && isSectionJsDoc(comment)) {
1587
1526
  context.report({ node: comment, messageId: "sectionBanner" });
1588
1527
  }
@@ -1648,7 +1587,7 @@ var no_comment_cruft_default = createRule({
1648
1587
  });
1649
1588
 
1650
1589
  // src/rules/no-conditional-in-test.ts
1651
- import { AST_NODE_TYPES as AST_NODE_TYPES7 } from "@typescript-eslint/utils";
1590
+ import { AST_NODE_TYPES as AST_NODE_TYPES6 } from "@typescript-eslint/utils";
1652
1591
  var noConditionalInTestDocumentation = {
1653
1592
  summary: "Disallow test conditionals that can skip a runtime assertion or exit the test before one runs.",
1654
1593
  rationale: "A branch can skip the assertion that gives a test its meaning, allowing unexpected inputs to pass silently.",
@@ -1687,9 +1626,9 @@ var NON_TEST_MEMBERS = /* @__PURE__ */ new Set([
1687
1626
  "describe"
1688
1627
  ]);
1689
1628
  var FUNCTION_TYPES2 = /* @__PURE__ */ new Set([
1690
- AST_NODE_TYPES7.FunctionDeclaration,
1691
- AST_NODE_TYPES7.FunctionExpression,
1692
- AST_NODE_TYPES7.ArrowFunctionExpression
1629
+ AST_NODE_TYPES6.FunctionDeclaration,
1630
+ AST_NODE_TYPES6.FunctionExpression,
1631
+ AST_NODE_TYPES6.ArrowFunctionExpression
1693
1632
  ]);
1694
1633
  var ASSERTION_ROOTS = /* @__PURE__ */ new Set([
1695
1634
  "expect",
@@ -1710,38 +1649,38 @@ function nearestEnclosingFunction(node) {
1710
1649
  return null;
1711
1650
  }
1712
1651
  function testCallerName(callee) {
1713
- if (callee.type === AST_NODE_TYPES7.Identifier) {
1652
+ if (callee.type === AST_NODE_TYPES6.Identifier) {
1714
1653
  return callee.name;
1715
1654
  }
1716
- if (callee.type === AST_NODE_TYPES7.MemberExpression) {
1655
+ if (callee.type === AST_NODE_TYPES6.MemberExpression) {
1717
1656
  return testCallerName(callee.object);
1718
1657
  }
1719
- if (callee.type === AST_NODE_TYPES7.CallExpression) {
1658
+ if (callee.type === AST_NODE_TYPES6.CallExpression) {
1720
1659
  return testCallerName(callee.callee);
1721
1660
  }
1722
- if (callee.type === AST_NODE_TYPES7.TaggedTemplateExpression) {
1661
+ if (callee.type === AST_NODE_TYPES6.TaggedTemplateExpression) {
1723
1662
  return testCallerName(callee.tag);
1724
1663
  }
1725
1664
  return null;
1726
1665
  }
1727
1666
  function hasNonTestMember(callee) {
1728
- if (callee.type === AST_NODE_TYPES7.MemberExpression) {
1729
- if (!callee.computed && callee.property.type === AST_NODE_TYPES7.Identifier && NON_TEST_MEMBERS.has(callee.property.name)) {
1667
+ if (callee.type === AST_NODE_TYPES6.MemberExpression) {
1668
+ if (!callee.computed && callee.property.type === AST_NODE_TYPES6.Identifier && NON_TEST_MEMBERS.has(callee.property.name)) {
1730
1669
  return true;
1731
1670
  }
1732
1671
  return hasNonTestMember(callee.object);
1733
1672
  }
1734
- if (callee.type === AST_NODE_TYPES7.CallExpression) {
1673
+ if (callee.type === AST_NODE_TYPES6.CallExpression) {
1735
1674
  return hasNonTestMember(callee.callee);
1736
1675
  }
1737
- if (callee.type === AST_NODE_TYPES7.TaggedTemplateExpression) {
1676
+ if (callee.type === AST_NODE_TYPES6.TaggedTemplateExpression) {
1738
1677
  return hasNonTestMember(callee.tag);
1739
1678
  }
1740
1679
  return false;
1741
1680
  }
1742
1681
  function isTestBody(fn) {
1743
1682
  const call = fn.parent;
1744
- if (call?.type !== AST_NODE_TYPES7.CallExpression || !call.arguments.some((argument) => argument === fn)) {
1683
+ if (call?.type !== AST_NODE_TYPES6.CallExpression || !call.arguments.some((argument) => argument === fn)) {
1745
1684
  return false;
1746
1685
  }
1747
1686
  if (hasNonTestMember(call.callee)) {
@@ -1787,22 +1726,22 @@ function subtreeMatches(node, predicate, descendIntoFunctions = true) {
1787
1726
  }
1788
1727
  function rootIdentifier2(node) {
1789
1728
  switch (node.type) {
1790
- case AST_NODE_TYPES7.Identifier:
1729
+ case AST_NODE_TYPES6.Identifier:
1791
1730
  return node.name;
1792
- case AST_NODE_TYPES7.MemberExpression:
1731
+ case AST_NODE_TYPES6.MemberExpression:
1793
1732
  return rootIdentifier2(node.object);
1794
- case AST_NODE_TYPES7.UnaryExpression:
1733
+ case AST_NODE_TYPES6.UnaryExpression:
1795
1734
  return rootIdentifier2(node.argument);
1796
- case AST_NODE_TYPES7.AwaitExpression:
1735
+ case AST_NODE_TYPES6.AwaitExpression:
1797
1736
  return rootIdentifier2(node.argument);
1798
- case AST_NODE_TYPES7.ChainExpression:
1799
- case AST_NODE_TYPES7.TSNonNullExpression:
1800
- case AST_NODE_TYPES7.TSAsExpression:
1737
+ case AST_NODE_TYPES6.ChainExpression:
1738
+ case AST_NODE_TYPES6.TSNonNullExpression:
1739
+ case AST_NODE_TYPES6.TSAsExpression:
1801
1740
  return rootIdentifier2(node.expression);
1802
- case AST_NODE_TYPES7.BinaryExpression:
1803
- case AST_NODE_TYPES7.LogicalExpression:
1741
+ case AST_NODE_TYPES6.BinaryExpression:
1742
+ case AST_NODE_TYPES6.LogicalExpression:
1804
1743
  return rootIdentifier2(node.left);
1805
- case AST_NODE_TYPES7.CallExpression:
1744
+ case AST_NODE_TYPES6.CallExpression:
1806
1745
  return rootIdentifier2(node.callee);
1807
1746
  default:
1808
1747
  return null;
@@ -1811,26 +1750,26 @@ function rootIdentifier2(node) {
1811
1750
  function calleeRootName(call) {
1812
1751
  let current = call.callee;
1813
1752
  for (; ; ) {
1814
- if (current.type === AST_NODE_TYPES7.Identifier) {
1753
+ if (current.type === AST_NODE_TYPES6.Identifier) {
1815
1754
  return current.name;
1816
1755
  }
1817
- if (current.type === AST_NODE_TYPES7.MemberExpression) {
1756
+ if (current.type === AST_NODE_TYPES6.MemberExpression) {
1818
1757
  current = current.object;
1819
1758
  continue;
1820
1759
  }
1821
- if (current.type === AST_NODE_TYPES7.CallExpression) {
1760
+ if (current.type === AST_NODE_TYPES6.CallExpression) {
1822
1761
  current = current.callee;
1823
1762
  continue;
1824
1763
  }
1825
- if (current.type === AST_NODE_TYPES7.ChainExpression || current.type === AST_NODE_TYPES7.TSNonNullExpression) {
1764
+ if (current.type === AST_NODE_TYPES6.ChainExpression || current.type === AST_NODE_TYPES6.TSNonNullExpression) {
1826
1765
  current = current.expression;
1827
1766
  continue;
1828
1767
  }
1829
1768
  return null;
1830
1769
  }
1831
1770
  }
1832
- var isAssertionCall = (node) => node.type === AST_NODE_TYPES7.CallExpression && ASSERTION_ROOTS.has(calleeRootName(node) ?? "");
1833
- var isTypeAssertionCall = (node) => node.type === AST_NODE_TYPES7.CallExpression && TYPE_ASSERTION_ROOTS.has(calleeRootName(node) ?? "");
1771
+ var isAssertionCall = (node) => node.type === AST_NODE_TYPES6.CallExpression && ASSERTION_ROOTS.has(calleeRootName(node) ?? "");
1772
+ var isTypeAssertionCall = (node) => node.type === AST_NODE_TYPES6.CallExpression && TYPE_ASSERTION_ROOTS.has(calleeRootName(node) ?? "");
1834
1773
  var containsAssertion = (node) => subtreeMatches(node, isAssertionCall);
1835
1774
  var containsRuntimeAssertion = (node) => subtreeMatches(
1836
1775
  node,
@@ -1838,22 +1777,22 @@ var containsRuntimeAssertion = (node) => subtreeMatches(
1838
1777
  );
1839
1778
  var containsSkipCall = (node) => subtreeMatches(
1840
1779
  node,
1841
- (current) => current.type === AST_NODE_TYPES7.CallExpression && current.callee.type === AST_NODE_TYPES7.MemberExpression && !current.callee.computed && current.callee.property.type === AST_NODE_TYPES7.Identifier && current.callee.property.name === "skip"
1780
+ (current) => current.type === AST_NODE_TYPES6.CallExpression && current.callee.type === AST_NODE_TYPES6.MemberExpression && !current.callee.computed && current.callee.property.type === AST_NODE_TYPES6.Identifier && current.callee.property.name === "skip"
1842
1781
  );
1843
1782
  var containsEscape = (node) => subtreeMatches(
1844
1783
  node,
1845
- (current) => current.type === AST_NODE_TYPES7.ReturnStatement || current.type === AST_NODE_TYPES7.ContinueStatement || current.type === AST_NODE_TYPES7.BreakStatement || current.type === AST_NODE_TYPES7.ThrowStatement,
1784
+ (current) => current.type === AST_NODE_TYPES6.ReturnStatement || current.type === AST_NODE_TYPES6.ContinueStatement || current.type === AST_NODE_TYPES6.BreakStatement || current.type === AST_NODE_TYPES6.ThrowStatement,
1846
1785
  false
1847
1786
  );
1848
1787
  function branchStatements(branch) {
1849
- return branch.type === AST_NODE_TYPES7.BlockStatement ? branch.body : [branch];
1788
+ return branch.type === AST_NODE_TYPES6.BlockStatement ? branch.body : [branch];
1850
1789
  }
1851
1790
  function previousSibling(node) {
1852
1791
  const parent = node.parent;
1853
1792
  let siblings = null;
1854
- if (parent.type === AST_NODE_TYPES7.BlockStatement || parent.type === AST_NODE_TYPES7.Program || parent.type === AST_NODE_TYPES7.StaticBlock) {
1793
+ if (parent.type === AST_NODE_TYPES6.BlockStatement || parent.type === AST_NODE_TYPES6.Program || parent.type === AST_NODE_TYPES6.StaticBlock) {
1855
1794
  siblings = parent.body;
1856
- } else if (parent.type === AST_NODE_TYPES7.SwitchCase) {
1795
+ } else if (parent.type === AST_NODE_TYPES6.SwitchCase) {
1857
1796
  siblings = parent.consequent;
1858
1797
  }
1859
1798
  if (siblings === null) {
@@ -1868,12 +1807,12 @@ function isPinnedNarrowingGuard(node) {
1868
1807
  return false;
1869
1808
  }
1870
1809
  const previous = previousSibling(node);
1871
- if (previous === null || previous.type !== AST_NODE_TYPES7.ExpressionStatement) {
1810
+ if (previous === null || previous.type !== AST_NODE_TYPES6.ExpressionStatement) {
1872
1811
  return false;
1873
1812
  }
1874
1813
  let matched = false;
1875
1814
  subtreeMatches(previous, (current) => {
1876
- if (current.type !== AST_NODE_TYPES7.CallExpression || !ASSERTION_ROOTS.has(calleeRootName(current) ?? "")) {
1815
+ if (current.type !== AST_NODE_TYPES6.CallExpression || !ASSERTION_ROOTS.has(calleeRootName(current) ?? "")) {
1877
1816
  return false;
1878
1817
  }
1879
1818
  const subject = current.arguments[0];
@@ -1893,12 +1832,12 @@ function isThrowingGuard(node) {
1893
1832
  return false;
1894
1833
  }
1895
1834
  const statements = branchStatements(node.consequent);
1896
- return statements.length === 1 && statements[0]?.type === AST_NODE_TYPES7.ThrowStatement;
1835
+ return statements.length === 1 && statements[0]?.type === AST_NODE_TYPES6.ThrowStatement;
1897
1836
  }
1898
1837
  function isTypeAssertionBranch(branch) {
1899
1838
  const statements = branchStatements(branch);
1900
1839
  return statements.length > 0 && statements.every(
1901
- (statement) => statement.type === AST_NODE_TYPES7.ExpressionStatement && isTypeAssertionCall(statement.expression)
1840
+ (statement) => statement.type === AST_NODE_TYPES6.ExpressionStatement && isTypeAssertionCall(statement.expression)
1902
1841
  );
1903
1842
  }
1904
1843
  function isTypeLevelNarrowing(node) {
@@ -1906,17 +1845,17 @@ function isTypeLevelNarrowing(node) {
1906
1845
  }
1907
1846
  var isInertBranch = (branch) => !containsAssertion(branch) && !containsEscape(branch) && !containsSkipCall(branch);
1908
1847
  function isNormalizationStatement(statement) {
1909
- if (statement.type === AST_NODE_TYPES7.VariableDeclaration) {
1848
+ if (statement.type === AST_NODE_TYPES6.VariableDeclaration) {
1910
1849
  return true;
1911
1850
  }
1912
- if (statement.type === AST_NODE_TYPES7.BlockStatement) {
1851
+ if (statement.type === AST_NODE_TYPES6.BlockStatement) {
1913
1852
  return statement.body.every(isNormalizationStatement);
1914
1853
  }
1915
- if (statement.type !== AST_NODE_TYPES7.ExpressionStatement) {
1854
+ if (statement.type !== AST_NODE_TYPES6.ExpressionStatement) {
1916
1855
  return false;
1917
1856
  }
1918
1857
  const { expression } = statement;
1919
- return expression.type === AST_NODE_TYPES7.AssignmentExpression || expression.type === AST_NODE_TYPES7.UpdateExpression || expression.type === AST_NODE_TYPES7.UnaryExpression && expression.operator === "delete";
1858
+ return expression.type === AST_NODE_TYPES6.AssignmentExpression || expression.type === AST_NODE_TYPES6.UpdateExpression || expression.type === AST_NODE_TYPES6.UnaryExpression && expression.operator === "delete";
1920
1859
  }
1921
1860
  function isInertNormalization(node) {
1922
1861
  const branches = [node.consequent, node.alternate].filter(
@@ -1954,7 +1893,7 @@ function conditionalSkipsAssertion(node) {
1954
1893
  return consequent !== alternate;
1955
1894
  }
1956
1895
  function isShortCircuitedAssertion(node) {
1957
- return node.operator !== "??" && node.parent.type === AST_NODE_TYPES7.ExpressionStatement && containsAssertion(node.right);
1896
+ return node.operator !== "??" && node.parent.type === AST_NODE_TYPES6.ExpressionStatement && containsAssertion(node.right);
1958
1897
  }
1959
1898
  var no_conditional_in_test_default = createRule({
1960
1899
  name: "no-conditional-in-test",
@@ -2005,7 +1944,10 @@ var no_conditional_in_test_default = createRule({
2005
1944
  });
2006
1945
 
2007
1946
  // src/rules/no-cors-wildcard-with-credentials.ts
2008
- import "@typescript-eslint/utils";
1947
+ import {
1948
+ AST_NODE_TYPES as AST_NODE_TYPES7,
1949
+ ASTUtils as ASTUtils2
1950
+ } from "@typescript-eslint/utils";
2009
1951
  var noCorsWildcardWithCredentialsDocumentation = {
2010
1952
  summary: "Disallow wildcard CORS origins when credentials are enabled.",
2011
1953
  rationale: "Reflecting every origin while allowing credentials can let an untrusted site read authenticated cross-origin responses.",
@@ -2192,12 +2134,44 @@ var no_cors_wildcard_with_credentials_default = createRule({
2192
2134
  defaultOptions: [],
2193
2135
  create(context) {
2194
2136
  const scopeHeaderSets = /* @__PURE__ */ new Map();
2137
+ const variableIds = /* @__PURE__ */ new WeakMap();
2138
+ let nextVariableId = 0;
2139
+ function variableId(variable) {
2140
+ const existing = variableIds.get(variable);
2141
+ if (existing !== void 0) return existing;
2142
+ const value = nextVariableId++;
2143
+ variableIds.set(variable, value);
2144
+ return value;
2145
+ }
2146
+ function receiverIdentity(node) {
2147
+ if (node.type === AST_NODE_TYPES7.Identifier) {
2148
+ const variable = ASTUtils2.findVariable(
2149
+ context.sourceCode.getScope(node),
2150
+ node.name
2151
+ );
2152
+ return variable === null ? `global:${node.name}` : `variable:${variableId(variable)}`;
2153
+ }
2154
+ if (node.type === AST_NODE_TYPES7.ThisExpression) return "this";
2155
+ if (node.type === AST_NODE_TYPES7.MemberExpression && !node.computed && node.property.type === AST_NODE_TYPES7.Identifier) {
2156
+ const owner = receiverIdentity(node.object);
2157
+ return owner === null ? null : `${owner}.${node.property.name}`;
2158
+ }
2159
+ return null;
2160
+ }
2195
2161
  function recordHeaderSet(node, kind) {
2162
+ if (node.callee.type !== AST_NODE_TYPES7.MemberExpression) return;
2163
+ const receiver = receiverIdentity(node.callee.object);
2164
+ if (receiver === null) return;
2196
2165
  const key = enclosingScope(node) ?? "module";
2197
- let entry = scopeHeaderSets.get(key);
2166
+ let receivers = scopeHeaderSets.get(key);
2167
+ if (receivers === void 0) {
2168
+ receivers = /* @__PURE__ */ new Map();
2169
+ scopeHeaderSets.set(key, receivers);
2170
+ }
2171
+ let entry = receivers.get(receiver);
2198
2172
  if (entry === void 0) {
2199
2173
  entry = { originNodes: [], credentialsNodes: [] };
2200
- scopeHeaderSets.set(key, entry);
2174
+ receivers.set(receiver, entry);
2201
2175
  }
2202
2176
  if (kind === "origin") {
2203
2177
  entry.originNodes.push(node);
@@ -2227,13 +2201,15 @@ var no_cors_wildcard_with_credentials_default = createRule({
2227
2201
  }
2228
2202
  },
2229
2203
  "Program:exit"() {
2230
- for (const { originNodes, credentialsNodes } of scopeHeaderSets.values()) {
2231
- if (originNodes.length > 0 && credentialsNodes.length > 0) {
2232
- for (const node of originNodes) {
2233
- context.report({
2234
- node,
2235
- messageId: "corsWildcardWithCredentials"
2236
- });
2204
+ for (const receivers of scopeHeaderSets.values()) {
2205
+ for (const { originNodes, credentialsNodes } of receivers.values()) {
2206
+ if (originNodes.length > 0 && credentialsNodes.length > 0) {
2207
+ for (const node of originNodes) {
2208
+ context.report({
2209
+ node,
2210
+ messageId: "corsWildcardWithCredentials"
2211
+ });
2212
+ }
2237
2213
  }
2238
2214
  }
2239
2215
  }
@@ -3255,7 +3231,7 @@ var noHandRolledSpinnerDocumentation = {
3255
3231
  };
3256
3232
  var DESIGN_SYSTEM_PATH = /(?:^|[/\\])components[/\\]ui[/\\]/u;
3257
3233
  var BORDER_WIDTH = /^border(?:-[0-9]+)?$/u;
3258
- var TRANSPARENT_EDGE = /^border-[trbl]-transparent$/u;
3234
+ var CONTRASTING_EDGE = /^border-[trbl]-(?!0$|[0-9]+$).+/u;
3259
3235
  function staticClassName(attribute) {
3260
3236
  const value = attribute.value;
3261
3237
  if (value?.type === AST_NODE_TYPES12.Literal && typeof value.value === "string") {
@@ -3264,6 +3240,9 @@ function staticClassName(attribute) {
3264
3240
  if (value?.type === AST_NODE_TYPES12.JSXExpressionContainer && value.expression.type === AST_NODE_TYPES12.Literal && typeof value.expression.value === "string") {
3265
3241
  return value.expression.value;
3266
3242
  }
3243
+ if (value?.type === AST_NODE_TYPES12.JSXExpressionContainer && value.expression.type === AST_NODE_TYPES12.TemplateLiteral && value.expression.expressions.length === 0) {
3244
+ return value.expression.quasis[0]?.value.cooked ?? null;
3245
+ }
3267
3246
  return null;
3268
3247
  }
3269
3248
  var no_hand_rolled_spinner_default = createRule({
@@ -3296,7 +3275,7 @@ var no_hand_rolled_spinner_default = createRule({
3296
3275
  const className = staticClassName(classNameAttribute);
3297
3276
  if (className === null) return;
3298
3277
  const classes = className.split(/\s+/u);
3299
- if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some((token) => BORDER_WIDTH.test(token)) && classes.some((token) => TRANSPARENT_EDGE.test(token))) {
3278
+ if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some((token) => BORDER_WIDTH.test(token)) && classes.some((token) => CONTRASTING_EDGE.test(token))) {
3300
3279
  context.report({ node, messageId: "handRolledSpinner" });
3301
3280
  }
3302
3281
  }
@@ -3318,7 +3297,7 @@ var noInsecureRandomIdDocumentation = {
3318
3297
  ]
3319
3298
  };
3320
3299
  var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
3321
- var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
3300
+ var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker|dialog|select|menu|tab|field|input|form|element|dom|aria|component/i;
3322
3301
  var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
3323
3302
  function isMathRandomCall(node) {
3324
3303
  if (node.type !== "CallExpression") {
@@ -4005,7 +3984,7 @@ var no_impossible_zod_literal_bounds_default = createRule({
4005
3984
  });
4006
3985
 
4007
3986
  // src/rules/no-log-only-catch.ts
4008
- import { AST_NODE_TYPES as AST_NODE_TYPES14, ASTUtils as ASTUtils2 } from "@typescript-eslint/utils";
3987
+ import { AST_NODE_TYPES as AST_NODE_TYPES14, ASTUtils as ASTUtils3 } from "@typescript-eslint/utils";
4009
3988
 
4010
3989
  // src/rules/_logging.ts
4011
3990
  import "@typescript-eslint/utils";
@@ -4177,7 +4156,7 @@ function seededFallbackHandled(tryStatement, scope) {
4177
4156
  if (previous.declarations.length !== 1 || declarator === void 0) return false;
4178
4157
  if (declarator.id.type !== AST_NODE_TYPES14.Identifier) return false;
4179
4158
  if (declarator.init == null || !isSeedValue(declarator.init)) return false;
4180
- const variable = ASTUtils2.findVariable(scope, declarator.id.name);
4159
+ const variable = ASTUtils3.findVariable(scope, declarator.id.name);
4181
4160
  if (variable === null) return false;
4182
4161
  const [tryStart, tryEnd] = tryStatement.block.range;
4183
4162
  let writtenInTry = false;
@@ -4391,11 +4370,11 @@ function typedFunction(node) {
4391
4370
 
4392
4371
  // src/rules/no-long-comment.ts
4393
4372
  var noLongCommentDocumentation = {
4394
- summary: "Flag unusually large unstructured prose blocks in implementation code.",
4373
+ summary: "Flag unusually large unstructured JSDoc blocks in implementation code.",
4395
4374
  rationale: "Large narrative comments become stale and obscure the local facts that belong beside the code.",
4396
4375
  remediation: "Keep only durable local constraints and express the remaining behavior in code.",
4397
4376
  category: "maintainability",
4398
- limitations: ["Structured API docs, tests, scripts, generated files, and versioned dependencies are excluded."],
4377
+ limitations: ["Only JSDoc blocks are inspected; structured API docs, tests, scripts, generated files, and versioned dependencies are excluded."],
4399
4378
  examples: [
4400
4379
  { id: "local-fact", title: "Keep a concise local fact", outcome: "no-match", files: [{ path: "src/cache.ts", source: "// The cache is process local.\nconst cache = new Map();" }], focusPath: "src/cache.ts", expectedCount: 0, public: true },
4401
4380
  { id: "prose-wall", title: "Avoid an unstructured prose wall", outcome: "match", files: [{ path: "src/chart.ts", source: "/** One. Two. Three. Four. Five. Six. Seven. Eight. */\nconst chart = createChart();" }], focusPath: "src/chart.ts", expectedCount: 1, public: true }
@@ -4423,7 +4402,7 @@ var no_long_comment_default = createRule({
4423
4402
  documentation: noLongCommentDocumentation,
4424
4403
  meta: {
4425
4404
  type: "suggestion",
4426
- docs: { description: "Flag unusually large unstructured prose blocks in implementation code." },
4405
+ docs: { description: "Flag unusually large unstructured JSDoc blocks in implementation code." },
4427
4406
  schema: [],
4428
4407
  messages: {
4429
4408
  tooLong: "Comment is an unusually large prose block \u2014 keep the local facts and clarify the code itself."
@@ -4445,7 +4424,7 @@ var no_long_comment_default = createRule({
4445
4424
  });
4446
4425
 
4447
4426
  // src/rules/no-generic-single-export-module.ts
4448
- import { AST_NODE_TYPES as AST_NODE_TYPES17, ASTUtils as ASTUtils3 } from "@typescript-eslint/utils";
4427
+ import { AST_NODE_TYPES as AST_NODE_TYPES17, ASTUtils as ASTUtils4 } from "@typescript-eslint/utils";
4449
4428
  var noGenericSingleExportModuleDocumentation = {
4450
4429
  summary: "Disallow generic module stems when one runtime export already names the responsibility.",
4451
4430
  rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
@@ -4606,7 +4585,7 @@ function kebabCase(name) {
4606
4585
  return name.replaceAll(/oauth/giu, "Oauth").replaceAll(/graphql/giu, "Graphql").replaceAll(/grpc/giu, "Grpc").replaceAll(/([a-z\d])([A-Z])/gu, "$1-$2").replaceAll(/([A-Z]+)([A-Z][a-z])/gu, "$1-$2").replaceAll(/[_\s]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "").toLowerCase();
4607
4586
  }
4608
4587
  function isGlobalIdentifier(context, node) {
4609
- const variable = ASTUtils3.findVariable(context.sourceCode.getScope(node), node.name);
4588
+ const variable = ASTUtils4.findVariable(context.sourceCode.getScope(node), node.name);
4610
4589
  return variable === null || variable.defs.length === 0;
4611
4590
  }
4612
4591
  function isConventionalFrameworkUtility(filename, exported) {
@@ -5134,7 +5113,7 @@ var no_raw_env_default = createRule({
5134
5113
  });
5135
5114
 
5136
5115
  // src/rules/no-raw-fetch-outside-clients.ts
5137
- import { AST_NODE_TYPES as AST_NODE_TYPES19 } from "@typescript-eslint/utils";
5116
+ import { AST_NODE_TYPES as AST_NODE_TYPES19, ASTUtils as ASTUtils5 } from "@typescript-eslint/utils";
5138
5117
  var noRawFetchOutsideClientsDocumentation = {
5139
5118
  summary: "Disallow calling the global `fetch` outside the client layer; route outbound HTTP through a client module that owns retry, timeout and status handling.",
5140
5119
  rationale: "Scattered fetch calls bypass shared transport policy and are harder to stub and observe consistently.",
@@ -5166,19 +5145,75 @@ var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
5166
5145
  "self"
5167
5146
  ]);
5168
5147
  var PRESIGNED_URL_NAME_RE = /(?:pre-?signed|signed|upload|download)Url$/i;
5169
- function isGlobalFetchCall(node) {
5148
+ var INTERNAL_MUTATION_METHODS = /* @__PURE__ */ new Set([
5149
+ "POST",
5150
+ "PUT",
5151
+ "DELETE",
5152
+ "PATCH"
5153
+ ]);
5154
+ var ANALYTICS_SEGMENTS2 = /* @__PURE__ */ new Set([
5155
+ "analytics",
5156
+ "telemetry",
5157
+ "track",
5158
+ "log",
5159
+ "ping",
5160
+ "beacon",
5161
+ "metrics",
5162
+ "event"
5163
+ ]);
5164
+ var SERVER_ACTION_SKIP_FILE_RE = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
5165
+ var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
5166
+ function isGlobalFetchCall(node, resolvesToGlobal) {
5170
5167
  const callee = node.callee;
5171
5168
  if (callee.type === "Identifier") {
5172
- return callee.name === "fetch";
5169
+ return callee.name === "fetch" && resolvesToGlobal(callee);
5173
5170
  }
5174
5171
  if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier" && callee.property.name === "fetch" && callee.object.type === "Identifier") {
5175
- return GLOBAL_RECEIVERS.has(callee.object.name);
5172
+ return GLOBAL_RECEIVERS.has(callee.object.name) && resolvesToGlobal(callee.object);
5176
5173
  }
5177
5174
  return false;
5178
5175
  }
5179
- function isConstructedArgumentHandoff(node) {
5176
+ function isConstructedArgumentHandoff(node, resolvesToGlobal) {
5180
5177
  const [first] = node.arguments;
5181
- return node.arguments.length === 1 && first !== void 0 && first.type === AST_NODE_TYPES19.NewExpression;
5178
+ return node.arguments.length === 1 && first !== void 0 && first.type === AST_NODE_TYPES19.NewExpression && first.callee.type === AST_NODE_TYPES19.Identifier && (first.callee.name === "URL" || first.callee.name === "Request") && resolvesToGlobal(first.callee);
5179
+ }
5180
+ function effectOwns(node) {
5181
+ if (node.callee.type !== AST_NODE_TYPES19.Identifier) return false;
5182
+ const method = readDirectMethod(node);
5183
+ if (method !== null && method !== "GET") return false;
5184
+ const first = node.arguments[0];
5185
+ let url = "";
5186
+ if (first?.type === AST_NODE_TYPES19.Literal && typeof first.value === "string") {
5187
+ url = first.value;
5188
+ } else if (first?.type === AST_NODE_TYPES19.TemplateLiteral) {
5189
+ url = first.quasis.map((quasi) => quasi.value.cooked).join("");
5190
+ } else if (first?.type === AST_NODE_TYPES19.Identifier) {
5191
+ url = first.name;
5192
+ }
5193
+ if (url !== "" && url.toLowerCase().split(/[/.]/).some((segment) => ANALYTICS_SEGMENTS2.has(segment))) {
5194
+ return false;
5195
+ }
5196
+ for (let current = node.parent; current != null; current = current.parent) {
5197
+ if (current.type !== AST_NODE_TYPES19.CallExpression) continue;
5198
+ const callee = current.callee;
5199
+ const isEffect = callee.type === AST_NODE_TYPES19.Identifier && (callee.name === "useEffect" || callee.name === "useLayoutEffect") || callee.type === AST_NODE_TYPES19.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES19.Identifier && callee.object.name === "React" && callee.property.type === AST_NODE_TYPES19.Identifier && (callee.property.name === "useEffect" || callee.property.name === "useLayoutEffect");
5200
+ if (!isEffect) continue;
5201
+ const callback = current.arguments[0];
5202
+ return callback !== void 0 && callback.type !== AST_NODE_TYPES19.SpreadElement && node.range[0] >= callback.range[0] && node.range[1] <= callback.range[1];
5203
+ }
5204
+ return false;
5205
+ }
5206
+ function readDirectMethod(node) {
5207
+ const init = node.arguments[1];
5208
+ if (init?.type !== AST_NODE_TYPES19.ObjectExpression) return null;
5209
+ for (const property of init.properties) {
5210
+ if (property.type !== AST_NODE_TYPES19.Property || property.computed) continue;
5211
+ const key = property.key;
5212
+ const isMethod = key.type === AST_NODE_TYPES19.Identifier && key.name === "method" || key.type === AST_NODE_TYPES19.Literal && key.value === "method";
5213
+ if (!isMethod) continue;
5214
+ return property.value.type === AST_NODE_TYPES19.Literal && typeof property.value.value === "string" ? property.value.value.toUpperCase() : null;
5215
+ }
5216
+ return null;
5182
5217
  }
5183
5218
  function isPresignedUrlTransfer(node) {
5184
5219
  const first = node.arguments[0];
@@ -5240,24 +5275,93 @@ var no_raw_fetch_outside_clients_default = createRule({
5240
5275
  }
5241
5276
  const patterns = options?.allow ?? DEFAULT_ALLOW;
5242
5277
  const allowed = compile(patterns);
5278
+ const nonReactFramework = context.sourceCode.ast.body.some(
5279
+ (statement) => statement.type === AST_NODE_TYPES19.ImportDeclaration && typeof statement.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(statement.source.value)
5280
+ );
5281
+ function resolvesToGlobal(identifier) {
5282
+ const variable = ASTUtils5.findVariable(
5283
+ context.sourceCode.getScope(identifier),
5284
+ identifier.name
5285
+ );
5286
+ return variable === null || variable.defs.length === 0;
5287
+ }
5288
+ function resolveNode2(node) {
5289
+ if (node === void 0) return null;
5290
+ if (node.type !== AST_NODE_TYPES19.Identifier) return node;
5291
+ const variable = ASTUtils5.findVariable(
5292
+ context.sourceCode.getScope(node),
5293
+ node.name
5294
+ );
5295
+ if (variable?.defs.length !== 1) return node;
5296
+ const definition = variable.defs[0];
5297
+ return definition?.type === "Variable" && definition.node.init !== null ? definition.node.init : node;
5298
+ }
5299
+ function propertyValue(node, name) {
5300
+ if (node?.type !== AST_NODE_TYPES19.ObjectExpression) return null;
5301
+ for (const property of node.properties) {
5302
+ if (property.type !== AST_NODE_TYPES19.Property || property.computed) continue;
5303
+ const key = property.key;
5304
+ const keyName = key.type === AST_NODE_TYPES19.Identifier ? key.name : key.type === AST_NODE_TYPES19.Literal && typeof key.value === "string" ? key.value : null;
5305
+ if (keyName !== name) continue;
5306
+ return property.value.type === AST_NODE_TYPES19.AssignmentPattern || property.value.type === AST_NODE_TYPES19.ArrayPattern || property.value.type === AST_NODE_TYPES19.ObjectPattern ? null : property.value;
5307
+ }
5308
+ return null;
5309
+ }
5310
+ function isInternalApiUrl(node) {
5311
+ const resolved = resolveNode2(node ?? void 0);
5312
+ if (resolved?.type === AST_NODE_TYPES19.Literal) {
5313
+ return typeof resolved.value === "string" && resolved.value.startsWith("/api/");
5314
+ }
5315
+ if (resolved?.type === AST_NODE_TYPES19.TemplateLiteral) {
5316
+ return resolved.quasis[0]?.value.cooked?.startsWith("/api/") === true;
5317
+ }
5318
+ return resolved?.type === AST_NODE_TYPES19.BinaryExpression && resolved.operator === "+" && isInternalApiUrl(resolved.left);
5319
+ }
5320
+ function isMutationMethod2(node) {
5321
+ const resolved = resolveNode2(node ?? void 0);
5322
+ if (resolved?.type === AST_NODE_TYPES19.Literal) {
5323
+ return typeof resolved.value === "string" && INTERNAL_MUTATION_METHODS.has(resolved.value.toUpperCase());
5324
+ }
5325
+ if (resolved?.type === AST_NODE_TYPES19.TemplateLiteral && resolved.expressions.length === 0) {
5326
+ return INTERNAL_MUTATION_METHODS.has(
5327
+ resolved.quasis.map((quasi) => quasi.value.cooked).join("").toUpperCase()
5328
+ );
5329
+ }
5330
+ if (resolved?.type === AST_NODE_TYPES19.ConditionalExpression) {
5331
+ return isMutationMethod2(resolved.consequent) || isMutationMethod2(resolved.alternate);
5332
+ }
5333
+ return resolved?.type === AST_NODE_TYPES19.LogicalExpression && resolved.operator === "||" && (isMutationMethod2(resolved.left) || isMutationMethod2(resolved.right));
5334
+ }
5335
+ function serverActionOwns(node) {
5336
+ if (node.callee.type !== AST_NODE_TYPES19.Identifier || SERVER_ACTION_SKIP_FILE_RE.test(filename) || nonReactFramework) {
5337
+ return false;
5338
+ }
5339
+ const url = node.arguments[0];
5340
+ const init = node.arguments[1];
5341
+ if (url === void 0 || url.type === AST_NODE_TYPES19.SpreadElement || init === void 0 || init.type === AST_NODE_TYPES19.SpreadElement || !isInternalApiUrl(url)) {
5342
+ return false;
5343
+ }
5344
+ return isMutationMethod2(propertyValue(resolveNode2(init), "method"));
5345
+ }
5243
5346
  if (allowed.some((re) => re.test(filename))) {
5244
5347
  return {};
5245
5348
  }
5246
5349
  return {
5247
5350
  CallExpression(node) {
5248
- if (isPresignedUrlTransfer(node) || isConstructedArgumentHandoff(node)) {
5351
+ if (!isGlobalFetchCall(node, resolvesToGlobal)) {
5249
5352
  return;
5250
5353
  }
5251
- if (isGlobalFetchCall(node)) {
5252
- context.report({ node, messageId: "rawFetch" });
5354
+ if (isPresignedUrlTransfer(node) || isConstructedArgumentHandoff(node, resolvesToGlobal) || effectOwns(node) || serverActionOwns(node)) {
5355
+ return;
5253
5356
  }
5357
+ context.report({ node, messageId: "rawFetch" });
5254
5358
  }
5255
5359
  };
5256
5360
  }
5257
5361
  });
5258
5362
 
5259
5363
  // src/rules/no-restricted-library-load.ts
5260
- import { AST_NODE_TYPES as AST_NODE_TYPES20, ASTUtils as ASTUtils4 } from "@typescript-eslint/utils";
5364
+ import { AST_NODE_TYPES as AST_NODE_TYPES20, ASTUtils as ASTUtils6 } from "@typescript-eslint/utils";
5261
5365
  var noRestrictedLibraryLoadDocumentation = {
5262
5366
  summary: "Apply a configured library-replacement policy to literal dynamic imports, CommonJS loads, and TypeScript import-equals declarations.",
5263
5367
  rationale: "Runtime module loads can bypass the replacement policy enforced for static imports.",
@@ -5330,7 +5434,7 @@ var no_restricted_library_load_default = createRule({
5330
5434
  });
5331
5435
  }
5332
5436
  function isUnshadowedRequire(node) {
5333
- const variable = ASTUtils4.findVariable(
5437
+ const variable = ASTUtils6.findVariable(
5334
5438
  context.sourceCode.getScope(node),
5335
5439
  node.name
5336
5440
  );
@@ -5368,6 +5472,7 @@ var MIN_DISTINCT_SCOPES = 2;
5368
5472
  var PREVIEW_LENGTH = 40;
5369
5473
  var SQL_KEYWORD_RE = /\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|VALUES|ON CONFLICT|RETURNING|GROUP BY|ORDER BY)\b/;
5370
5474
  var IDENTIFIER_RE = /^[a-z_][a-z0-9_.]*$/;
5475
+ var URL_PATH_RE = /^\/(?=[^\s]*[A-Za-z0-9])[A-Za-z0-9._~!$&'()*+,;=:@%/?#{}\u005B\u005D-]+$/;
5371
5476
  var FUNCTION_TYPES4 = /* @__PURE__ */ new Set([
5372
5477
  AST_NODE_TYPES21.FunctionDeclaration,
5373
5478
  AST_NODE_TYPES21.FunctionExpression,
@@ -5375,7 +5480,7 @@ var FUNCTION_TYPES4 = /* @__PURE__ */ new Set([
5375
5480
  ]);
5376
5481
  var noRepeatedStringLiteralDocumentation = {
5377
5482
  summary: "Disallow a long structured string literal repeated across functions; the copies drift when one is edited. Extract a module-level constant.",
5378
- rationale: "Independent copies of a structured value can diverge and silently change behavior.",
5483
+ rationale: "Independent copies of a query, route template, or identifier can diverge and silently change behavior.",
5379
5484
  remediation: "Extract the repeated value to one module-level constant and reference it from each function.",
5380
5485
  category: "maintainability",
5381
5486
  limitations: ["Test files, short strings, prose, substitutions, module sources, JSX attributes, and repetition within one function are excluded."],
@@ -5385,7 +5490,7 @@ var noRepeatedStringLiteralDocumentation = {
5385
5490
  ]
5386
5491
  };
5387
5492
  function isStructured(value) {
5388
- return value.includes("\n") || SQL_KEYWORD_RE.test(value) || IDENTIFIER_RE.test(value);
5493
+ return value.includes("\n") || SQL_KEYWORD_RE.test(value) || IDENTIFIER_RE.test(value) || URL_PATH_RE.test(value);
5389
5494
  }
5390
5495
  function preview(value) {
5391
5496
  const oneLine = value.replaceAll("\n", " ").trim();
@@ -5404,8 +5509,9 @@ function isScaffolding(node) {
5404
5509
  if (parent === void 0) {
5405
5510
  return true;
5406
5511
  }
5512
+ const isNonComputedPropertyKey = (parent.type === AST_NODE_TYPES21.Property || parent.type === AST_NODE_TYPES21.PropertyDefinition || parent.type === AST_NODE_TYPES21.MethodDefinition || parent.type === AST_NODE_TYPES21.AccessorProperty) && parent.key === node && !parent.computed;
5407
5513
  const isRequireSource = parent.type === AST_NODE_TYPES21.CallExpression && parent.callee.type === AST_NODE_TYPES21.Identifier && parent.callee.name === "require";
5408
- return parent.type === AST_NODE_TYPES21.ImportDeclaration || parent.type === AST_NODE_TYPES21.ImportExpression || parent.type === AST_NODE_TYPES21.ExportNamedDeclaration || parent.type === AST_NODE_TYPES21.ExportAllDeclaration || parent.type === AST_NODE_TYPES21.TSImportType || parent.type === AST_NODE_TYPES21.JSXAttribute || parent.type === AST_NODE_TYPES21.TSLiteralType || isRequireSource;
5514
+ return parent.type === AST_NODE_TYPES21.ImportDeclaration || parent.type === AST_NODE_TYPES21.ImportExpression || parent.type === AST_NODE_TYPES21.ExportNamedDeclaration || parent.type === AST_NODE_TYPES21.ExportAllDeclaration || parent.type === AST_NODE_TYPES21.TSImportType || parent.type === AST_NODE_TYPES21.JSXAttribute || parent.type === AST_NODE_TYPES21.TSLiteralType || isNonComputedPropertyKey || isRequireSource;
5409
5515
  }
5410
5516
  var no_repeated_string_literal_default = createRule({
5411
5517
  name: "no-repeated-string-literal",
@@ -5422,7 +5528,7 @@ var no_repeated_string_literal_default = createRule({
5422
5528
  },
5423
5529
  defaultOptions: [],
5424
5530
  create(context) {
5425
- if (isTestFile(context.filename)) {
5531
+ if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
5426
5532
  return {};
5427
5533
  }
5428
5534
  const occurrences = /* @__PURE__ */ new Map();
@@ -5742,10 +5848,14 @@ var no_restated_jsdoc_default = createRule({
5742
5848
  for (const comment of sourceCode.getAllComments()) {
5743
5849
  if (comment.type !== "Block" || !comment.value.startsWith("*")) continue;
5744
5850
  const { description, tags } = parseJsDoc(comment.value);
5745
- if (DIRECTIVE_RE4.test(description)) continue;
5851
+ const describedText = [
5852
+ description,
5853
+ ...tags.filter((tag) => tag.name === "description").map((tag) => tag.text)
5854
+ ].filter((text) => text.length > 0).join("\n");
5855
+ if (DIRECTIVE_RE4.test(describedText)) continue;
5746
5856
  const tagNames = new Set(tags.map((tag) => tag.name));
5747
5857
  if ([...tagNames].some((name) => !MODELLED_TAGS.has(name))) continue;
5748
- if (isProtected(description)) continue;
5858
+ if (isProtected(describedText)) continue;
5749
5859
  const token = sourceCode.getTokenAfter(comment, { includeComments: false });
5750
5860
  if (token === null || token.loc.start.line !== comment.loc.end.line + 1) continue;
5751
5861
  let node = sourceCode.getNodeByRangeIndex(token.range[0]);
@@ -5758,13 +5868,14 @@ var no_restated_jsdoc_default = createRule({
5758
5868
  if (declaration === null) continue;
5759
5869
  const paramTags = tags.filter((tag) => PARAM_TAGS.has(tag.name));
5760
5870
  const returnTags = tags.filter((tag) => RETURN_TAGS.has(tag.name));
5761
- if (description.length === 0 && paramTags.length === 0 && returnTags.length === 0) {
5871
+ if (describedText.length === 0 && paramTags.length === 0 && returnTags.length === 0) {
5762
5872
  continue;
5763
5873
  }
5874
+ if ((paramTags.length > 0 || returnTags.length > 0) && documentsTypedFunction(sourceCode, comment)) continue;
5764
5875
  const nameTokens = tokensOf([declaration.name]);
5765
5876
  const paramTokens = tokensOf(declaration.params);
5766
5877
  const known = /* @__PURE__ */ new Set([...nameTokens, ...paramTokens]);
5767
- let addsNothing = covered(description, known);
5878
+ let addsNothing = covered(describedText, known);
5768
5879
  for (const tag of paramTags) {
5769
5880
  const text = tag.text.replace(/^\{[^}]*\}\s*/, "");
5770
5881
  const match = /^\[?([A-Za-z_$][\w.$]*)\]?\s*-?\s*([\s\S]*)$/.exec(text);
@@ -5772,7 +5883,13 @@ var no_restated_jsdoc_default = createRule({
5772
5883
  addsNothing = false;
5773
5884
  break;
5774
5885
  }
5775
- const own = /* @__PURE__ */ new Set([...splitIdentifier(match[1]?.split(".").pop() ?? ""), ...nameTokens]);
5886
+ const path = match[1] ?? "";
5887
+ const root = path.split(".")[0] ?? "";
5888
+ if (!declaration.params.includes(root)) {
5889
+ addsNothing = false;
5890
+ break;
5891
+ }
5892
+ const own = /* @__PURE__ */ new Set([...splitIdentifier(path.split(".").pop() ?? ""), ...nameTokens]);
5776
5893
  if (!covered(match[2] ?? "", own)) {
5777
5894
  addsNothing = false;
5778
5895
  break;
@@ -6597,10 +6714,10 @@ var no_sentinel_return_on_catch_default = createRule({
6597
6714
  return false;
6598
6715
  }
6599
6716
  if (matcher.isLoggingCall(current)) {
6600
- return true;
6717
+ return caughtName === null || argsIncludeBinding(current.arguments, caughtName);
6601
6718
  }
6602
6719
  const name = calleeName2(current.callee);
6603
- return name !== null && REPORT_NAME_RE.test(name) && argsIncludeBinding(current.arguments, caughtName);
6720
+ return name !== null && REPORT_NAME_RE.test(name) && (caughtName === null || argsIncludeBinding(current.arguments, caughtName));
6604
6721
  });
6605
6722
  }
6606
6723
  return {
@@ -6661,8 +6778,16 @@ var noSilentPromiseCatchDocumentation = {
6661
6778
  { id: "silent-rejection", title: "Do not swallow the rejection", outcome: "match", files: [{ path: "src/load.ts", source: "load().catch(() => null);" }], focusPath: "src/load.ts", expectedCount: 1, public: true }
6662
6779
  ]
6663
6780
  };
6781
+ var BODY_PARSE_METHODS = /* @__PURE__ */ new Set([
6782
+ "arrayBuffer",
6783
+ "blob",
6784
+ "bytes",
6785
+ "formData",
6786
+ "json",
6787
+ "text"
6788
+ ]);
6664
6789
  function isBodyParseCall(node) {
6665
- return node.type === AST_NODE_TYPES25.CallExpression && node.arguments.length === 0 && node.callee.type === AST_NODE_TYPES25.MemberExpression && !node.callee.computed && node.callee.property.type === AST_NODE_TYPES25.Identifier && (node.callee.property.name === "json" || node.callee.property.name === "text");
6790
+ return node.type === AST_NODE_TYPES25.CallExpression && node.arguments.length === 0 && node.callee.type === AST_NODE_TYPES25.MemberExpression && !node.callee.computed && node.callee.property.type === AST_NODE_TYPES25.Identifier && BODY_PARSE_METHODS.has(node.callee.property.name);
6666
6791
  }
6667
6792
  var TEARDOWN_METHODS = /* @__PURE__ */ new Set([
6668
6793
  "cancel",
@@ -8069,7 +8194,7 @@ var no_type_member_comment_wall_default = createRule({
8069
8194
  claimed.add(comment);
8070
8195
  commented += 1;
8071
8196
  const body2 = commentBody(comment);
8072
- if (body2.length === 0 || carriesValue(body2)) continue;
8197
+ if (body2.length === 0 || carriesValue(body2) || isTagsOnly(body2)) continue;
8073
8198
  if (novelWords(body2, knownTokens(sourceCode.getText(member))) <= options.maxNovelWords) {
8074
8199
  restated += 1;
8075
8200
  }
@@ -8315,7 +8440,7 @@ var no_unnecessary_use_client_default = createRule({
8315
8440
  // src/rules/no-unsafe-mock-casting.ts
8316
8441
  import {
8317
8442
  AST_NODE_TYPES as AST_NODE_TYPES34,
8318
- ASTUtils as ASTUtils5
8443
+ ASTUtils as ASTUtils7
8319
8444
  } from "@typescript-eslint/utils";
8320
8445
  var MOCK_TYPE_NAMES = /* @__PURE__ */ new Set([
8321
8446
  "Mock",
@@ -8377,7 +8502,7 @@ var no_unsafe_mock_casting_default = createRule({
8377
8502
  const directBindings = /* @__PURE__ */ new Set();
8378
8503
  const namespaceBindings = /* @__PURE__ */ new Set();
8379
8504
  function resolve(identifier) {
8380
- return ASTUtils5.findVariable(
8505
+ return ASTUtils7.findVariable(
8381
8506
  context.sourceCode.getScope(identifier),
8382
8507
  identifier.name
8383
8508
  );
@@ -8638,7 +8763,7 @@ var no_zod_native_enum_default = createRule({
8638
8763
  });
8639
8764
 
8640
8765
  // src/rules/test-loops-over-literal-cases.ts
8641
- import { AST_NODE_TYPES as AST_NODE_TYPES36, ASTUtils as ASTUtils6 } from "@typescript-eslint/utils";
8766
+ import { AST_NODE_TYPES as AST_NODE_TYPES36, ASTUtils as ASTUtils8 } from "@typescript-eslint/utils";
8642
8767
  var testLoopsOverLiteralCasesDocumentation = {
8643
8768
  summary: "Disallow assertions over an inline literal case loop in a test; parameterization reports and names every case independently.",
8644
8769
  rationale: "A loop is reported as one test, so failures hide the individual case name and may stop later cases from running.",
@@ -8801,7 +8926,7 @@ var test_loops_over_literal_cases_default = createRule({
8801
8926
  return {};
8802
8927
  }
8803
8928
  const isFrameworkIdentifier = (identifier, modules) => {
8804
- const variable = ASTUtils6.findVariable(context.sourceCode.getScope(identifier), identifier.name);
8929
+ const variable = ASTUtils8.findVariable(context.sourceCode.getScope(identifier), identifier.name);
8805
8930
  if (variable === null || variable.defs.length === 0) return true;
8806
8931
  return variable.defs.some((definition) => {
8807
8932
  let current = definition.node;
@@ -9216,7 +9341,7 @@ var prefer_input_group_search_default = createRule({
9216
9341
  });
9217
9342
 
9218
9343
  // src/rules/prefer-immutable-module-constant.ts
9219
- import { AST_NODE_TYPES as AST_NODE_TYPES40, ASTUtils as ASTUtils7 } from "@typescript-eslint/utils";
9344
+ import { AST_NODE_TYPES as AST_NODE_TYPES40, ASTUtils as ASTUtils9 } from "@typescript-eslint/utils";
9220
9345
  var preferImmutableModuleConstantDocumentation = {
9221
9346
  summary: "Require module-level constant collections to expose readonly state.",
9222
9347
  rationale: "A const binding prevents reassignment but does not stop callers from mutating its array, object, Set, or Map contents.",
@@ -9369,7 +9494,7 @@ var prefer_immutable_module_constant_default = createRule({
9369
9494
  create(context) {
9370
9495
  const sourceCode = context.sourceCode;
9371
9496
  const isUnshadowedGlobal = (identifier) => {
9372
- const variable = ASTUtils7.findVariable(sourceCode.getScope(identifier), identifier.name);
9497
+ const variable = ASTUtils9.findVariable(sourceCode.getScope(identifier), identifier.name);
9373
9498
  return variable === null || variable.defs.length === 0;
9374
9499
  };
9375
9500
  if (JAVASCRIPT_FILE_RE.test(context.filename) || isTestFile(context.filename) || isGeneratedFile(context.filename, sourceCode.getText())) {
@@ -10247,7 +10372,7 @@ var prefer_module_level_schema_default = createRule({
10247
10372
  });
10248
10373
 
10249
10374
  // src/rules/prefer-native-random-uuid.ts
10250
- import { AST_NODE_TYPES as AST_NODE_TYPES44, ASTUtils as ASTUtils8 } from "@typescript-eslint/utils";
10375
+ import { AST_NODE_TYPES as AST_NODE_TYPES44, ASTUtils as ASTUtils10 } from "@typescript-eslint/utils";
10251
10376
  var preferNativeRandomUuidDocumentation = {
10252
10377
  summary: "Prefer `globalThis.crypto.randomUUID()` over resolved zero-argument UUID v4 bindings from the `uuid` package.",
10253
10378
  rationale: "The platform implementation avoids an unnecessary dependency for standard random UUID generation.",
@@ -10283,7 +10408,7 @@ var prefer_native_random_uuid_default = createRule({
10283
10408
  const directBindings = /* @__PURE__ */ new Set();
10284
10409
  const namespaceBindings = /* @__PURE__ */ new Set();
10285
10410
  function resolve(identifier) {
10286
- return ASTUtils8.findVariable(context.sourceCode.getScope(identifier), identifier.name);
10411
+ return ASTUtils10.findVariable(context.sourceCode.getScope(identifier), identifier.name);
10287
10412
  }
10288
10413
  function record(identifier, destination) {
10289
10414
  const variable = resolve(identifier);
@@ -10346,7 +10471,7 @@ var prefer_native_random_uuid_default = createRule({
10346
10471
  });
10347
10472
 
10348
10473
  // src/rules/prefer-non-nullable-collection.ts
10349
- import { AST_NODE_TYPES as AST_NODE_TYPES45, ASTUtils as ASTUtils9 } from "@typescript-eslint/utils";
10474
+ import { AST_NODE_TYPES as AST_NODE_TYPES45, ASTUtils as ASTUtils11 } from "@typescript-eslint/utils";
10350
10475
  var preferNonNullableCollectionDocumentation = {
10351
10476
  summary: "Suggest non-null arrays only when local control flow proves the nullish state is equivalent to an empty collection.",
10352
10477
  rationale: "A redundant nullish collection state spreads defaults and guards through consumers without carrying information.",
@@ -10476,14 +10601,14 @@ function directlyCoalesced(node) {
10476
10601
  return parent?.type === AST_NODE_TYPES45.LogicalExpression && parent.left === node && (parent.operator === "??" || parent.operator === "||") && emptyArray(parent.right);
10477
10602
  }
10478
10603
  function identifierIsOnlyCoalesced(context, binding, fn) {
10479
- const variable = ASTUtils9.findVariable(context.sourceCode.getScope(binding), binding.name);
10604
+ const variable = ASTUtils11.findVariable(context.sourceCode.getScope(binding), binding.name);
10480
10605
  if (variable === null || variable.references.length === 0) return false;
10481
10606
  return variable.references.every(
10482
10607
  (reference) => belongsToFunction(reference.identifier, fn) && directlyCoalesced(reference.identifier)
10483
10608
  );
10484
10609
  }
10485
10610
  function memberIsOnlyCoalesced(context, object, property, fn) {
10486
- const variable = ASTUtils9.findVariable(context.sourceCode.getScope(object), object.name);
10611
+ const variable = ASTUtils11.findVariable(context.sourceCode.getScope(object), object.name);
10487
10612
  if (variable === null) return false;
10488
10613
  const accesses = variable.references.flatMap((reference) => {
10489
10614
  if (!belongsToFunction(reference.identifier, fn)) return [null];
@@ -11489,7 +11614,7 @@ var preferServerActionsDocumentation = {
11489
11614
  var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
11490
11615
  var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
11491
11616
  var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
11492
- var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
11617
+ var NON_REACT_FRAMEWORK_RE2 = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
11493
11618
  function getScope(context, node) {
11494
11619
  return context.sourceCode.getScope(node);
11495
11620
  }
@@ -11601,7 +11726,7 @@ var prefer_server_actions_default = createRule({
11601
11726
  return {};
11602
11727
  }
11603
11728
  const isNonReactFramework = context.sourceCode.ast.body.some(
11604
- (node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(node.source.value)
11729
+ (node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE2.test(node.source.value)
11605
11730
  );
11606
11731
  return {
11607
11732
  CallExpression(node) {
@@ -12419,7 +12544,7 @@ var require_assert_never_default = createRule({
12419
12544
  });
12420
12545
 
12421
12546
  // src/rules/require-fetch-timeout.ts
12422
- import { AST_NODE_TYPES as AST_NODE_TYPES51, ASTUtils as ASTUtils10 } from "@typescript-eslint/utils";
12547
+ import { AST_NODE_TYPES as AST_NODE_TYPES51, ASTUtils as ASTUtils12 } from "@typescript-eslint/utils";
12423
12548
  var requireFetchTimeoutDocumentation = {
12424
12549
  summary: "Require an abort `signal` (e.g. `AbortSignal.timeout(ms)`) on global `fetch()` calls so stalled upstreams cannot hang the caller forever.",
12425
12550
  rationale: "An unbounded request can occupy work indefinitely when an upstream stalls.",
@@ -12500,7 +12625,7 @@ var require_fetch_timeout_default = createRule({
12500
12625
  }
12501
12626
  function resolvesToGlobal(identifier) {
12502
12627
  const scope = context.sourceCode.getScope(identifier);
12503
- const variable = ASTUtils10.findVariable(scope, identifier.name);
12628
+ const variable = ASTUtils12.findVariable(scope, identifier.name);
12504
12629
  return variable === null || variable.defs.length === 0;
12505
12630
  }
12506
12631
  function isGlobalFetchCall2(callee) {
@@ -13313,7 +13438,7 @@ var store_insert_requires_on_conflict_default = createRule({
13313
13438
  });
13314
13439
 
13315
13440
  // src/rules/stepdown.ts
13316
- import { AST_NODE_TYPES as AST_NODE_TYPES55, ASTUtils as ASTUtils11 } from "@typescript-eslint/utils";
13441
+ import { AST_NODE_TYPES as AST_NODE_TYPES55, ASTUtils as ASTUtils13 } from "@typescript-eslint/utils";
13317
13442
  var stepdownDocumentation = {
13318
13443
  summary: "Place a private helper below its sole direct same-scope caller.",
13319
13444
  rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
@@ -13506,7 +13631,7 @@ function methodName(node) {
13506
13631
  return !node.computed && node.key.type === AST_NODE_TYPES55.Identifier ? node.key.name : null;
13507
13632
  }
13508
13633
  function referencedMethod(context, node, classVariables) {
13509
- const objectVariable = node.object.type === AST_NODE_TYPES55.Identifier ? ASTUtils11.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
13634
+ const objectVariable = node.object.type === AST_NODE_TYPES55.Identifier ? ASTUtils13.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
13510
13635
  const isClassReference = objectVariable !== null && classVariables.has(objectVariable);
13511
13636
  if (node.object.type !== AST_NODE_TYPES55.ThisExpression && !isClassReference) return null;
13512
13637
  if (node.property.type === AST_NODE_TYPES55.PrivateIdentifier) return `#${node.property.name}`;
@@ -13559,11 +13684,11 @@ function classScope(context, node, computedReferenceNames) {
13559
13684
  const pinned = /* @__PURE__ */ new Set();
13560
13685
  const classVariables = /* @__PURE__ */ new Set();
13561
13686
  if (node.id !== null) {
13562
- const internal = ASTUtils11.findVariable(context.sourceCode.getScope(node), node.id.name);
13687
+ const internal = ASTUtils13.findVariable(context.sourceCode.getScope(node), node.id.name);
13563
13688
  if (internal !== null) classVariables.add(internal);
13564
13689
  }
13565
13690
  if (node.type === AST_NODE_TYPES55.ClassExpression && node.parent.type === AST_NODE_TYPES55.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES55.Identifier) {
13566
- const outer = ASTUtils11.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
13691
+ const outer = ASTUtils13.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
13567
13692
  if (outer !== null) classVariables.add(outer);
13568
13693
  }
13569
13694
  for (const method of methods) {
@@ -13599,7 +13724,7 @@ function classScope(context, node, computedReferenceNames) {
13599
13724
  return;
13600
13725
  }
13601
13726
  if (binding.type !== AST_NODE_TYPES55.Identifier) return;
13602
- const variable = ASTUtils11.findVariable(context.sourceCode.getScope(binding), binding.name);
13727
+ const variable = ASTUtils13.findVariable(context.sourceCode.getScope(binding), binding.name);
13603
13728
  if (variable !== null) {
13604
13729
  methodClassVariables.add(variable);
13605
13730
  methodAliases.add(variable);
@@ -13629,7 +13754,7 @@ function classScope(context, node, computedReferenceNames) {
13629
13754
  return;
13630
13755
  }
13631
13756
  if (!privateNames.has(target)) return;
13632
- const objectVariable = current.object.type === AST_NODE_TYPES55.Identifier ? ASTUtils11.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
13757
+ const objectVariable = current.object.type === AST_NODE_TYPES55.Identifier ? ASTUtils13.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
13633
13758
  if (objectVariable !== null && methodAliases.has(objectVariable)) {
13634
13759
  pinned.add(target);
13635
13760
  return;
@@ -13713,7 +13838,7 @@ var stepdown_default = createRule({
13713
13838
  // src/rules/zod-naming-convention.ts
13714
13839
  import {
13715
13840
  AST_NODE_TYPES as AST_NODE_TYPES56,
13716
- ASTUtils as ASTUtils12
13841
+ ASTUtils as ASTUtils14
13717
13842
  } from "@typescript-eslint/utils";
13718
13843
  var zodNamingConventionDocumentation = {
13719
13844
  summary: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.",
@@ -13800,7 +13925,7 @@ var zod_naming_convention_default = createRule({
13800
13925
  const acceptsSchemaWord = convention !== "prefix";
13801
13926
  const zodBindings = /* @__PURE__ */ new Set();
13802
13927
  function resolvedBinding(identifier) {
13803
- return ASTUtils12.findVariable(
13928
+ return ASTUtils14.findVariable(
13804
13929
  context.sourceCode.getScope(identifier),
13805
13930
  identifier.name
13806
13931
  );
@@ -13851,7 +13976,6 @@ var zod_naming_convention_default = createRule({
13851
13976
  // src/rules/_renames.ts
13852
13977
  var renamedRules = {
13853
13978
  "jsdoc-restates-signature": "no-restated-jsdoc",
13854
- "no-async-callback-in-waitfor": "no-async-callback-in-wait-for",
13855
13979
  "require-interface-for-injected-service": "require-port-for-service",
13856
13980
  "strict-test-assertions": "prefer-whole-object-assertion",
13857
13981
  "trailing-value-narration": "no-trailing-value-narration"
@@ -13867,6 +13991,14 @@ var retiredRules = {
13867
13991
  removedIn: "5.0.0",
13868
13992
  reason: "Delete the config entry and suppressions; there is no replacement."
13869
13993
  },
13994
+ "no-async-callback-in-wait-for": {
13995
+ removedIn: "14.0.0",
13996
+ reason: "Delete the entry; Testing Library supports async waitFor callbacks and retries when their promises reject."
13997
+ },
13998
+ "no-async-callback-in-waitfor": {
13999
+ removedIn: "14.0.0",
14000
+ reason: "Delete the stale alias; the replacement was also retired because Testing Library supports async waitFor callbacks."
14001
+ },
13870
14002
  "no-sequential-await": {
13871
14003
  removedIn: "3.0.0",
13872
14004
  reason: "Delete the entry; core `no-await-in-loop` covers it."
@@ -13921,7 +14053,6 @@ var retiredRules = {
13921
14053
  var rules = {
13922
14054
  "duplicate-test-body": duplicate_test_body_default,
13923
14055
  "enforce-file-structure": enforce_file_structure_default,
13924
- "no-async-callback-in-wait-for": no_async_callback_in_wait_for_default,
13925
14056
  "no-client-side-data-fetching": no_client_side_data_fetching_default,
13926
14057
  "no-comment-cruft": no_comment_cruft_default,
13927
14058
  "no-conditional-in-test": no_conditional_in_test_default,
@@ -13987,7 +14118,7 @@ var rules = {
13987
14118
  };
13988
14119
  var meta = {
13989
14120
  name: "@sarj/eslint-plugin",
13990
- version: "13.0.0"
14121
+ version: "14.0.0"
13991
14122
  };
13992
14123
  var applicationOnlyRules = [
13993
14124
  "no-restricted-library-load",
@@ -13997,7 +14128,6 @@ var applicationOnlyRules = [
13997
14128
  var recommendedRules = {
13998
14129
  "@sarj/duplicate-test-body": "error",
13999
14130
  "@sarj/enforce-file-structure": "error",
14000
- "@sarj/no-async-callback-in-wait-for": "error",
14001
14131
  "@sarj/no-client-side-data-fetching": "error",
14002
14132
  "@sarj/no-comment-cruft": "error",
14003
14133
  "@sarj/no-conditional-in-test": "error",
@@ -14057,7 +14187,6 @@ var recommendedRules = {
14057
14187
  var strictRules = {
14058
14188
  "@sarj/duplicate-test-body": "error",
14059
14189
  "@sarj/enforce-file-structure": "error",
14060
- "@sarj/no-async-callback-in-wait-for": "error",
14061
14190
  "@sarj/no-client-side-data-fetching": "error",
14062
14191
  "@sarj/no-comment-cruft": "error",
14063
14192
  "@sarj/no-conditional-in-test": "error",