@sarj/eslint-plugin 2.3.4 → 2.4.1

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
@@ -37,43 +37,20 @@ module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/rules/enforce-file-structure.ts
39
39
  var import_utils = require("@typescript-eslint/utils");
40
- var SECTION = {
41
- declarations: 0,
42
- functions: 1,
43
- exports: 2
44
- };
45
- var SECTION_NAMES = ["declarations", "functions", "exports"];
46
- var sectionName = (ordinal) => {
47
- const name = SECTION_NAMES[ordinal];
48
- return name ?? "unknown";
49
- };
50
- var SERVER_ACTION_FILE_RE = /(?:^|\/)actions\/|\.action\.[jt]sx?$|(?:^|\/)actions\.[jt]sx?$/;
51
- var isFunctionExpression = (node) => node.type === import_utils.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils.AST_NODE_TYPES.FunctionExpression;
52
- var isFunctionLikeVariable = (statement) => statement.declarations.length > 0 && statement.declarations.every(
53
- (decl) => decl.init !== null && isFunctionExpression(decl.init)
54
- );
55
- var getStatementSection = (statement) => {
40
+ var classifyStatement = (statement) => {
56
41
  switch (statement.type) {
57
42
  case import_utils.AST_NODE_TYPES.ImportDeclaration:
58
- case import_utils.AST_NODE_TYPES.TSTypeAliasDeclaration:
59
- case import_utils.AST_NODE_TYPES.TSInterfaceDeclaration:
60
- case import_utils.AST_NODE_TYPES.TSEnumDeclaration:
61
- case import_utils.AST_NODE_TYPES.ClassDeclaration:
62
- return SECTION.declarations;
63
- case import_utils.AST_NODE_TYPES.VariableDeclaration:
64
- return isFunctionLikeVariable(statement) ? SECTION.functions : SECTION.declarations;
65
- case import_utils.AST_NODE_TYPES.FunctionDeclaration:
66
- return SECTION.functions;
67
- case import_utils.AST_NODE_TYPES.ExportNamedDeclaration:
68
- case import_utils.AST_NODE_TYPES.ExportDefaultDeclaration:
43
+ return "import";
69
44
  case import_utils.AST_NODE_TYPES.ExportAllDeclaration:
70
- return SECTION.exports;
45
+ return "reexport";
46
+ case import_utils.AST_NODE_TYPES.ExportNamedDeclaration:
47
+ return statement.declaration === null ? "reexport" : "body";
71
48
  default:
72
- return SECTION.functions;
49
+ return "body";
73
50
  }
74
51
  };
52
+ var isStringDirective = (statement) => statement.type === import_utils.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils.AST_NODE_TYPES.Literal && typeof statement.expression.value === "string" && statement.expression.value.startsWith("use ");
75
53
  var isUseServerDirective = (statement) => {
76
- if (statement === void 0) return false;
77
54
  if (statement.type !== import_utils.AST_NODE_TYPES.ExpressionStatement) return false;
78
55
  const expr = statement.expression;
79
56
  if (expr.type !== import_utils.AST_NODE_TYPES.Literal) return false;
@@ -86,47 +63,45 @@ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
86
63
  meta: {
87
64
  type: "suggestion",
88
65
  docs: {
89
- description: "Enforce that function definitions follow the file's top-of-file declarations (imports, types, constants, classes) \u2014 the stepdown rule. Ordering among non-function declarations is not enforced. Server-action files (under `/actions/`, named `*.action.ts`, or `actions.ts`) must also begin with a `use server` directive."
66
+ description: "Require `import` statements to come first, then allow step-down ordering (public API first, private helpers below) for the rest of the file. Exported statements are classified by WHAT they export \u2014 an exported interface is a declaration, an exported function is a function \u2014 so a public exported function followed by a private helper, or an exported interface among declarations, is allowed. Re-exports (`export { \u2026 } from`, `export *`, `export { \u2026 }`) are a neutral group, so generated namespace barrels pass. When a module contains a `use server` directive, it must be the first statement in the file."
90
67
  },
91
68
  schema: [],
92
69
  messages: {
93
- incorrectOrder: "File structure violation: {{current}} should come before {{expected}}",
94
- useServerDirective: "Server action files must start with 'use server' directive"
70
+ importsFirst: "File structure violation: import statements must come before other declarations",
71
+ useServerDirective: "A 'use server' directive must be the first statement in the file"
95
72
  }
96
73
  },
97
74
  defaultOptions: [],
98
75
  create(context) {
99
- const filename = context.filename;
100
- const isServerAction = SERVER_ACTION_FILE_RE.test(filename);
101
76
  return {
102
77
  Program(node) {
103
78
  const body = node.body;
104
- if (isServerAction) {
105
- const firstNode = body[0];
106
- if (!isUseServerDirective(firstNode)) {
107
- context.report({
108
- node,
109
- messageId: "useServerDirective"
110
- });
111
- }
79
+ const misplacedUseServer = body.find(
80
+ (statement, index) => index > 0 && isUseServerDirective(statement)
81
+ );
82
+ if (misplacedUseServer !== void 0) {
83
+ context.report({
84
+ node: misplacedUseServer,
85
+ messageId: "useServerDirective"
86
+ });
112
87
  }
113
- let currentSection = SECTION.declarations;
88
+ let seenBody = false;
114
89
  for (const statement of body) {
115
- if (statement.type === import_utils.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils.AST_NODE_TYPES.Literal && typeof statement.expression.value === "string" && statement.expression.value.startsWith("use ")) {
116
- continue;
117
- }
118
- const statementSection = getStatementSection(statement);
119
- if (statementSection < currentSection) {
120
- context.report({
121
- node: statement,
122
- messageId: "incorrectOrder",
123
- data: {
124
- current: sectionName(statementSection),
125
- expected: sectionName(currentSection)
90
+ if (isStringDirective(statement)) continue;
91
+ switch (classifyStatement(statement)) {
92
+ case "reexport":
93
+ continue;
94
+ case "body":
95
+ seenBody = true;
96
+ continue;
97
+ case "import":
98
+ if (seenBody) {
99
+ context.report({
100
+ node: statement,
101
+ messageId: "importsFirst"
102
+ });
126
103
  }
127
- });
128
- } else if (statementSection > currentSection) {
129
- currentSection = statementSection;
104
+ continue;
130
105
  }
131
106
  }
132
107
  }
@@ -272,7 +247,7 @@ var no_client_side_data_fetching_default = import_utils2.ESLintUtils.RuleCreator
272
247
  // src/rules/no-comment-cruft.ts
273
248
  var import_utils3 = require("@typescript-eslint/utils");
274
249
  var LEADING_PREAMBLE_MIN = 4;
275
- var DIRECTIVE_RE = /^(eslint\b|eslint-|@ts-|prettier-ignore|prettier\b|biome-|c8\b|v8\b|istanbul\b|@type\b|@vite|webpack|<reference|global\b|noinspection|todo\b|fixme\b|hack\b|xxx\b)/i;
250
+ var DIRECTIVE_RE = /^(eslint\b|eslint-|@ts-|prettier-ignore|prettier\b|biome-|c8\b|v8\b|istanbul\b|@type\b|@vite|webpack|<reference|<amd|global\b|noinspection|todo\b|fixme\b|hack\b|xxx\b)/i;
276
251
  var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
277
252
  var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
278
253
  var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
@@ -280,8 +255,9 @@ var REGION_RE = /^#?(?:end)?region\b/i;
280
255
  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\.)/;
281
256
  var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
282
257
  var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
258
+ var PSEUDOCODE_RE = /%\w+%|\[opt\]|(?:^|\s)<[A-Za-z]\w*>|…|\.\.\./;
283
259
  function stripCommentMarker(line) {
284
- return line.replace(/^\s*\/\//, "").replace(/^\s*\*+/, "").trim();
260
+ return line.replace(/^\s*\/{1,2}/, "").replace(/^\s*\*+/, "").trim();
285
261
  }
286
262
  function isDirective(text) {
287
263
  return DIRECTIVE_RE.test(text.trim());
@@ -297,6 +273,30 @@ function looksLikeCode(text) {
297
273
  if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;
298
274
  return CALL_OR_ASSIGN_RE.test(t);
299
275
  }
276
+ function hasPseudocode(text) {
277
+ return PSEUDOCODE_RE.test(text);
278
+ }
279
+ function isProse(text) {
280
+ const t = text.trim();
281
+ if (!t) return false;
282
+ if (t.endsWith(":")) return true;
283
+ if (/[.!?]$/.test(t) && /\s/.test(t) && /[a-z]/.test(t) && !looksLikeCode(t) && t.split(/\s+/).length >= 3) {
284
+ return true;
285
+ }
286
+ return false;
287
+ }
288
+ function hasCommentedOutCode(texts, precedingProse) {
289
+ for (let i = 0; i < texts.length; i++) {
290
+ const line = texts[i];
291
+ if (line === void 0 || !looksLikeCode(line) || hasPseudocode(line)) {
292
+ continue;
293
+ }
294
+ const prev = i > 0 ? texts[i - 1] : void 0;
295
+ if (prev !== void 0 ? isProse(prev) : precedingProse) continue;
296
+ return true;
297
+ }
298
+ return false;
299
+ }
300
300
  var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
301
301
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
302
302
  )({
@@ -351,12 +351,19 @@ var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
351
351
  Program() {
352
352
  const comments = sourceCode.getAllComments();
353
353
  const firstCodeLine = sourceCode.ast.tokens[0]?.loc.start.line ?? Number.MAX_SAFE_INTEGER;
354
- for (const comment of comments) {
354
+ for (let i = 0; i < comments.length; i++) {
355
+ const comment = comments[i];
356
+ if (comment === void 0) continue;
355
357
  if (isJsDoc(comment) || !isStandalone(comment)) continue;
358
+ if (LICENSE_RE.test(comment.value)) continue;
356
359
  const texts = comment.value.split("\n").map(stripCommentMarker).filter((l) => l.length > 0 && !isDirective(l));
357
360
  if (texts.some(isBanner)) {
358
361
  context.report({ node: comment, messageId: "sectionBanner" });
359
- } else if (texts.some(looksLikeCode)) {
362
+ continue;
363
+ }
364
+ const prev = comments[i - 1];
365
+ const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
366
+ if (hasCommentedOutCode(texts, precedingProse)) {
360
367
  context.report({ node: comment, messageId: "commentedOutCode" });
361
368
  }
362
369
  }
@@ -438,7 +445,9 @@ var no_enum_default = import_utils4.ESLintUtils.RuleCreator(
438
445
 
439
446
  // src/rules/no-insecure-random-id.ts
440
447
  var import_utils5 = require("@typescript-eslint/utils");
441
- var NAME_PATTERN = /id|token|key|secret|uuid|nonce|session|password|salt/i;
448
+ var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
449
+ var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
450
+ var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
442
451
  function isMathRandomCall(node) {
443
452
  if (node.type !== "CallExpression") {
444
453
  return false;
@@ -450,6 +459,24 @@ function isMathRandomCall(node) {
450
459
  const { object, property } = callee;
451
460
  return object.type === "Identifier" && object.name === "Math" && property.type === "Identifier" && property.name === "random";
452
461
  }
462
+ function climbValueChain(node) {
463
+ let current = node;
464
+ let parent = current.parent;
465
+ while (parent) {
466
+ if (parent.type === "MemberExpression" && parent.object === current && !parent.computed) {
467
+ current = parent;
468
+ parent = current.parent;
469
+ continue;
470
+ }
471
+ if (parent.type === "CallExpression" && parent.callee === current) {
472
+ current = parent;
473
+ parent = current.parent;
474
+ continue;
475
+ }
476
+ break;
477
+ }
478
+ return current;
479
+ }
453
480
  function isPartOfToString36Chain(node) {
454
481
  let current = node;
455
482
  let parent = current.parent;
@@ -515,6 +542,49 @@ function findEnclosingName(node) {
515
542
  }
516
543
  return void 0;
517
544
  }
545
+ function collectStaticStringParts(node, out) {
546
+ if (node.type === "Literal" && typeof node.value === "string") {
547
+ out.push(node.value);
548
+ return;
549
+ }
550
+ if (node.type === "TemplateLiteral") {
551
+ for (const quasi of node.quasis) {
552
+ out.push(quasi.value.cooked ?? quasi.value.raw);
553
+ }
554
+ return;
555
+ }
556
+ if (node.type === "BinaryExpression" && node.operator === "+") {
557
+ collectStaticStringParts(node.left, out);
558
+ collectStaticStringParts(node.right, out);
559
+ }
560
+ }
561
+ function isConcatenatedIntoPathOrDomId(node) {
562
+ const valueNode = climbValueChain(node);
563
+ let current = valueNode;
564
+ let parent = current.parent;
565
+ let top;
566
+ while (parent) {
567
+ if (parent.type === "BinaryExpression" && parent.operator === "+" && (parent.left === current || parent.right === current)) {
568
+ top = parent;
569
+ current = parent;
570
+ parent = current.parent;
571
+ continue;
572
+ }
573
+ if (parent.type === "TemplateLiteral") {
574
+ top = parent;
575
+ current = parent;
576
+ parent = current.parent;
577
+ continue;
578
+ }
579
+ break;
580
+ }
581
+ if (!top) {
582
+ return false;
583
+ }
584
+ const parts = [];
585
+ collectStaticStringParts(top, parts);
586
+ return parts.some((part) => PATH_OR_DOM_MARKER.test(part));
587
+ }
518
588
  var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
519
589
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
520
590
  )({
@@ -536,12 +606,18 @@ var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
536
606
  if (!isMathRandomCall(node)) {
537
607
  return;
538
608
  }
539
- if (isPartOfToString36Chain(node)) {
609
+ const name = findEnclosingName(node);
610
+ if (name !== void 0 && STRONG_SECURITY_PATTERN.test(name)) {
540
611
  context.report({ node, messageId: "insecureRandomId" });
541
612
  return;
542
613
  }
543
- const name = findEnclosingName(node);
544
- if (name !== void 0 && NAME_PATTERN.test(name)) {
614
+ if (name !== void 0 && NON_SECURITY_ID_PATTERN.test(name)) {
615
+ return;
616
+ }
617
+ if (isConcatenatedIntoPathOrDomId(node)) {
618
+ return;
619
+ }
620
+ if (isPartOfToString36Chain(node)) {
545
621
  context.report({ node, messageId: "insecureRandomId" });
546
622
  }
547
623
  }
@@ -552,6 +628,8 @@ var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
552
628
  // src/rules/no-json-stringify-error.ts
553
629
  var import_utils6 = require("@typescript-eslint/utils");
554
630
  var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
631
+ var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
632
+ var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
555
633
  function isCatchBinding(scope, name) {
556
634
  let current = scope;
557
635
  while (current) {
@@ -567,6 +645,98 @@ function isCatchBinding(scope, name) {
567
645
  }
568
646
  return false;
569
647
  }
648
+ function memberSuggestsError(member, scope) {
649
+ const propName2 = !member.computed && member.property.type === "Identifier" ? member.property.name : null;
650
+ if (propName2 !== null && ERROR_PROP_PATTERN.test(propName2)) {
651
+ return true;
652
+ }
653
+ const base = member.object;
654
+ const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
655
+ if (baseSuggestsError) {
656
+ return propName2 === null || !SAFE_STRING_PROPS.has(propName2.toLowerCase());
657
+ }
658
+ return false;
659
+ }
660
+ function instanceofErrorSubject(test) {
661
+ if (test.type === "BinaryExpression" && test.operator === "instanceof" && test.right.type === "Identifier" && test.right.name === "Error") {
662
+ return test.left;
663
+ }
664
+ return null;
665
+ }
666
+ var TYPE_GUARD_PATTERN = /^(is|has)[A-Z]/;
667
+ function typeGuardSubject(test) {
668
+ const arg = test.type === "CallExpression" ? test.arguments[0] : void 0;
669
+ if (test.type === "CallExpression" && test.callee.type === "Identifier" && TYPE_GUARD_PATTERN.test(test.callee.name) && test.arguments.length === 1 && arg !== void 0 && arg.type !== "SpreadElement") {
670
+ return arg;
671
+ }
672
+ return null;
673
+ }
674
+ function positiveErrorSubject(test) {
675
+ return instanceofErrorSubject(test) ?? typeGuardSubject(test);
676
+ }
677
+ function negatedInstanceofErrorSubject(test) {
678
+ if (test.type === "UnaryExpression" && test.operator === "!") {
679
+ return positiveErrorSubject(test.argument);
680
+ }
681
+ return null;
682
+ }
683
+ function branchTerminates(branch) {
684
+ const body = branch.type === "BlockStatement" ? branch.body : [branch];
685
+ const last = body[body.length - 1];
686
+ return last !== void 0 && (last.type === "ReturnStatement" || last.type === "ThrowStatement");
687
+ }
688
+ function isNarrowedByEarlyReturn(node, argExpr, sourceCode) {
689
+ const argText = sourceCode.getText(argExpr);
690
+ let current = node.parent;
691
+ while (current) {
692
+ if (current.type === "BlockStatement" || current.type === "Program") {
693
+ for (const stmt of current.body) {
694
+ if (stmt.range[0] >= node.range[0]) {
695
+ break;
696
+ }
697
+ if (stmt.type === "IfStatement" && stmt.alternate === null && branchTerminates(stmt.consequent)) {
698
+ const subject = positiveErrorSubject(stmt.test);
699
+ if (subject && sourceCode.getText(subject) === argText) {
700
+ return true;
701
+ }
702
+ }
703
+ }
704
+ }
705
+ current = current.parent;
706
+ }
707
+ return false;
708
+ }
709
+ function nodeWithin(node, container) {
710
+ return container !== null && node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
711
+ }
712
+ function isGuardedByInstanceofError(node, argExpr, sourceCode) {
713
+ const argText = sourceCode.getText(argExpr);
714
+ const sameSubject = (subject) => sourceCode.getText(subject) === argText;
715
+ let current = node.parent;
716
+ while (current) {
717
+ if (current.type === "ConditionalExpression") {
718
+ const subject = positiveErrorSubject(current.test);
719
+ if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
720
+ return true;
721
+ }
722
+ const negated = negatedInstanceofErrorSubject(current.test);
723
+ if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
724
+ return true;
725
+ }
726
+ } else if (current.type === "IfStatement") {
727
+ const subject = positiveErrorSubject(current.test);
728
+ if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
729
+ return true;
730
+ }
731
+ const negated = negatedInstanceofErrorSubject(current.test);
732
+ if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
733
+ return true;
734
+ }
735
+ }
736
+ current = current.parent;
737
+ }
738
+ return false;
739
+ }
570
740
  function isJsonStringify(callee) {
571
741
  return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
572
742
  }
@@ -592,72 +762,124 @@ var no_json_stringify_error_default = import_utils6.ESLintUtils.RuleCreator(
592
762
  return;
593
763
  }
594
764
  const firstArg = node.arguments[0];
595
- if (!firstArg || firstArg.type !== "Identifier") {
765
+ if (!firstArg) {
596
766
  return;
597
767
  }
598
- const name = firstArg.name;
599
768
  const scope = context.sourceCode.getScope(firstArg);
600
- if (ERROR_NAME_PATTERN.test(name) || isCatchBinding(scope, name)) {
601
- context.report({
602
- node,
603
- messageId: "noJsonStringifyError"
604
- });
769
+ let suggestsError;
770
+ if (firstArg.type === "Identifier") {
771
+ suggestsError = ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);
772
+ } else if (firstArg.type === "MemberExpression") {
773
+ suggestsError = memberSuggestsError(firstArg, scope);
774
+ } else {
775
+ return;
605
776
  }
777
+ if (!suggestsError) {
778
+ return;
779
+ }
780
+ if (isGuardedByInstanceofError(node, firstArg, context.sourceCode) || isNarrowedByEarlyReturn(node, firstArg, context.sourceCode)) {
781
+ return;
782
+ }
783
+ context.report({
784
+ node,
785
+ messageId: "noJsonStringifyError"
786
+ });
606
787
  }
607
788
  };
608
789
  }
609
790
  });
610
791
 
611
792
  // src/rules/no-log-only-catch.ts
793
+ var import_utils8 = require("@typescript-eslint/utils");
794
+
795
+ // src/rules/_logging.ts
612
796
  var import_utils7 = require("@typescript-eslint/utils");
613
- var DEFAULT_IGNORE_PATTERNS2 = [
614
- /\.test\./,
615
- /\.spec\./,
616
- /[\\/]__tests__[\\/]/
617
- ];
618
- var CONSOLE_METHODS = /* @__PURE__ */ new Set([
619
- "log",
620
- "error",
621
- "warn",
797
+ var LOG_METHODS = /* @__PURE__ */ new Set([
798
+ "debug",
622
799
  "info",
623
- "debug"
800
+ "warn",
801
+ "warning",
802
+ "error",
803
+ "exception",
804
+ "critical",
805
+ "trace",
806
+ "log",
807
+ "fatal",
808
+ "success"
624
809
  ]);
625
- function isConsoleCallStatement(statement) {
626
- if (statement.type !== "ExpressionStatement") {
627
- return false;
810
+ var LOGGER_NAMES = /* @__PURE__ */ new Set([
811
+ "logger",
812
+ "log",
813
+ "logging",
814
+ "loguru",
815
+ "console",
816
+ "_logger",
817
+ "_log"
818
+ ]);
819
+ var REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;
820
+ function isLoggerReceiver(expr) {
821
+ switch (expr.type) {
822
+ case "Identifier":
823
+ return LOGGER_NAMES.has(expr.name.toLowerCase());
824
+ case "MemberExpression": {
825
+ const { property, object } = expr;
826
+ if (!expr.computed && property.type === "Identifier" && LOGGER_NAMES.has(property.name.toLowerCase())) {
827
+ return true;
828
+ }
829
+ return isLoggerReceiver(object);
830
+ }
831
+ default:
832
+ return false;
628
833
  }
629
- const expr = statement.expression;
834
+ }
835
+ function isLoggingCall(expr) {
630
836
  if (expr.type !== "CallExpression") {
631
837
  return false;
632
838
  }
633
839
  const callee = expr.callee;
634
- if (callee.type !== "MemberExpression") {
840
+ if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier") {
635
841
  return false;
636
842
  }
637
- const { object, property } = callee;
638
- if (object.type !== "Identifier" || object.name !== "console") {
843
+ if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {
639
844
  return false;
640
845
  }
641
- if (callee.computed) {
642
- return false;
846
+ return isLoggerReceiver(callee.object);
847
+ }
848
+ function calleeName(callee) {
849
+ if (callee.type === "Identifier") {
850
+ return callee.name;
851
+ }
852
+ if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
853
+ return callee.property.name;
643
854
  }
644
- if (property.type !== "Identifier") {
855
+ return null;
856
+ }
857
+
858
+ // src/rules/no-log-only-catch.ts
859
+ var DEFAULT_IGNORE_PATTERNS2 = [
860
+ /\.test\./,
861
+ /\.spec\./,
862
+ /[\\/]__tests__[\\/]/
863
+ ];
864
+ function isLoggingCallStatement(statement) {
865
+ if (statement.type !== "ExpressionStatement") {
645
866
  return false;
646
867
  }
647
- return CONSOLE_METHODS.has(property.name);
868
+ return isLoggingCall(statement.expression);
648
869
  }
649
- var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
870
+ var no_log_only_catch_default = import_utils8.ESLintUtils.RuleCreator(
650
871
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
651
872
  )({
652
873
  name: "no-log-only-catch",
653
874
  meta: {
654
875
  type: "problem",
655
876
  docs: {
656
- description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
877
+ description: "Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead."
657
878
  },
658
879
  schema: [],
659
880
  messages: {
660
- noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
881
+ noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real.",
882
+ emptyCatch: "Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore."
661
883
  }
662
884
  },
663
885
  defaultOptions: [],
@@ -673,13 +895,16 @@ var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
673
895
  CatchClause(node) {
674
896
  const statements = node.body.body;
675
897
  if (statements.length === 0) {
676
- context.report({ node, messageId: "noLogOnlyCatch" });
898
+ if (context.sourceCode.getCommentsInside(node.body).length > 0) {
899
+ return;
900
+ }
901
+ context.report({ node, messageId: "emptyCatch" });
677
902
  return;
678
903
  }
679
- const everyStatementIsConsoleLog = statements.every(
680
- (statement) => isConsoleCallStatement(statement)
904
+ const everyStatementIsLogging = statements.every(
905
+ (statement) => isLoggingCallStatement(statement)
681
906
  );
682
- if (everyStatementIsConsoleLog) {
907
+ if (everyStatementIsLogging) {
683
908
  context.report({ node, messageId: "noLogOnlyCatch" });
684
909
  }
685
910
  }
@@ -688,8 +913,25 @@ var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
688
913
  });
689
914
 
690
915
  // src/rules/no-raw-env.ts
691
- var import_utils8 = require("@typescript-eslint/utils");
692
- var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
916
+ var import_utils9 = require("@typescript-eslint/utils");
917
+ function isProcessEnv(node) {
918
+ return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
919
+ }
920
+ function isImportMetaEnv(node) {
921
+ return !node.computed && node.property.type === "Identifier" && node.property.name === "env" && node.object.type === "MetaProperty" && node.object.meta.name === "import" && node.object.property.name === "meta";
922
+ }
923
+ var BUILD_TIME_CONSTANTS = /* @__PURE__ */ new Set([
924
+ "NODE_ENV",
925
+ "MODE",
926
+ "DEV",
927
+ "PROD",
928
+ "SSR"
929
+ ]);
930
+ function isBuildTimeConstantAccess(node) {
931
+ const parent = node.parent;
932
+ return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
933
+ }
934
+ var no_raw_env_default = import_utils9.ESLintUtils.RuleCreator(
693
935
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
694
936
  )({
695
937
  name: "no-raw-env",
@@ -707,10 +949,7 @@ var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
707
949
  create(context) {
708
950
  return {
709
951
  MemberExpression(node) {
710
- if (node.computed) {
711
- return;
712
- }
713
- if (node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env") {
952
+ if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node)) {
714
953
  context.report({
715
954
  node,
716
955
  messageId: "noRawEnv"
@@ -722,39 +961,72 @@ var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
722
961
  });
723
962
 
724
963
  // src/rules/no-sentinel-return-on-catch.ts
725
- var import_utils9 = require("@typescript-eslint/utils");
964
+ var import_utils10 = require("@typescript-eslint/utils");
965
+ function sentinelKind(arg) {
966
+ if (arg === null) {
967
+ return null;
968
+ }
969
+ if (arg.type === import_utils10.AST_NODE_TYPES.Literal) {
970
+ if (arg.value === null) {
971
+ return "nullish";
972
+ }
973
+ if (typeof arg.value === "boolean") {
974
+ return "boolean";
975
+ }
976
+ if (typeof arg.value === "string") {
977
+ return "string";
978
+ }
979
+ return null;
980
+ }
981
+ if (arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
982
+ return "nullish";
983
+ }
984
+ if (arg.type === import_utils10.AST_NODE_TYPES.ArrayExpression) {
985
+ return "array";
986
+ }
987
+ if (arg.type === import_utils10.AST_NODE_TYPES.ObjectExpression) {
988
+ return "object";
989
+ }
990
+ return null;
991
+ }
726
992
  function isSentinelArgument(arg) {
727
993
  if (arg === null) {
728
994
  return false;
729
995
  }
730
- if (arg.type === import_utils9.AST_NODE_TYPES.Literal && arg.value === null) {
996
+ if (arg.type === import_utils10.AST_NODE_TYPES.Literal && arg.value === null) {
731
997
  return true;
732
998
  }
733
- if (arg.type === import_utils9.AST_NODE_TYPES.Literal && arg.value === false) {
999
+ if (arg.type === import_utils10.AST_NODE_TYPES.Literal && arg.value === false) {
734
1000
  return true;
735
1001
  }
736
- if (arg.type === import_utils9.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
1002
+ if (arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
737
1003
  return true;
738
1004
  }
739
- if (arg.type === import_utils9.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
1005
+ if (arg.type === import_utils10.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
740
1006
  return true;
741
1007
  }
742
- if (arg.type === import_utils9.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
1008
+ if (arg.type === import_utils10.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
743
1009
  return true;
744
1010
  }
745
1011
  return false;
746
1012
  }
747
- function containsThrow(node) {
1013
+ function isFunctionNode(node) {
1014
+ return node.type === import_utils10.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils10.AST_NODE_TYPES.FunctionExpression || node.type === import_utils10.AST_NODE_TYPES.ArrowFunctionExpression;
1015
+ }
1016
+ function isNode(value) {
1017
+ return typeof value === "object" && value !== null && typeof value.type === "string";
1018
+ }
1019
+ function walkWithinScope(node, visit) {
748
1020
  let found = false;
749
- const visit = (current) => {
1021
+ const recurse = (current) => {
750
1022
  if (found) {
751
1023
  return;
752
1024
  }
753
- if (current.type === import_utils9.AST_NODE_TYPES.ThrowStatement) {
1025
+ if (visit(current)) {
754
1026
  found = true;
755
1027
  return;
756
1028
  }
757
- if (current.type === import_utils9.AST_NODE_TYPES.FunctionDeclaration || current.type === import_utils9.AST_NODE_TYPES.FunctionExpression || current.type === import_utils9.AST_NODE_TYPES.ArrowFunctionExpression) {
1029
+ if (isFunctionNode(current)) {
758
1030
  return;
759
1031
  }
760
1032
  for (const key of Object.keys(current)) {
@@ -765,32 +1037,111 @@ function containsThrow(node) {
765
1037
  if (Array.isArray(value)) {
766
1038
  for (const child of value) {
767
1039
  if (isNode(child)) {
768
- visit(child);
1040
+ recurse(child);
769
1041
  }
770
1042
  }
771
1043
  } else if (isNode(value)) {
772
- visit(value);
1044
+ recurse(value);
773
1045
  }
774
1046
  }
775
1047
  };
776
- visit(node);
1048
+ recurse(node);
777
1049
  return found;
778
1050
  }
779
- function isNode(value) {
780
- return typeof value === "object" && value !== null && typeof value.type === "string";
1051
+ function containsThrow(node) {
1052
+ return walkWithinScope(
1053
+ node,
1054
+ (current) => current.type === import_utils10.AST_NODE_TYPES.ThrowStatement
1055
+ );
781
1056
  }
782
- var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
1057
+ function argsIncludeBinding(args, caughtName) {
1058
+ if (caughtName === null) {
1059
+ return false;
1060
+ }
1061
+ return args.some(
1062
+ (arg) => arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === caughtName
1063
+ );
1064
+ }
1065
+ function logsOrReportsError(catchBody, caughtName) {
1066
+ return walkWithinScope(catchBody, (current) => {
1067
+ if (current.type !== import_utils10.AST_NODE_TYPES.CallExpression) {
1068
+ return false;
1069
+ }
1070
+ if (isLoggingCall(current)) {
1071
+ return true;
1072
+ }
1073
+ const name = calleeName(current.callee);
1074
+ return name !== null && REPORT_NAME_RE.test(name) && argsIncludeBinding(current.arguments, caughtName);
1075
+ });
1076
+ }
1077
+ function tryBlockOf(catchNode) {
1078
+ return catchNode.parent.block;
1079
+ }
1080
+ function isSafeParseExpression(arg) {
1081
+ if (arg === null) {
1082
+ return false;
1083
+ }
1084
+ if (arg.type === import_utils10.AST_NODE_TYPES.CallExpression && arg.callee.type === import_utils10.AST_NODE_TYPES.MemberExpression && !arg.callee.computed && arg.callee.property.type === import_utils10.AST_NODE_TYPES.Identifier && arg.callee.property.name === "parse") {
1085
+ return true;
1086
+ }
1087
+ if (arg.type === import_utils10.AST_NODE_TYPES.NewExpression && arg.callee.type === import_utils10.AST_NODE_TYPES.Identifier) {
1088
+ return arg.callee.name === "RegExp" || arg.callee.name === "URL";
1089
+ }
1090
+ return false;
1091
+ }
1092
+ function tryReturnsSafeParse(catchNode) {
1093
+ return walkWithinScope(
1094
+ tryBlockOf(catchNode),
1095
+ (current) => current.type === import_utils10.AST_NODE_TYPES.ReturnStatement && isSafeParseExpression(current.argument)
1096
+ );
1097
+ }
1098
+ function enclosingFunctionBody(node) {
1099
+ let current = node.parent;
1100
+ while (current !== void 0 && current !== null) {
1101
+ if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === import_utils10.AST_NODE_TYPES.BlockStatement) {
1102
+ return current.body;
1103
+ }
1104
+ current = current.parent;
1105
+ }
1106
+ return null;
1107
+ }
1108
+ function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
1109
+ const functionBody = enclosingFunctionBody(catchNode);
1110
+ if (functionBody === null) {
1111
+ return false;
1112
+ }
1113
+ return walkWithinScope(functionBody, (current) => {
1114
+ if (current.type !== import_utils10.AST_NODE_TYPES.ReturnStatement) {
1115
+ return false;
1116
+ }
1117
+ if (isWithin(current, catchNode.body)) {
1118
+ return false;
1119
+ }
1120
+ return sentinelKind(current.argument) === kind;
1121
+ });
1122
+ }
1123
+ function isWithin(node, ancestor) {
1124
+ let current = node;
1125
+ while (current !== void 0 && current !== null) {
1126
+ if (current === ancestor) {
1127
+ return true;
1128
+ }
1129
+ current = current.parent;
1130
+ }
1131
+ return false;
1132
+ }
1133
+ var no_sentinel_return_on_catch_default = import_utils10.ESLintUtils.RuleCreator(
783
1134
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
784
1135
  )({
785
1136
  name: "no-sentinel-return-on-catch",
786
1137
  meta: {
787
1138
  type: "problem",
788
1139
  docs: {
789
- description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
1140
+ description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block, unless the error is logged/reported or the sentinel is the declared safe-parse/predicate contract."
790
1141
  },
791
1142
  schema: [],
792
1143
  messages: {
793
- noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel. Rethrow it, return a typed Result, or handle the error explicitly."
1144
+ noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel without logging it. Rethrow it, log/report it, or return a typed Result."
794
1145
  }
795
1146
  },
796
1147
  defaultOptions: [],
@@ -802,7 +1153,7 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
802
1153
  return;
803
1154
  }
804
1155
  const last = body[body.length - 1];
805
- if (last === void 0 || last.type !== import_utils9.AST_NODE_TYPES.ReturnStatement) {
1156
+ if (last === void 0 || last.type !== import_utils10.AST_NODE_TYPES.ReturnStatement) {
806
1157
  return;
807
1158
  }
808
1159
  if (!isSentinelArgument(last.argument)) {
@@ -811,6 +1162,17 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
811
1162
  if (containsThrow(node.body)) {
812
1163
  return;
813
1164
  }
1165
+ const caughtName = node.param?.type === import_utils10.AST_NODE_TYPES.Identifier ? node.param.name : null;
1166
+ if (logsOrReportsError(node.body, caughtName)) {
1167
+ return;
1168
+ }
1169
+ if (tryReturnsSafeParse(node)) {
1170
+ return;
1171
+ }
1172
+ const kind = sentinelKind(last.argument);
1173
+ if (kind !== null && functionReturnsSameSentinelKindElsewhere(node, kind)) {
1174
+ return;
1175
+ }
814
1176
  context.report({
815
1177
  node: last,
816
1178
  messageId: "noSentinelReturn"
@@ -821,14 +1183,112 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
821
1183
  });
822
1184
 
823
1185
  // src/rules/no-sequential-await.ts
824
- var import_utils10 = require("@typescript-eslint/utils");
1186
+ var import_utils11 = require("@typescript-eslint/utils");
1187
+ var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
1188
+ var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain|buffer|stream|teleport|chunk|\bqueue|drain/i;
825
1189
  function isFunctionLike(node) {
826
1190
  return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
827
1191
  }
828
1192
  function isLoop(node) {
829
1193
  return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
830
1194
  }
831
- var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
1195
+ function isNode2(value) {
1196
+ return typeof value === "object" && value !== null && typeof value.type === "string";
1197
+ }
1198
+ function visitScope(root, visit) {
1199
+ visit(root);
1200
+ for (const key of Object.keys(root)) {
1201
+ if (key === "parent") {
1202
+ continue;
1203
+ }
1204
+ const value = root[key];
1205
+ const children = Array.isArray(value) ? value : [value];
1206
+ for (const child of children) {
1207
+ if (isNode2(child) && !isFunctionLike(child) && !isLoop(child)) {
1208
+ visitScope(child, visit);
1209
+ }
1210
+ }
1211
+ }
1212
+ }
1213
+ function collectAwaits(root) {
1214
+ const awaits = [];
1215
+ visitScope(root, (node) => {
1216
+ if (node.type === "AwaitExpression") {
1217
+ awaits.push(node);
1218
+ }
1219
+ });
1220
+ return awaits;
1221
+ }
1222
+ function hasEarlyExit(root) {
1223
+ let found = false;
1224
+ visitScope(root, (node) => {
1225
+ if (node.type === "ReturnStatement" || node.type === "BreakStatement" || node.type === "ContinueStatement") {
1226
+ found = true;
1227
+ }
1228
+ });
1229
+ return found;
1230
+ }
1231
+ var TIMER_HELPER_RE = /^(sleep|timeout|delay|wait|pause|tick)$/i;
1232
+ function calleeName2(callee) {
1233
+ if (callee.type === "Identifier") return callee.name;
1234
+ if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
1235
+ return callee.property.name;
1236
+ }
1237
+ return null;
1238
+ }
1239
+ function isTimerYield(node) {
1240
+ const arg = node.argument;
1241
+ if (arg.type === "NewExpression" && arg.callee.type === "Identifier" && arg.callee.name === "Promise") {
1242
+ return true;
1243
+ }
1244
+ if (arg.type === "CallExpression") {
1245
+ const name = calleeName2(arg.callee);
1246
+ return name !== null && TIMER_HELPER_RE.test(name);
1247
+ }
1248
+ return false;
1249
+ }
1250
+ var QUEUE_DRAIN_METHODS = /^(shift|pop|dequeue|next|poll)$/;
1251
+ function isQueueDrain(node) {
1252
+ const arg = node.argument;
1253
+ return arg.type === "CallExpression" && arg.callee.type === "MemberExpression" && !arg.callee.computed && arg.callee.property.type === "Identifier" && QUEUE_DRAIN_METHODS.test(arg.callee.property.name);
1254
+ }
1255
+ function referencesName(root, name) {
1256
+ let found = false;
1257
+ visitScope(root, (node) => {
1258
+ if (node.type === "Identifier" && node.name === name) {
1259
+ found = true;
1260
+ }
1261
+ });
1262
+ return found;
1263
+ }
1264
+ function isThreadedAccumulator(node) {
1265
+ const parent = node.parent;
1266
+ let target = null;
1267
+ if (parent.type === "AssignmentExpression" && parent.operator === "=" && parent.right === node && parent.left.type === "Identifier") {
1268
+ target = parent.left.name;
1269
+ } else if (parent.type === "VariableDeclarator" && parent.init === node && parent.id.type === "Identifier") {
1270
+ target = parent.id.name;
1271
+ }
1272
+ if (target === null) {
1273
+ return false;
1274
+ }
1275
+ return referencesName(node.argument, target);
1276
+ }
1277
+ function shouldReport(awaits, earlyExit, iterableText) {
1278
+ if (awaits.length === 0) {
1279
+ return false;
1280
+ }
1281
+ if (earlyExit) {
1282
+ return false;
1283
+ }
1284
+ if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
1285
+ return false;
1286
+ }
1287
+ return awaits.some(
1288
+ (node) => !isTimerYield(node) && !isThreadedAccumulator(node) && !isQueueDrain(node)
1289
+ );
1290
+ }
1291
+ var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
832
1292
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
833
1293
  )({
834
1294
  name: "no-sequential-await",
@@ -844,53 +1304,35 @@ var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
844
1304
  },
845
1305
  defaultOptions: [],
846
1306
  create(context) {
847
- function findAwaitInScope(node) {
848
- if (node.type === "AwaitExpression") {
849
- return node;
1307
+ function loopParts(node) {
1308
+ if (node.type === "ForStatement") {
1309
+ return [node.body, node.test, node.update];
850
1310
  }
851
- if (isFunctionLike(node)) {
852
- return null;
1311
+ if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
1312
+ return [node.body];
853
1313
  }
854
- for (const key of Object.keys(node)) {
855
- if (key === "parent") {
856
- continue;
857
- }
858
- const value = node[key];
859
- if (Array.isArray(value)) {
860
- for (const child of value) {
861
- if (isNode4(child) && !isLoop(child)) {
862
- const found = findAwaitInScope(child);
863
- if (found) {
864
- return found;
865
- }
866
- }
867
- }
868
- } else if (isNode4(value) && !isLoop(value)) {
869
- const found = findAwaitInScope(value);
870
- if (found) {
871
- return found;
872
- }
873
- }
1314
+ return [node.body, node.test];
1315
+ }
1316
+ function iterableTextOf(node) {
1317
+ if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
1318
+ return context.sourceCode.getText(node.right);
874
1319
  }
875
1320
  return null;
876
1321
  }
877
- function isNode4(value) {
878
- return typeof value === "object" && value !== null && typeof value.type === "string";
879
- }
880
1322
  function checkLoop(node) {
881
- const parts = [node.body];
882
- if (node.type === "ForStatement") {
883
- parts.push(node.init, node.test, node.update);
884
- } else if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
885
- parts.push(node.right);
886
- } else {
887
- parts.push(node.test);
888
- }
889
- for (const part of parts) {
890
- if (part && !isLoop(part) && findAwaitInScope(part)) {
891
- context.report({ node, messageId: "noSequentialAwait" });
892
- return;
1323
+ const awaits = [];
1324
+ let earlyExit = false;
1325
+ for (const part of loopParts(node)) {
1326
+ if (part === null || isLoop(part)) {
1327
+ continue;
893
1328
  }
1329
+ awaits.push(...collectAwaits(part));
1330
+ if (!earlyExit && hasEarlyExit(part)) {
1331
+ earlyExit = true;
1332
+ }
1333
+ }
1334
+ if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
1335
+ context.report({ node, messageId: "noSequentialAwait" });
894
1336
  }
895
1337
  }
896
1338
  return {
@@ -903,13 +1345,35 @@ var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
903
1345
  return;
904
1346
  }
905
1347
  checkLoop(node);
1348
+ },
1349
+ CallExpression(node) {
1350
+ const callee = node.callee;
1351
+ if (callee.type !== "MemberExpression" || callee.computed) {
1352
+ return;
1353
+ }
1354
+ if (callee.property.type !== "Identifier" || !ARRAY_ITERATION_METHODS.has(callee.property.name)) {
1355
+ return;
1356
+ }
1357
+ const callback = node.arguments[0];
1358
+ if (callback === void 0 || !isFunctionLike(callback) || !("async" in callback && callback.async)) {
1359
+ return;
1360
+ }
1361
+ if (callee.property.name !== "forEach" && node.parent.type !== "ExpressionStatement") {
1362
+ return;
1363
+ }
1364
+ const awaits = collectAwaits(callback.body);
1365
+ const earlyExit = hasEarlyExit(callback.body);
1366
+ const iterableText = context.sourceCode.getText(callee.object);
1367
+ if (shouldReport(awaits, earlyExit, iterableText)) {
1368
+ context.report({ node, messageId: "noSequentialAwait" });
1369
+ }
906
1370
  }
907
1371
  };
908
1372
  }
909
1373
  });
910
1374
 
911
1375
  // src/rules/no-string-concat-in-loop.ts
912
- var import_utils11 = require("@typescript-eslint/utils");
1376
+ var import_utils12 = require("@typescript-eslint/utils");
913
1377
  var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
914
1378
  "ForStatement",
915
1379
  "ForOfStatement",
@@ -954,6 +1418,21 @@ function isStringInitializedVariable(variable) {
954
1418
  }
955
1419
  return isStringLiteralInit(declarator.init);
956
1420
  }
1421
+ function isConcatOperand(node, target) {
1422
+ if (node.type === "Identifier") {
1423
+ return node.name === target;
1424
+ }
1425
+ if (node.type === "BinaryExpression" && node.operator === "+") {
1426
+ return isConcatOperand(node.left, target) || isConcatOperand(node.right, target);
1427
+ }
1428
+ return false;
1429
+ }
1430
+ function isConcatOntoTarget(rhs, target) {
1431
+ if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
1432
+ return false;
1433
+ }
1434
+ return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
1435
+ }
957
1436
  function isInsideLoopBody(node) {
958
1437
  let child = node;
959
1438
  let parent = node.parent;
@@ -969,7 +1448,7 @@ function isInsideLoopBody(node) {
969
1448
  }
970
1449
  return false;
971
1450
  }
972
- var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
1451
+ var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
973
1452
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
974
1453
  )({
975
1454
  name: "no-string-concat-in-loop",
@@ -987,10 +1466,11 @@ var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
987
1466
  create(context) {
988
1467
  return {
989
1468
  AssignmentExpression(node) {
990
- if (node.operator !== "+=") {
1469
+ if (node.left.type !== "Identifier") {
991
1470
  return;
992
1471
  }
993
- if (node.left.type !== "Identifier") {
1472
+ const isAccumulation = node.operator === "+=" || node.operator === "=" && isConcatOntoTarget(node.right, node.left.name);
1473
+ if (!isAccumulation) {
994
1474
  return;
995
1475
  }
996
1476
  if (!isInsideLoopBody(node)) {
@@ -1014,7 +1494,7 @@ var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
1014
1494
  });
1015
1495
 
1016
1496
  // src/rules/no-unnecessary-use-client.ts
1017
- var import_utils12 = require("@typescript-eslint/utils");
1497
+ var import_utils13 = require("@typescript-eslint/utils");
1018
1498
  var HOOK_REGEX = /^use([A-Z]|$)/;
1019
1499
  var EVENT_PROP_REGEX = /^on[A-Z]/;
1020
1500
  var ERROR_FILE_REGEX = /\b(?:global-)?error\.[jt]sx?$/;
@@ -1037,16 +1517,16 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
1037
1517
  ]);
1038
1518
  var CLIENT_ONLY_PACKAGES_REGEX = /^(?:@radix-ui\/|framer-motion|react-dom|react-day-picker|@floating-ui\/|react-select|react-toastify|react-hook-form|recharts|react-dropzone|react-slick|react-swipeable|react-resizable|react-draggable|react-beautiful-dnd|@hello-pangea\/dnd|react-virtualized|react-window|@tanstack\/react-table|@tanstack\/react-query|react-redux|recoil|jotai|zustand|@tippyjs\/react|react-color|react-datepicker|next-themes|react-helmet|react-helmet-async|styled-components|@emotion\/)/;
1039
1519
  var isUseClientDirective = (node) => {
1040
- return node.type === import_utils12.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils12.AST_NODE_TYPES.Literal && node.expression.value === "use client";
1520
+ return node.type === import_utils13.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils13.AST_NODE_TYPES.Literal && node.expression.value === "use client";
1041
1521
  };
1042
1522
  var isGlobalReference = (node, context) => {
1043
1523
  if (!BROWSER_GLOBALS.has(node.name)) return false;
1044
1524
  const parent = node.parent;
1045
1525
  if (parent !== void 0) {
1046
- if (parent.type === import_utils12.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
1526
+ if (parent.type === import_utils13.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
1047
1527
  return false;
1048
1528
  }
1049
- if (parent.type === import_utils12.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
1529
+ if (parent.type === import_utils13.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
1050
1530
  return false;
1051
1531
  }
1052
1532
  if (parent.type.startsWith("TS")) {
@@ -1063,7 +1543,7 @@ var isGlobalReference = (node, context) => {
1063
1543
  }
1064
1544
  return true;
1065
1545
  };
1066
- var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1546
+ var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
1067
1547
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1068
1548
  )({
1069
1549
  name: "no-unnecessary-use-client",
@@ -1086,13 +1566,13 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1086
1566
  let directiveNode = null;
1087
1567
  let hasClientIndicator = false;
1088
1568
  const markIfHookOrContext = (callee) => {
1089
- if (callee.type === import_utils12.AST_NODE_TYPES.Identifier) {
1569
+ if (callee.type === import_utils13.AST_NODE_TYPES.Identifier) {
1090
1570
  if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
1091
1571
  hasClientIndicator = true;
1092
1572
  }
1093
1573
  return;
1094
1574
  }
1095
- if (callee.type === import_utils12.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils12.AST_NODE_TYPES.Identifier) {
1575
+ if (callee.type === import_utils13.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils13.AST_NODE_TYPES.Identifier) {
1096
1576
  const name = callee.property.name;
1097
1577
  if (HOOK_REGEX.test(name) || name === "createContext") {
1098
1578
  hasClientIndicator = true;
@@ -1102,7 +1582,7 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1102
1582
  return {
1103
1583
  Program(node) {
1104
1584
  for (const stmt of node.body) {
1105
- if (stmt.type !== import_utils12.AST_NODE_TYPES.ExpressionStatement) break;
1585
+ if (stmt.type !== import_utils13.AST_NODE_TYPES.ExpressionStatement) break;
1106
1586
  if (isUseClientDirective(stmt)) {
1107
1587
  directiveNode = stmt;
1108
1588
  break;
@@ -1115,7 +1595,7 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1115
1595
  },
1116
1596
  JSXAttribute(node) {
1117
1597
  if (directiveNode === null) return;
1118
- if (node.name.type === import_utils12.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
1598
+ if (node.name.type === import_utils13.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
1119
1599
  hasClientIndicator = true;
1120
1600
  }
1121
1601
  },
@@ -1164,8 +1644,8 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1164
1644
  });
1165
1645
 
1166
1646
  // src/rules/prefer-discriminated-union.ts
1167
- var import_utils13 = require("@typescript-eslint/utils");
1168
1647
  var import_utils14 = require("@typescript-eslint/utils");
1648
+ var import_utils15 = require("@typescript-eslint/utils");
1169
1649
  var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
1170
1650
  "success",
1171
1651
  "ok",
@@ -1175,26 +1655,26 @@ var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
1175
1655
  ]);
1176
1656
  var MIN_OPTIONAL_MEMBERS = 2;
1177
1657
  function getMemberName(member) {
1178
- if (member.type !== import_utils14.AST_NODE_TYPES.TSPropertySignature) {
1658
+ if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
1179
1659
  return null;
1180
1660
  }
1181
1661
  const { key } = member;
1182
- if (key.type === import_utils14.AST_NODE_TYPES.Identifier) {
1662
+ if (key.type === import_utils15.AST_NODE_TYPES.Identifier) {
1183
1663
  return key.name;
1184
1664
  }
1185
- if (key.type === import_utils14.AST_NODE_TYPES.Literal && typeof key.value === "string") {
1665
+ if (key.type === import_utils15.AST_NODE_TYPES.Literal && typeof key.value === "string") {
1186
1666
  return key.value;
1187
1667
  }
1188
1668
  return null;
1189
1669
  }
1190
1670
  function isBooleanTyped(member) {
1191
- return member.typeAnnotation?.typeAnnotation.type === import_utils14.AST_NODE_TYPES.TSBooleanKeyword;
1671
+ return member.typeAnnotation?.typeAnnotation.type === import_utils15.AST_NODE_TYPES.TSBooleanKeyword;
1192
1672
  }
1193
1673
  function looksLikeMutuallyExclusiveState(typeLiteral) {
1194
1674
  let hasStatusBoolean = false;
1195
1675
  let optionalCount = 0;
1196
1676
  for (const member of typeLiteral.members) {
1197
- if (member.type !== import_utils14.AST_NODE_TYPES.TSPropertySignature) {
1677
+ if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
1198
1678
  continue;
1199
1679
  }
1200
1680
  if (member.optional) {
@@ -1207,7 +1687,7 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
1207
1687
  }
1208
1688
  return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
1209
1689
  }
1210
- var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
1690
+ var prefer_discriminated_union_default = import_utils14.ESLintUtils.RuleCreator(
1211
1691
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1212
1692
  )({
1213
1693
  name: "prefer-discriminated-union",
@@ -1235,7 +1715,7 @@ var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
1235
1715
  TSInterfaceDeclaration(node) {
1236
1716
  const synthetic = {
1237
1717
  ...node.body,
1238
- type: import_utils14.AST_NODE_TYPES.TSTypeLiteral,
1718
+ type: import_utils15.AST_NODE_TYPES.TSTypeLiteral,
1239
1719
  members: node.body.body
1240
1720
  };
1241
1721
  checkTypeLiteral(synthetic, node);
@@ -1248,13 +1728,13 @@ var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
1248
1728
  });
1249
1729
 
1250
1730
  // src/rules/prefer-schema-for-api-payload.ts
1251
- var import_utils15 = require("@typescript-eslint/utils");
1731
+ var import_utils16 = require("@typescript-eslint/utils");
1252
1732
  var unwrap = (node) => {
1253
1733
  let current = node;
1254
1734
  while (current !== null && current !== void 0) {
1255
- if (current.type === import_utils15.AST_NODE_TYPES.TSAsExpression || current.type === import_utils15.AST_NODE_TYPES.TSTypeAssertion || current.type === import_utils15.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils15.AST_NODE_TYPES.TSSatisfiesExpression) {
1735
+ if (current.type === import_utils16.AST_NODE_TYPES.TSAsExpression || current.type === import_utils16.AST_NODE_TYPES.TSTypeAssertion || current.type === import_utils16.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils16.AST_NODE_TYPES.TSSatisfiesExpression) {
1256
1736
  current = current.expression;
1257
- } else if (current.type === import_utils15.AST_NODE_TYPES.ChainExpression) {
1737
+ } else if (current.type === import_utils16.AST_NODE_TYPES.ChainExpression) {
1258
1738
  current = current.expression;
1259
1739
  } else {
1260
1740
  break;
@@ -1265,18 +1745,18 @@ var unwrap = (node) => {
1265
1745
  var isJsonCall = (node) => {
1266
1746
  let current = unwrap(node);
1267
1747
  if (current === null) return false;
1268
- if (current.type === import_utils15.AST_NODE_TYPES.AwaitExpression) {
1748
+ if (current.type === import_utils16.AST_NODE_TYPES.AwaitExpression) {
1269
1749
  current = unwrap(current.argument);
1270
1750
  }
1271
- if (current === null || current.type !== import_utils15.AST_NODE_TYPES.CallExpression) {
1751
+ if (current === null || current.type !== import_utils16.AST_NODE_TYPES.CallExpression) {
1272
1752
  return false;
1273
1753
  }
1274
1754
  const callee = unwrap(current.callee);
1275
- if (callee === null || callee.type !== import_utils15.AST_NODE_TYPES.MemberExpression) {
1755
+ if (callee === null || callee.type !== import_utils16.AST_NODE_TYPES.MemberExpression) {
1276
1756
  return false;
1277
1757
  }
1278
1758
  const property = unwrap(callee.property);
1279
- return property !== null && property.type === import_utils15.AST_NODE_TYPES.Identifier && property.name === "json";
1759
+ return property !== null && property.type === import_utils16.AST_NODE_TYPES.Identifier && property.name === "json";
1280
1760
  };
1281
1761
  var findVariable2 = (scope, name) => {
1282
1762
  let current = scope;
@@ -1287,15 +1767,39 @@ var findVariable2 = (scope, name) => {
1287
1767
  }
1288
1768
  return null;
1289
1769
  };
1770
+ var GUARD_NAME_RE = /^is[A-Z]/;
1771
+ var isGuardTestPosition = (node) => {
1772
+ let current = node;
1773
+ let parent = current.parent;
1774
+ while (parent !== void 0 && parent !== null) {
1775
+ switch (parent.type) {
1776
+ case import_utils16.AST_NODE_TYPES.UnaryExpression:
1777
+ case import_utils16.AST_NODE_TYPES.LogicalExpression:
1778
+ case import_utils16.AST_NODE_TYPES.ChainExpression:
1779
+ current = parent;
1780
+ parent = parent.parent;
1781
+ continue;
1782
+ case import_utils16.AST_NODE_TYPES.IfStatement:
1783
+ case import_utils16.AST_NODE_TYPES.ConditionalExpression:
1784
+ case import_utils16.AST_NODE_TYPES.WhileStatement:
1785
+ case import_utils16.AST_NODE_TYPES.DoWhileStatement:
1786
+ case import_utils16.AST_NODE_TYPES.ForStatement:
1787
+ return parent.test === current;
1788
+ default:
1789
+ return false;
1790
+ }
1791
+ }
1792
+ return false;
1793
+ };
1290
1794
  var isUnvalidatedVariableRef = (node, scope, tracked) => {
1291
1795
  const unwrapped = unwrap(node);
1292
- if (unwrapped === null || unwrapped.type !== import_utils15.AST_NODE_TYPES.Identifier) {
1796
+ if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
1293
1797
  return false;
1294
1798
  }
1295
1799
  const variable = findVariable2(scope, unwrapped.name);
1296
1800
  return variable !== null && tracked.has(variable);
1297
1801
  };
1298
- var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreator(
1802
+ var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreator(
1299
1803
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1300
1804
  )({
1301
1805
  name: "prefer-schema-for-api-payload",
@@ -1323,11 +1827,11 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1323
1827
  return {
1324
1828
  VariableDeclarator(node) {
1325
1829
  const scope = context.sourceCode.getScope(node);
1326
- if (node.id.type === import_utils15.AST_NODE_TYPES.Identifier) {
1830
+ if (node.id.type === import_utils16.AST_NODE_TYPES.Identifier) {
1327
1831
  trackInitializer(node);
1328
1832
  return;
1329
1833
  }
1330
- if (node.id.type === import_utils15.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils15.AST_NODE_TYPES.ArrayPattern) {
1834
+ if (node.id.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
1331
1835
  if (isJsonCall(node.init)) {
1332
1836
  context.report({ node: node.id, messageId: "unparsedJsonAccess" });
1333
1837
  return;
@@ -1339,7 +1843,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1339
1843
  },
1340
1844
  AssignmentExpression(node) {
1341
1845
  const scope = context.sourceCode.getScope(node);
1342
- if (node.left.type === import_utils15.AST_NODE_TYPES.Identifier) {
1846
+ if (node.left.type === import_utils16.AST_NODE_TYPES.Identifier) {
1343
1847
  const variable = findVariable2(scope, node.left.name);
1344
1848
  if (variable === null) return;
1345
1849
  if (isJsonCall(node.right)) {
@@ -1349,7 +1853,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1349
1853
  }
1350
1854
  return;
1351
1855
  }
1352
- if (node.left.type === import_utils15.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils15.AST_NODE_TYPES.ArrayPattern) {
1856
+ if (node.left.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
1353
1857
  if (isJsonCall(node.right)) {
1354
1858
  context.report({
1355
1859
  node: node.left,
@@ -1365,18 +1869,34 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1365
1869
  }
1366
1870
  }
1367
1871
  },
1872
+ CallExpression(node) {
1873
+ if (node.callee.type !== import_utils16.AST_NODE_TYPES.Identifier) return;
1874
+ if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {
1875
+ return;
1876
+ }
1877
+ const scope = context.sourceCode.getScope(node);
1878
+ for (const arg of node.arguments) {
1879
+ if (arg.type === import_utils16.AST_NODE_TYPES.SpreadElement) continue;
1880
+ const unwrapped = unwrap(arg);
1881
+ if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
1882
+ continue;
1883
+ }
1884
+ const variable = findVariable2(scope, unwrapped.name);
1885
+ if (variable !== null) unvalidatedVariables.delete(variable);
1886
+ }
1887
+ },
1368
1888
  MemberExpression(node) {
1369
1889
  const scope = context.sourceCode.getScope(node);
1370
1890
  const obj = unwrap(node.object);
1371
1891
  if (isJsonCall(obj)) {
1372
1892
  const parent = node.parent;
1373
- if (parent.type === import_utils15.AST_NODE_TYPES.CallExpression && parent.callee === node && node.property.type === import_utils15.AST_NODE_TYPES.Identifier && (node.property.name === "parse" || node.property.name === "safeParse")) {
1893
+ if (parent.type === import_utils16.AST_NODE_TYPES.CallExpression && parent.callee === node && node.property.type === import_utils16.AST_NODE_TYPES.Identifier && (node.property.name === "parse" || node.property.name === "safeParse")) {
1374
1894
  return;
1375
1895
  }
1376
1896
  context.report({ node, messageId: "unparsedJsonAccess" });
1377
1897
  return;
1378
1898
  }
1379
- if (obj !== null && obj.type === import_utils15.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
1899
+ if (obj !== null && obj.type === import_utils16.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
1380
1900
  context.report({ node, messageId: "unparsedJsonAccess" });
1381
1901
  const variable = findVariable2(scope, obj.name);
1382
1902
  if (variable !== null) {
@@ -1389,7 +1909,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1389
1909
  });
1390
1910
 
1391
1911
  // src/rules/prefer-semantic-colors.ts
1392
- var import_utils16 = require("@typescript-eslint/utils");
1912
+ var import_utils17 = require("@typescript-eslint/utils");
1393
1913
 
1394
1914
  // src/rules/_tailwind.ts
1395
1915
  var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
@@ -1426,12 +1946,41 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
1426
1946
  "lightingColor"
1427
1947
  ]);
1428
1948
  var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
1949
+ var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
1950
+ var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
1951
+ "mask",
1952
+ "clipPath",
1953
+ "defs",
1954
+ "pattern",
1955
+ "linearGradient",
1956
+ "radialGradient"
1957
+ ]);
1958
+ var SVG_EXEMPT_COLOR_VALUES = /* @__PURE__ */ new Set([
1959
+ "#fff",
1960
+ "#ffffff",
1961
+ "#000",
1962
+ "#000000",
1963
+ "transparent",
1964
+ "none",
1965
+ "currentcolor",
1966
+ "inherit"
1967
+ ]);
1968
+ var isInsideSvg = (node) => {
1969
+ let current = node.parent;
1970
+ while (current !== void 0 && current !== null) {
1971
+ if (current.type === import_utils17.AST_NODE_TYPES.JSXElement && current.openingElement.name.type === import_utils17.AST_NODE_TYPES.JSXIdentifier && (current.openingElement.name.name === "svg" || SVG_DEFS_CONTAINERS.has(current.openingElement.name.name))) {
1972
+ return true;
1973
+ }
1974
+ current = current.parent;
1975
+ }
1976
+ return false;
1977
+ };
1429
1978
  var propName = (key) => {
1430
- if (key.type === import_utils16.AST_NODE_TYPES.Identifier) return key.name;
1431
- if (key.type === import_utils16.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
1979
+ if (key.type === import_utils17.AST_NODE_TYPES.Identifier) return key.name;
1980
+ if (key.type === import_utils17.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
1432
1981
  return null;
1433
1982
  };
1434
- var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1983
+ var prefer_semantic_colors_default = import_utils17.ESLintUtils.RuleCreator(
1435
1984
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
1436
1985
  )({
1437
1986
  name: "prefer-semantic-colors",
@@ -1449,6 +1998,7 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1449
1998
  },
1450
1999
  defaultOptions: [],
1451
2000
  create(context) {
2001
+ if (STORIES_FILE_RE.test(context.filename)) return {};
1452
2002
  const reportClasses = (value, node) => {
1453
2003
  for (const token of classTokens(value)) {
1454
2004
  const base = tailwindBase(token);
@@ -1462,27 +2012,27 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1462
2012
  const checkClassNode = (node) => {
1463
2013
  if (node === null) return;
1464
2014
  switch (node.type) {
1465
- case import_utils16.AST_NODE_TYPES.Literal:
2015
+ case import_utils17.AST_NODE_TYPES.Literal:
1466
2016
  if (typeof node.value === "string") reportClasses(node.value, node);
1467
2017
  break;
1468
- case import_utils16.AST_NODE_TYPES.TemplateLiteral:
2018
+ case import_utils17.AST_NODE_TYPES.TemplateLiteral:
1469
2019
  for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? "", quasi);
1470
2020
  break;
1471
- case import_utils16.AST_NODE_TYPES.ArrayExpression:
2021
+ case import_utils17.AST_NODE_TYPES.ArrayExpression:
1472
2022
  for (const element of node.elements) {
1473
- if (element !== null && element.type !== import_utils16.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
2023
+ if (element !== null && element.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
1474
2024
  }
1475
2025
  break;
1476
- case import_utils16.AST_NODE_TYPES.ObjectExpression:
2026
+ case import_utils17.AST_NODE_TYPES.ObjectExpression:
1477
2027
  for (const property of node.properties) {
1478
- if (property.type === import_utils16.AST_NODE_TYPES.Property) checkClassNode(property.value);
2028
+ if (property.type === import_utils17.AST_NODE_TYPES.Property) checkClassNode(property.value);
1479
2029
  }
1480
2030
  break;
1481
- case import_utils16.AST_NODE_TYPES.ConditionalExpression:
2031
+ case import_utils17.AST_NODE_TYPES.ConditionalExpression:
1482
2032
  checkClassNode(node.consequent);
1483
2033
  checkClassNode(node.alternate);
1484
2034
  break;
1485
- case import_utils16.AST_NODE_TYPES.LogicalExpression:
2035
+ case import_utils17.AST_NODE_TYPES.LogicalExpression:
1486
2036
  checkClassNode(node.right);
1487
2037
  break;
1488
2038
  default:
@@ -1490,29 +2040,29 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1490
2040
  }
1491
2041
  };
1492
2042
  const checkColorValueNode = (node) => {
1493
- if (node.type === import_utils16.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
2043
+ if (node.type === import_utils17.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
1494
2044
  context.report({ node, messageId: "inlineColor", data: { value: node.value } });
1495
2045
  }
1496
2046
  };
1497
2047
  return {
1498
2048
  "JSXAttribute[name.name='className']"(node) {
1499
2049
  if (node.value === null) return;
1500
- if (node.value.type === import_utils16.AST_NODE_TYPES.Literal) checkClassNode(node.value);
1501
- else if (node.value.type === import_utils16.AST_NODE_TYPES.JSXExpressionContainer) {
1502
- if (node.value.expression.type !== import_utils16.AST_NODE_TYPES.JSXEmptyExpression) {
2050
+ if (node.value.type === import_utils17.AST_NODE_TYPES.Literal) checkClassNode(node.value);
2051
+ else if (node.value.type === import_utils17.AST_NODE_TYPES.JSXExpressionContainer) {
2052
+ if (node.value.expression.type !== import_utils17.AST_NODE_TYPES.JSXEmptyExpression) {
1503
2053
  checkClassNode(node.value.expression);
1504
2054
  }
1505
2055
  }
1506
2056
  },
1507
2057
  CallExpression(node) {
1508
- if (node.callee.type === import_utils16.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
2058
+ if (node.callee.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
1509
2059
  for (const arg of node.arguments) {
1510
- if (arg.type !== import_utils16.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
2060
+ if (arg.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
1511
2061
  }
1512
2062
  }
1513
2063
  },
1514
2064
  VariableDeclarator(node) {
1515
- if (node.id.type === import_utils16.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
2065
+ if (node.id.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
1516
2066
  checkClassNode(node.init);
1517
2067
  }
1518
2068
  },
@@ -1520,9 +2070,16 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1520
2070
  const name = propName(node.key);
1521
2071
  if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);
1522
2072
  },
1523
- // SVG presentation attributes: <path fill="#000" stroke="#fff" />
2073
+ // SVG presentation attributes: <path fill="#7c3aed" stroke="#7c3aed" />.
2074
+ // Neutral drawing literals and anything inside an SVG defs container are
2075
+ // structural, not UI tokens, so they never fire.
1524
2076
  "JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
1525
- if (node.value?.type === import_utils16.AST_NODE_TYPES.Literal) checkColorValueNode(node.value);
2077
+ if (node.value?.type !== import_utils17.AST_NODE_TYPES.Literal) return;
2078
+ if (typeof node.value.value === "string" && SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())) {
2079
+ return;
2080
+ }
2081
+ if (isInsideSvg(node)) return;
2082
+ checkColorValueNode(node.value);
1526
2083
  },
1527
2084
  // Inline style objects: style={{ color: "#111827", backgroundColor: "#fff" }}
1528
2085
  "JSXAttribute[name.name='style'] ObjectExpression > Property"(node) {
@@ -1534,7 +2091,7 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1534
2091
  });
1535
2092
 
1536
2093
  // src/rules/prefer-server-actions.ts
1537
- var import_utils17 = require("@typescript-eslint/utils");
2094
+ var import_utils18 = require("@typescript-eslint/utils");
1538
2095
  var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
1539
2096
  var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
1540
2097
  var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
@@ -1613,7 +2170,7 @@ function getPropertyNode(objNode, propName2) {
1613
2170
  }
1614
2171
  return null;
1615
2172
  }
1616
- var prefer_server_actions_default = import_utils17.ESLintUtils.RuleCreator(
2173
+ var prefer_server_actions_default = import_utils18.ESLintUtils.RuleCreator(
1617
2174
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1618
2175
  )({
1619
2176
  name: "prefer-server-actions",
@@ -1681,14 +2238,38 @@ var prefer_server_actions_default = import_utils17.ESLintUtils.RuleCreator(
1681
2238
  });
1682
2239
 
1683
2240
  // src/rules/prefer-shadcn.ts
1684
- var import_utils18 = require("@typescript-eslint/utils");
2241
+ var import_utils19 = require("@typescript-eslint/utils");
1685
2242
  var REPLACEMENTS = {
1686
- input: "Input",
1687
2243
  select: "Select",
1688
2244
  textarea: "Textarea",
1689
2245
  dialog: "Dialog"
1690
2246
  };
1691
- var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
2247
+ var INPUT_TYPE_REPLACEMENTS = {
2248
+ checkbox: "Checkbox",
2249
+ radio: "RadioGroup",
2250
+ range: "Slider"
2251
+ };
2252
+ var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
2253
+ var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2254
+ var literalTypeAttr = (node) => {
2255
+ for (const attribute of node.attributes) {
2256
+ if (attribute.type !== import_utils19.AST_NODE_TYPES.JSXAttribute || attribute.name.type !== import_utils19.AST_NODE_TYPES.JSXIdentifier || attribute.name.name !== "type") {
2257
+ continue;
2258
+ }
2259
+ if (attribute.value?.type === import_utils19.AST_NODE_TYPES.Literal && typeof attribute.value.value === "string") {
2260
+ return { kind: "literal", value: attribute.value.value.toLowerCase() };
2261
+ }
2262
+ return { kind: "dynamic" };
2263
+ }
2264
+ return null;
2265
+ };
2266
+ var resolveInputReplacement = (node) => {
2267
+ const typeAttr = literalTypeAttr(node);
2268
+ if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
2269
+ if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
2270
+ return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
2271
+ };
2272
+ var prefer_shadcn_default = import_utils19.ESLintUtils.RuleCreator(
1692
2273
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1693
2274
  )({
1694
2275
  name: "prefer-shadcn",
@@ -1710,8 +2291,8 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
1710
2291
  return;
1711
2292
  }
1712
2293
  const elementName = node.name.name;
1713
- const replacement = REPLACEMENTS[elementName];
1714
- if (replacement === void 0) {
2294
+ const replacement = elementName === "input" ? resolveInputReplacement(node) : REPLACEMENTS[elementName];
2295
+ if (replacement === void 0 || replacement === null) {
1715
2296
  return;
1716
2297
  }
1717
2298
  context.report({
@@ -1720,7 +2301,7 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
1720
2301
  data: {
1721
2302
  element: elementName,
1722
2303
  replacement,
1723
- lowercase: elementName
2304
+ lowercase: kebabCase(replacement)
1724
2305
  }
1725
2306
  });
1726
2307
  }
@@ -1729,41 +2310,57 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
1729
2310
  });
1730
2311
 
1731
2312
  // src/rules/require-assert-never.ts
1732
- var import_utils19 = require("@typescript-eslint/utils");
2313
+ var import_utils20 = require("@typescript-eslint/utils");
1733
2314
  var isAssertNeverCall = (expression) => {
1734
- if (expression.type !== import_utils19.AST_NODE_TYPES.CallExpression) return false;
2315
+ if (expression.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
1735
2316
  const callee = expression.callee;
1736
- if (callee.type === import_utils19.AST_NODE_TYPES.Identifier) {
2317
+ if (callee.type === import_utils20.AST_NODE_TYPES.Identifier) {
1737
2318
  return callee.name === "assertNever";
1738
2319
  }
1739
- if (callee.type === import_utils19.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils19.AST_NODE_TYPES.Identifier) {
2320
+ if (callee.type === import_utils20.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils20.AST_NODE_TYPES.Identifier) {
1740
2321
  return callee.property.name === "assertNever";
1741
2322
  }
1742
2323
  return false;
1743
2324
  };
1744
2325
  var statementContainsAssertNever = (statement) => {
1745
- if (statement.type === import_utils19.AST_NODE_TYPES.ExpressionStatement) {
2326
+ if (statement.type === import_utils20.AST_NODE_TYPES.ExpressionStatement) {
1746
2327
  return isAssertNeverCall(statement.expression);
1747
2328
  }
1748
- if (statement.type === import_utils19.AST_NODE_TYPES.ThrowStatement) {
2329
+ if (statement.type === import_utils20.AST_NODE_TYPES.ThrowStatement) {
1749
2330
  return isAssertNeverCall(statement.argument);
1750
2331
  }
1751
- if (statement.type === import_utils19.AST_NODE_TYPES.ReturnStatement) {
2332
+ if (statement.type === import_utils20.AST_NODE_TYPES.ReturnStatement) {
1752
2333
  return statement.argument !== null && isAssertNeverCall(statement.argument);
1753
2334
  }
1754
- if (statement.type === import_utils19.AST_NODE_TYPES.BlockStatement) {
2335
+ if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
1755
2336
  return statement.body.some(statementContainsAssertNever);
1756
2337
  }
1757
2338
  return false;
1758
2339
  };
1759
2340
  var isRuntimeHandlingStatement = (statement) => {
1760
- if (statement.type === import_utils19.AST_NODE_TYPES.EmptyStatement) return false;
1761
- if (statement.type === import_utils19.AST_NODE_TYPES.BlockStatement) {
2341
+ if (statement.type === import_utils20.AST_NODE_TYPES.EmptyStatement) return false;
2342
+ if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
1762
2343
  return statement.body.some(isRuntimeHandlingStatement);
1763
2344
  }
1764
2345
  return true;
1765
2346
  };
1766
- var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
2347
+ var isFallthroughDefault = (node, defaultIndex) => {
2348
+ const defaultCase = node.cases[defaultIndex];
2349
+ return defaultCase !== void 0 && defaultCase.consequent.length === 0 && defaultIndex < node.cases.length - 1;
2350
+ };
2351
+ var isCommentOnlyNoopDefault = (defaultCase, sourceCode) => {
2352
+ if (defaultCase.consequent.length === 0) {
2353
+ const defaultToken = sourceCode.getFirstToken(defaultCase);
2354
+ const colonToken = defaultToken ? sourceCode.getTokenAfter(defaultToken) : null;
2355
+ return colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0;
2356
+ }
2357
+ const only = defaultCase.consequent[0];
2358
+ if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === import_utils20.AST_NODE_TYPES.BlockStatement && only.body.length === 0) {
2359
+ return sourceCode.getCommentsInside(only).length > 0;
2360
+ }
2361
+ return false;
2362
+ };
2363
+ var require_assert_never_default = import_utils20.ESLintUtils.RuleCreator(
1767
2364
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1768
2365
  )({
1769
2366
  name: "require-assert-never",
@@ -1781,12 +2378,16 @@ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
1781
2378
  create(context) {
1782
2379
  return {
1783
2380
  SwitchStatement(node) {
1784
- const defaultCase = node.cases.find(
2381
+ const defaultIndex = node.cases.findIndex(
1785
2382
  (caseNode) => caseNode.test === null
1786
2383
  );
1787
- if (!defaultCase) return;
2384
+ if (defaultIndex === -1) return;
2385
+ const defaultCase = node.cases[defaultIndex];
2386
+ if (defaultCase === void 0) return;
1788
2387
  if (defaultCase.consequent.some(statementContainsAssertNever)) return;
1789
2388
  if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;
2389
+ if (isFallthroughDefault(node, defaultIndex)) return;
2390
+ if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;
1790
2391
  context.report({
1791
2392
  node: defaultCase,
1792
2393
  messageId: "missingAssertNever"
@@ -1797,19 +2398,19 @@ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
1797
2398
  });
1798
2399
 
1799
2400
  // src/rules/require-zod-form-validation.ts
1800
- var import_utils20 = require("@typescript-eslint/utils");
2401
+ var import_utils21 = require("@typescript-eslint/utils");
1801
2402
  var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
1802
2403
  var looksLikeZodSchema = (node) => {
1803
2404
  let current = node;
1804
2405
  while (true) {
1805
- if (current.type === import_utils20.AST_NODE_TYPES.Identifier) {
2406
+ if (current.type === import_utils21.AST_NODE_TYPES.Identifier) {
1806
2407
  return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
1807
2408
  }
1808
- if (current.type === import_utils20.AST_NODE_TYPES.CallExpression) {
2409
+ if (current.type === import_utils21.AST_NODE_TYPES.CallExpression) {
1809
2410
  current = current.callee;
1810
2411
  continue;
1811
2412
  }
1812
- if (current.type === import_utils20.AST_NODE_TYPES.MemberExpression) {
2413
+ if (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
1813
2414
  current = current.object;
1814
2415
  continue;
1815
2416
  }
@@ -1817,25 +2418,25 @@ var looksLikeZodSchema = (node) => {
1817
2418
  }
1818
2419
  };
1819
2420
  var isZodParseCall = (node) => {
1820
- if (node.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
2421
+ if (node.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
1821
2422
  const callee = node.callee;
1822
- if (callee.type !== import_utils20.AST_NODE_TYPES.MemberExpression) return false;
2423
+ if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
1823
2424
  if (callee.computed) return false;
1824
- if (callee.property.type !== import_utils20.AST_NODE_TYPES.Identifier) return false;
2425
+ if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
1825
2426
  const method = callee.property.name;
1826
2427
  if (method !== "parse" && method !== "safeParse") return false;
1827
2428
  return looksLikeZodSchema(callee.object);
1828
2429
  };
1829
2430
  var isFormDataMethodCall = (node) => {
1830
2431
  let current = node;
1831
- if (current.type === import_utils20.AST_NODE_TYPES.AwaitExpression) {
2432
+ if (current.type === import_utils21.AST_NODE_TYPES.AwaitExpression) {
1832
2433
  current = current.argument;
1833
2434
  }
1834
- if (current.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
2435
+ if (current.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
1835
2436
  const callee = current.callee;
1836
- return callee.type === import_utils20.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils20.AST_NODE_TYPES.Identifier && callee.property.name === "formData";
2437
+ return callee.type === import_utils21.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils21.AST_NODE_TYPES.Identifier && callee.property.name === "formData";
1837
2438
  };
1838
- var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator(
2439
+ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator(
1839
2440
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1840
2441
  )({
1841
2442
  name: "require-zod-form-validation",
@@ -1852,14 +2453,14 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
1852
2453
  defaultOptions: [],
1853
2454
  create(context) {
1854
2455
  const isFormSourceIdentifier = (node) => {
1855
- if (node.type !== import_utils20.AST_NODE_TYPES.Identifier) return false;
2456
+ if (node.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
1856
2457
  if (/formdata/i.test(node.name)) return true;
1857
2458
  let scope = context.sourceCode.getScope(node);
1858
2459
  while (scope !== null) {
1859
2460
  const variable = scope.set.get(node.name);
1860
2461
  if (variable !== void 0 && variable.defs.length === 1) {
1861
2462
  const def = variable.defs[0];
1862
- if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils20.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
2463
+ if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils21.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
1863
2464
  return isFormDataMethodCall(def.node.init);
1864
2465
  }
1865
2466
  return false;
@@ -1870,8 +2471,8 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
1870
2471
  };
1871
2472
  const isFormDataGetCall = (node) => {
1872
2473
  const callee = node.callee;
1873
- if (callee.type !== import_utils20.AST_NODE_TYPES.MemberExpression) return false;
1874
- if (callee.property.type !== import_utils20.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
2474
+ if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
2475
+ if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
1875
2476
  return false;
1876
2477
  }
1877
2478
  return isFormSourceIdentifier(callee.object);
@@ -1894,15 +2495,15 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
1894
2495
  });
1895
2496
 
1896
2497
  // src/rules/zod-naming-convention.ts
1897
- var import_utils21 = require("@typescript-eslint/utils");
2498
+ var import_utils22 = require("@typescript-eslint/utils");
1898
2499
  var calleeChainStartsWithZ = (node) => {
1899
2500
  let current = node;
1900
- while (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
2501
+ while (current.type === import_utils22.AST_NODE_TYPES.MemberExpression) {
1901
2502
  const receiver = current.object;
1902
- if (receiver.type === import_utils21.AST_NODE_TYPES.Identifier && receiver.name === "z") {
2503
+ if (receiver.type === import_utils22.AST_NODE_TYPES.Identifier && receiver.name === "z") {
1903
2504
  return true;
1904
2505
  }
1905
- if (receiver.type === import_utils21.AST_NODE_TYPES.CallExpression) {
2506
+ if (receiver.type === import_utils22.AST_NODE_TYPES.CallExpression) {
1906
2507
  current = receiver.callee;
1907
2508
  continue;
1908
2509
  }
@@ -1910,7 +2511,7 @@ var calleeChainStartsWithZ = (node) => {
1910
2511
  }
1911
2512
  return false;
1912
2513
  };
1913
- var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
2514
+ var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
1914
2515
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1915
2516
  )({
1916
2517
  name: "zod-naming-convention",
@@ -1930,11 +2531,11 @@ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
1930
2531
  VariableDeclarator(node) {
1931
2532
  const init = node.init;
1932
2533
  if (init === null || init === void 0) return;
1933
- if (init.type !== import_utils21.AST_NODE_TYPES.CallExpression) return;
2534
+ if (init.type !== import_utils22.AST_NODE_TYPES.CallExpression) return;
1934
2535
  const callee = init.callee;
1935
- if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return;
2536
+ if (callee.type !== import_utils22.AST_NODE_TYPES.MemberExpression) return;
1936
2537
  if (!calleeChainStartsWithZ(callee)) return;
1937
- if (node.id.type !== import_utils21.AST_NODE_TYPES.Identifier) return;
2538
+ if (node.id.type !== import_utils22.AST_NODE_TYPES.Identifier) return;
1938
2539
  const variableName = node.id.name;
1939
2540
  if (variableName.startsWith("Z")) return;
1940
2541
  context.report({
@@ -1947,7 +2548,7 @@ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
1947
2548
  });
1948
2549
 
1949
2550
  // src/rules/no-cors-wildcard-with-credentials.ts
1950
- var import_utils22 = require("@typescript-eslint/utils");
2551
+ var import_utils23 = require("@typescript-eslint/utils");
1951
2552
  var ACAO_HEADER = "access-control-allow-origin";
1952
2553
  var ACAC_HEADER = "access-control-allow-credentials";
1953
2554
  var HEADER_SET_METHODS = /* @__PURE__ */ new Set(["setheader", "set", "append"]);
@@ -1979,17 +2580,17 @@ function subtreeContainsStarLiteral(node) {
1979
2580
  const value = node[key];
1980
2581
  if (Array.isArray(value)) {
1981
2582
  for (const child of value) {
1982
- if (isNode2(child) && subtreeContainsStarLiteral(child)) {
2583
+ if (isNode3(child) && subtreeContainsStarLiteral(child)) {
1983
2584
  return true;
1984
2585
  }
1985
2586
  }
1986
- } else if (isNode2(value) && subtreeContainsStarLiteral(value)) {
2587
+ } else if (isNode3(value) && subtreeContainsStarLiteral(value)) {
1987
2588
  return true;
1988
2589
  }
1989
2590
  }
1990
2591
  return false;
1991
2592
  }
1992
- function isNode2(value) {
2593
+ function isNode3(value) {
1993
2594
  return typeof value === "object" && value !== null && typeof value.type === "string";
1994
2595
  }
1995
2596
  function propertyKeyName(prop) {
@@ -2005,7 +2606,7 @@ function propertyKeyName(prop) {
2005
2606
  }
2006
2607
  return void 0;
2007
2608
  }
2008
- function calleeName(node) {
2609
+ function calleeName3(node) {
2009
2610
  const callee = node.callee;
2010
2611
  if (callee.type === "Identifier") {
2011
2612
  return callee.name;
@@ -2016,7 +2617,7 @@ function calleeName(node) {
2016
2617
  return void 0;
2017
2618
  }
2018
2619
  function isCorsWildcardCredentialsCall(node) {
2019
- const name = calleeName(node);
2620
+ const name = calleeName3(node);
2020
2621
  if (name === void 0 || name.toLowerCase() !== "cors") {
2021
2622
  return false;
2022
2623
  }
@@ -2089,7 +2690,7 @@ function enclosingScope(node) {
2089
2690
  }
2090
2691
  return void 0;
2091
2692
  }
2092
- var no_cors_wildcard_with_credentials_default = import_utils22.ESLintUtils.RuleCreator(
2693
+ var no_cors_wildcard_with_credentials_default = import_utils23.ESLintUtils.RuleCreator(
2093
2694
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2094
2695
  )({
2095
2696
  name: "no-cors-wildcard-with-credentials",
@@ -2157,12 +2758,12 @@ var no_cors_wildcard_with_credentials_default = import_utils22.ESLintUtils.RuleC
2157
2758
  });
2158
2759
 
2159
2760
  // src/rules/no-fat-try-blocks.ts
2160
- var import_utils23 = require("@typescript-eslint/utils");
2761
+ var import_utils24 = require("@typescript-eslint/utils");
2161
2762
  var MAX_TRY_BODY_STATEMENTS = 3;
2162
2763
  var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set([
2163
- import_utils23.AST_NODE_TYPES.FunctionDeclaration,
2164
- import_utils23.AST_NODE_TYPES.FunctionExpression,
2165
- import_utils23.AST_NODE_TYPES.ArrowFunctionExpression
2764
+ import_utils24.AST_NODE_TYPES.FunctionDeclaration,
2765
+ import_utils24.AST_NODE_TYPES.FunctionExpression,
2766
+ import_utils24.AST_NODE_TYPES.ArrowFunctionExpression
2166
2767
  ]);
2167
2768
  var PURE_METHODS = /* @__PURE__ */ new Set([
2168
2769
  "map",
@@ -2255,25 +2856,25 @@ var PURE_CONSTRUCTORS = /* @__PURE__ */ new Set([
2255
2856
  "Response",
2256
2857
  "AbortController"
2257
2858
  ]);
2258
- function isNode3(value) {
2859
+ function isNode4(value) {
2259
2860
  return typeof value === "object" && value !== null && typeof value.type === "string";
2260
2861
  }
2261
2862
  function isPureCall(node) {
2262
2863
  const callee = node.callee;
2263
- if (callee.type !== import_utils23.AST_NODE_TYPES.MemberExpression) {
2864
+ if (callee.type !== import_utils24.AST_NODE_TYPES.MemberExpression) {
2264
2865
  return false;
2265
2866
  }
2266
2867
  const property = callee.property;
2267
- if (property.type !== import_utils23.AST_NODE_TYPES.Identifier) {
2868
+ if (property.type !== import_utils24.AST_NODE_TYPES.Identifier) {
2268
2869
  return false;
2269
2870
  }
2270
- if (callee.object.type === import_utils23.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
2871
+ if (callee.object.type === import_utils24.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
2271
2872
  return true;
2272
2873
  }
2273
2874
  return PURE_METHODS.has(property.name);
2274
2875
  }
2275
2876
  function isPureNew(node) {
2276
- return node.callee.type === import_utils23.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
2877
+ return node.callee.type === import_utils24.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
2277
2878
  }
2278
2879
  function subtreeMatches(stmt, predicate) {
2279
2880
  let found = false;
@@ -2295,11 +2896,11 @@ function subtreeMatches(stmt, predicate) {
2295
2896
  const value = current[key];
2296
2897
  if (Array.isArray(value)) {
2297
2898
  for (const child of value) {
2298
- if (isNode3(child)) {
2899
+ if (isNode4(child)) {
2299
2900
  visit(child);
2300
2901
  }
2301
2902
  }
2302
- } else if (isNode3(value)) {
2903
+ } else if (isNode4(value)) {
2303
2904
  visit(value);
2304
2905
  }
2305
2906
  if (found) {
@@ -2310,14 +2911,14 @@ function subtreeMatches(stmt, predicate) {
2310
2911
  visit(stmt);
2311
2912
  return found;
2312
2913
  }
2313
- var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === import_utils23.AST_NODE_TYPES.AwaitExpression);
2914
+ var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === import_utils24.AST_NODE_TYPES.AwaitExpression);
2314
2915
  var hasThrowingCallOrNew = (stmt) => subtreeMatches(
2315
2916
  stmt,
2316
- (n) => n.type === import_utils23.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils23.AST_NODE_TYPES.NewExpression && !isPureNew(n)
2917
+ (n) => n.type === import_utils24.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils24.AST_NODE_TYPES.NewExpression && !isPureNew(n)
2317
2918
  );
2318
2919
  function unwrap2(expr) {
2319
2920
  let current = expr;
2320
- while (current.type === import_utils23.AST_NODE_TYPES.ChainExpression || current.type === import_utils23.AST_NODE_TYPES.TSNonNullExpression) {
2921
+ while (current.type === import_utils24.AST_NODE_TYPES.ChainExpression || current.type === import_utils24.AST_NODE_TYPES.TSNonNullExpression) {
2321
2922
  current = current.expression;
2322
2923
  }
2323
2924
  return current;
@@ -2326,7 +2927,7 @@ function canThrow(stmt) {
2326
2927
  if (hasAwait(stmt)) {
2327
2928
  return true;
2328
2929
  }
2329
- if (stmt.type === import_utils23.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils23.AST_NODE_TYPES.CallExpression) {
2930
+ if (stmt.type === import_utils24.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils24.AST_NODE_TYPES.CallExpression) {
2330
2931
  return false;
2331
2932
  }
2332
2933
  return hasThrowingCallOrNew(stmt);
@@ -2337,9 +2938,9 @@ function handlerRethrows(handler) {
2337
2938
  }
2338
2939
  const body = handler.body.body;
2339
2940
  const last = body[body.length - 1];
2340
- return last !== void 0 && last.type === import_utils23.AST_NODE_TYPES.ThrowStatement;
2941
+ return last !== void 0 && last.type === import_utils24.AST_NODE_TYPES.ThrowStatement;
2341
2942
  }
2342
- var no_fat_try_blocks_default = import_utils23.ESLintUtils.RuleCreator(
2943
+ var no_fat_try_blocks_default = import_utils24.ESLintUtils.RuleCreator(
2343
2944
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
2344
2945
  )({
2345
2946
  name: "no-fat-try-blocks",
@@ -2380,8 +2981,8 @@ var no_fat_try_blocks_default = import_utils23.ESLintUtils.RuleCreator(
2380
2981
  });
2381
2982
 
2382
2983
  // src/rules/no-secret-in-log.ts
2383
- var import_utils24 = require("@typescript-eslint/utils");
2384
- var LOG_METHODS = /* @__PURE__ */ new Set([
2984
+ var import_utils25 = require("@typescript-eslint/utils");
2985
+ var LOG_METHODS2 = /* @__PURE__ */ new Set([
2385
2986
  "debug",
2386
2987
  "info",
2387
2988
  "warn",
@@ -2394,7 +2995,7 @@ var LOG_METHODS = /* @__PURE__ */ new Set([
2394
2995
  "fatal",
2395
2996
  "success"
2396
2997
  ]);
2397
- var LOGGER_NAMES = /* @__PURE__ */ new Set([
2998
+ var LOGGER_NAMES2 = /* @__PURE__ */ new Set([
2398
2999
  "logger",
2399
3000
  "log",
2400
3001
  "logging",
@@ -2535,12 +3136,12 @@ function isSecretKeyword(name) {
2535
3136
  function isLoggerExpr(expr) {
2536
3137
  switch (expr.type) {
2537
3138
  case "Identifier":
2538
- return LOGGER_NAMES.has(expr.name.toLowerCase());
3139
+ return LOGGER_NAMES2.has(expr.name.toLowerCase());
2539
3140
  case "MemberExpression": {
2540
3141
  const { property, object } = expr;
2541
3142
  if (!expr.computed && property.type === "Identifier") {
2542
3143
  const lowered = property.name.toLowerCase();
2543
- if (LOGGER_NAMES.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
3144
+ if (LOGGER_NAMES2.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
2544
3145
  return true;
2545
3146
  }
2546
3147
  }
@@ -2578,7 +3179,7 @@ function propertyKeyName2(prop) {
2578
3179
  }
2579
3180
  return null;
2580
3181
  }
2581
- var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
3182
+ var no_secret_in_log_default = import_utils25.ESLintUtils.RuleCreator(
2582
3183
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
2583
3184
  )({
2584
3185
  name: "no-secret-in-log",
@@ -2597,7 +3198,7 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
2597
3198
  return {
2598
3199
  CallExpression(node) {
2599
3200
  const callee = node.callee;
2600
- if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS.has(callee.property.name)) {
3201
+ if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS2.has(callee.property.name)) {
2601
3202
  return;
2602
3203
  }
2603
3204
  if (!isLoggerExpr(callee.object)) {
@@ -2614,6 +3215,16 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
2614
3215
  }
2615
3216
  continue;
2616
3217
  }
3218
+ if (arg.type === "MemberExpression") {
3219
+ if (!arg.computed && arg.property.type === "Identifier" && isSecretKeyword(arg.property.name)) {
3220
+ context.report({
3221
+ node: arg,
3222
+ messageId: "noSecretInLog",
3223
+ data: { name: arg.property.name }
3224
+ });
3225
+ }
3226
+ continue;
3227
+ }
2617
3228
  if (arg.type === "ObjectExpression") {
2618
3229
  for (const prop of arg.properties) {
2619
3230
  if (prop.type !== "Property") {
@@ -2636,7 +3247,7 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
2636
3247
  });
2637
3248
 
2638
3249
  // src/rules/prefer-string-literal-union.ts
2639
- var import_utils25 = require("@typescript-eslint/utils");
3250
+ var import_utils26 = require("@typescript-eslint/utils");
2640
3251
  var ts = __toESM(require("typescript"), 1);
2641
3252
  var CHOICE_TOKENS = /* @__PURE__ */ new Set([
2642
3253
  "status",
@@ -2679,19 +3290,19 @@ function isChoiceLikeName(name) {
2679
3290
  return CHOICE_TOKENS.has(lastWord(name));
2680
3291
  }
2681
3292
  function keyName(key) {
2682
- if (key.type === import_utils25.AST_NODE_TYPES.Identifier) {
3293
+ if (key.type === import_utils26.AST_NODE_TYPES.Identifier) {
2683
3294
  return key.name;
2684
3295
  }
2685
- if (key.type === import_utils25.AST_NODE_TYPES.Literal && typeof key.value === "string") {
3296
+ if (key.type === import_utils26.AST_NODE_TYPES.Literal && typeof key.value === "string") {
2686
3297
  return key.value;
2687
3298
  }
2688
3299
  return null;
2689
3300
  }
2690
3301
  function isStringLiteralMember(t) {
2691
- return t.type === import_utils25.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils25.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
3302
+ return t.type === import_utils26.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils26.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
2692
3303
  }
2693
3304
  function isStringLiteralUnion(node) {
2694
- if (node?.type !== import_utils25.AST_NODE_TYPES.TSUnionType) {
3305
+ if (node?.type !== import_utils26.AST_NODE_TYPES.TSUnionType) {
2695
3306
  return false;
2696
3307
  }
2697
3308
  return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
@@ -2720,12 +3331,12 @@ function bindingSourceExpression(decl) {
2720
3331
  return ts.isForOfStatement(node) ? node.expression : node.initializer;
2721
3332
  }
2722
3333
  function refKey(node) {
2723
- if (node.type === import_utils25.AST_NODE_TYPES.Identifier) {
3334
+ if (node.type === import_utils26.AST_NODE_TYPES.Identifier) {
2724
3335
  return node.name;
2725
3336
  }
2726
- if (node.type === import_utils25.AST_NODE_TYPES.MemberExpression && !node.computed) {
3337
+ if (node.type === import_utils26.AST_NODE_TYPES.MemberExpression && !node.computed) {
2727
3338
  const inner = refKey(node.object);
2728
- if (inner === null || node.property.type !== import_utils25.AST_NODE_TYPES.Identifier) {
3339
+ if (inner === null || node.property.type !== import_utils26.AST_NODE_TYPES.Identifier) {
2729
3340
  return null;
2730
3341
  }
2731
3342
  return `${inner}.${node.property.name}`;
@@ -2733,12 +3344,12 @@ function refKey(node) {
2733
3344
  return null;
2734
3345
  }
2735
3346
  function strLiteral(node) {
2736
- if (node.type === import_utils25.AST_NODE_TYPES.Literal && typeof node.value === "string") {
3347
+ if (node.type === import_utils26.AST_NODE_TYPES.Literal && typeof node.value === "string") {
2737
3348
  return node.value;
2738
3349
  }
2739
3350
  return null;
2740
3351
  }
2741
- var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator(
3352
+ var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator(
2742
3353
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2743
3354
  )({
2744
3355
  name: "prefer-string-literal-union",
@@ -2762,7 +3373,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2762
3373
  }
2763
3374
  let services;
2764
3375
  try {
2765
- services = import_utils25.ESLintUtils.getParserServices(context);
3376
+ services = import_utils26.ESLintUtils.getParserServices(context);
2766
3377
  } catch {
2767
3378
  services = null;
2768
3379
  }
@@ -2846,7 +3457,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2846
3457
  containersWithUnion.add(container);
2847
3458
  return;
2848
3459
  }
2849
- if (typeNode?.type !== import_utils25.AST_NODE_TYPES.TSStringKeyword) {
3460
+ if (typeNode?.type !== import_utils26.AST_NODE_TYPES.TSStringKeyword) {
2850
3461
  return;
2851
3462
  }
2852
3463
  const name = keyName(key);
@@ -2934,10 +3545,10 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2934
3545
  }
2935
3546
  };
2936
3547
  function refKeyText(node) {
2937
- if (node.type === import_utils25.AST_NODE_TYPES.BinaryExpression) {
3548
+ if (node.type === import_utils26.AST_NODE_TYPES.BinaryExpression) {
2938
3549
  return refKey(node.left) ?? refKey(node.right) ?? "value";
2939
3550
  }
2940
- if (node.type === import_utils25.AST_NODE_TYPES.SwitchStatement) {
3551
+ if (node.type === import_utils26.AST_NODE_TYPES.SwitchStatement) {
2941
3552
  return refKey(node.discriminant) ?? "value";
2942
3553
  }
2943
3554
  return "value";
@@ -2946,7 +3557,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2946
3557
  });
2947
3558
 
2948
3559
  // src/rules/single-public-export.ts
2949
- var import_utils26 = require("@typescript-eslint/utils");
3560
+ var import_utils27 = require("@typescript-eslint/utils");
2950
3561
  var JUNK_DRAWER_STEMS = /* @__PURE__ */ new Set([
2951
3562
  "util",
2952
3563
  "utils",
@@ -2973,20 +3584,20 @@ var TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/i;
2973
3584
  var SCRIPT_EXT_RE = /\.[cm]?[jt]sx?$/i;
2974
3585
  var basename = (filename) => filename.split(/[/\\]/).pop() ?? filename;
2975
3586
  var stemOf = (base) => base.replace(SCRIPT_EXT_RE, "");
2976
- var kebabCase = (name) => {
3587
+ var kebabCase2 = (name) => {
2977
3588
  let normalized = name;
2978
3589
  for (const [pattern, replacement] of ACRONYM_OVERRIDES) {
2979
3590
  normalized = normalized.replace(pattern, replacement);
2980
3591
  }
2981
3592
  return normalized.replace(CAMEL_BOUNDARY_RE, "-").toLowerCase();
2982
3593
  };
2983
- var isFunctionExpression2 = (node) => node !== null && (node.type === import_utils26.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils26.AST_NODE_TYPES.FunctionExpression);
3594
+ var isFunctionExpression = (node) => node !== null && (node.type === import_utils27.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils27.AST_NODE_TYPES.FunctionExpression);
2984
3595
  var functionConstName = (decl) => {
2985
3596
  if (decl.declarations.length !== 1) return null;
2986
3597
  const [declarator] = decl.declarations;
2987
3598
  if (declarator === void 0) return null;
2988
- if (declarator.id.type !== import_utils26.AST_NODE_TYPES.Identifier) return null;
2989
- if (!isFunctionExpression2(declarator.init)) return null;
3599
+ if (declarator.id.type !== import_utils27.AST_NODE_TYPES.Identifier) return null;
3600
+ if (!isFunctionExpression(declarator.init)) return null;
2990
3601
  return declarator.id.name;
2991
3602
  };
2992
3603
  var summarizeExports = (body) => {
@@ -2999,20 +3610,20 @@ var summarizeExports = (body) => {
2999
3610
  };
3000
3611
  for (const statement of body) {
3001
3612
  switch (statement.type) {
3002
- case import_utils26.AST_NODE_TYPES.ExportAllDeclaration:
3613
+ case import_utils27.AST_NODE_TYPES.ExportAllDeclaration:
3003
3614
  hasReExport = true;
3004
3615
  break;
3005
- case import_utils26.AST_NODE_TYPES.ExportDefaultDeclaration: {
3616
+ case import_utils27.AST_NODE_TYPES.ExportDefaultDeclaration: {
3006
3617
  names += 1;
3007
3618
  const decl = statement.declaration;
3008
- if (decl.type === import_utils26.AST_NODE_TYPES.FunctionDeclaration && decl.id !== null) {
3619
+ if (decl.type === import_utils27.AST_NODE_TYPES.FunctionDeclaration && decl.id !== null) {
3009
3620
  candidate = { name: decl.id.name, node: statement };
3010
- } else if (decl.type === import_utils26.AST_NODE_TYPES.ClassDeclaration && decl.id !== null) {
3621
+ } else if (decl.type === import_utils27.AST_NODE_TYPES.ClassDeclaration && decl.id !== null) {
3011
3622
  candidate = { name: decl.id.name, node: statement };
3012
3623
  }
3013
3624
  break;
3014
3625
  }
3015
- case import_utils26.AST_NODE_TYPES.ExportNamedDeclaration: {
3626
+ case import_utils27.AST_NODE_TYPES.ExportNamedDeclaration: {
3016
3627
  if (statement.source !== null) {
3017
3628
  hasReExport = true;
3018
3629
  break;
@@ -3023,15 +3634,15 @@ var summarizeExports = (body) => {
3023
3634
  break;
3024
3635
  }
3025
3636
  switch (decl.type) {
3026
- case import_utils26.AST_NODE_TYPES.FunctionDeclaration:
3637
+ case import_utils27.AST_NODE_TYPES.FunctionDeclaration:
3027
3638
  if (decl.id !== null) addCandidate(decl.id.name, statement);
3028
3639
  else names += 1;
3029
3640
  break;
3030
- case import_utils26.AST_NODE_TYPES.ClassDeclaration:
3641
+ case import_utils27.AST_NODE_TYPES.ClassDeclaration:
3031
3642
  if (decl.id !== null) addCandidate(decl.id.name, statement);
3032
3643
  else names += 1;
3033
3644
  break;
3034
- case import_utils26.AST_NODE_TYPES.VariableDeclaration: {
3645
+ case import_utils27.AST_NODE_TYPES.VariableDeclaration: {
3035
3646
  const fnName = functionConstName(decl);
3036
3647
  if (fnName !== null && decl.declarations.length === 1) {
3037
3648
  addCandidate(fnName, statement);
@@ -3051,7 +3662,7 @@ var summarizeExports = (body) => {
3051
3662
  }
3052
3663
  return { names, hasReExport, candidate };
3053
3664
  };
3054
- var single_public_export_default = import_utils26.ESLintUtils.RuleCreator(
3665
+ var single_public_export_default = import_utils27.ESLintUtils.RuleCreator(
3055
3666
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
3056
3667
  )({
3057
3668
  name: "single-public-export",
@@ -3078,7 +3689,7 @@ var single_public_export_default = import_utils26.ESLintUtils.RuleCreator(
3078
3689
  if (hasReExport) return;
3079
3690
  if (names !== 1 || candidate === null) return;
3080
3691
  if (CONVENTIONAL_BUCKET_EXPORTS.has(candidate.name)) return;
3081
- const expected = kebabCase(candidate.name);
3692
+ const expected = kebabCase2(candidate.name);
3082
3693
  if (stem === expected) return;
3083
3694
  context.report({
3084
3695
  node: candidate.node,
@@ -3121,7 +3732,7 @@ var rules = {
3121
3732
  var plugin = {
3122
3733
  meta: {
3123
3734
  name: "@sarj/eslint-plugin",
3124
- version: "2.3.4"
3735
+ version: "2.4.1"
3125
3736
  },
3126
3737
  rules,
3127
3738
  configs: {