@sarj/eslint-plugin 2.3.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -37,41 +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
40
  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) => {
41
+ var classifyStatement = (statement) => {
56
42
  switch (statement.type) {
57
43
  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:
44
+ return "import";
69
45
  case import_utils.AST_NODE_TYPES.ExportAllDeclaration:
70
- return SECTION.exports;
46
+ return "reexport";
47
+ case import_utils.AST_NODE_TYPES.ExportNamedDeclaration:
48
+ return statement.declaration === null ? "reexport" : "body";
71
49
  default:
72
- return SECTION.functions;
50
+ return "body";
73
51
  }
74
52
  };
53
+ 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
54
  var isUseServerDirective = (statement) => {
76
55
  if (statement === void 0) return false;
77
56
  if (statement.type !== import_utils.AST_NODE_TYPES.ExpressionStatement) return false;
@@ -86,47 +65,43 @@ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
86
65
  meta: {
87
66
  type: "suggestion",
88
67
  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."
68
+ 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. Server-action files (under `/actions/`, named `*.action.ts`, or `actions.ts`) must also begin with a `use server` directive."
90
69
  },
91
70
  schema: [],
92
71
  messages: {
93
- incorrectOrder: "File structure violation: {{current}} should come before {{expected}}",
72
+ importsFirst: "File structure violation: import statements must come before other declarations",
94
73
  useServerDirective: "Server action files must start with 'use server' directive"
95
74
  }
96
75
  },
97
76
  defaultOptions: [],
98
77
  create(context) {
99
- const filename = context.filename;
100
- const isServerAction = SERVER_ACTION_FILE_RE.test(filename);
78
+ const isServerAction = SERVER_ACTION_FILE_RE.test(context.filename);
101
79
  return {
102
80
  Program(node) {
103
81
  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
- }
82
+ if (isServerAction && !isUseServerDirective(body[0])) {
83
+ context.report({
84
+ node,
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,61 @@ 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
+ function negatedInstanceofErrorSubject(test) {
667
+ if (test.type === "UnaryExpression" && test.operator === "!") {
668
+ return instanceofErrorSubject(test.argument);
669
+ }
670
+ return null;
671
+ }
672
+ function nodeWithin(node, container) {
673
+ return container !== null && node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
674
+ }
675
+ function isGuardedByInstanceofError(node, argExpr, sourceCode) {
676
+ const argText = sourceCode.getText(argExpr);
677
+ const sameSubject = (subject) => sourceCode.getText(subject) === argText;
678
+ let current = node.parent;
679
+ while (current) {
680
+ if (current.type === "ConditionalExpression") {
681
+ const subject = instanceofErrorSubject(current.test);
682
+ if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
683
+ return true;
684
+ }
685
+ const negated = negatedInstanceofErrorSubject(current.test);
686
+ if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
687
+ return true;
688
+ }
689
+ } else if (current.type === "IfStatement") {
690
+ const subject = instanceofErrorSubject(current.test);
691
+ if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
692
+ return true;
693
+ }
694
+ const negated = negatedInstanceofErrorSubject(current.test);
695
+ if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
696
+ return true;
697
+ }
698
+ }
699
+ current = current.parent;
700
+ }
701
+ return false;
702
+ }
570
703
  function isJsonStringify(callee) {
571
704
  return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
572
705
  }
@@ -592,72 +725,124 @@ var no_json_stringify_error_default = import_utils6.ESLintUtils.RuleCreator(
592
725
  return;
593
726
  }
594
727
  const firstArg = node.arguments[0];
595
- if (!firstArg || firstArg.type !== "Identifier") {
728
+ if (!firstArg) {
596
729
  return;
597
730
  }
598
- const name = firstArg.name;
599
731
  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
- });
732
+ let suggestsError;
733
+ if (firstArg.type === "Identifier") {
734
+ suggestsError = ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);
735
+ } else if (firstArg.type === "MemberExpression") {
736
+ suggestsError = memberSuggestsError(firstArg, scope);
737
+ } else {
738
+ return;
739
+ }
740
+ if (!suggestsError) {
741
+ return;
605
742
  }
743
+ if (isGuardedByInstanceofError(node, firstArg, context.sourceCode)) {
744
+ return;
745
+ }
746
+ context.report({
747
+ node,
748
+ messageId: "noJsonStringifyError"
749
+ });
606
750
  }
607
751
  };
608
752
  }
609
753
  });
610
754
 
611
755
  // src/rules/no-log-only-catch.ts
756
+ var import_utils8 = require("@typescript-eslint/utils");
757
+
758
+ // src/rules/_logging.ts
612
759
  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",
760
+ var LOG_METHODS = /* @__PURE__ */ new Set([
761
+ "debug",
622
762
  "info",
623
- "debug"
763
+ "warn",
764
+ "warning",
765
+ "error",
766
+ "exception",
767
+ "critical",
768
+ "trace",
769
+ "log",
770
+ "fatal",
771
+ "success"
624
772
  ]);
625
- function isConsoleCallStatement(statement) {
626
- if (statement.type !== "ExpressionStatement") {
627
- return false;
773
+ var LOGGER_NAMES = /* @__PURE__ */ new Set([
774
+ "logger",
775
+ "log",
776
+ "logging",
777
+ "loguru",
778
+ "console",
779
+ "_logger",
780
+ "_log"
781
+ ]);
782
+ var REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;
783
+ function isLoggerReceiver(expr) {
784
+ switch (expr.type) {
785
+ case "Identifier":
786
+ return LOGGER_NAMES.has(expr.name.toLowerCase());
787
+ case "MemberExpression": {
788
+ const { property, object } = expr;
789
+ if (!expr.computed && property.type === "Identifier" && LOGGER_NAMES.has(property.name.toLowerCase())) {
790
+ return true;
791
+ }
792
+ return isLoggerReceiver(object);
793
+ }
794
+ default:
795
+ return false;
628
796
  }
629
- const expr = statement.expression;
797
+ }
798
+ function isLoggingCall(expr) {
630
799
  if (expr.type !== "CallExpression") {
631
800
  return false;
632
801
  }
633
802
  const callee = expr.callee;
634
- if (callee.type !== "MemberExpression") {
803
+ if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier") {
635
804
  return false;
636
805
  }
637
- const { object, property } = callee;
638
- if (object.type !== "Identifier" || object.name !== "console") {
806
+ if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {
639
807
  return false;
640
808
  }
641
- if (callee.computed) {
642
- return false;
809
+ return isLoggerReceiver(callee.object);
810
+ }
811
+ function calleeName(callee) {
812
+ if (callee.type === "Identifier") {
813
+ return callee.name;
643
814
  }
644
- if (property.type !== "Identifier") {
815
+ if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
816
+ return callee.property.name;
817
+ }
818
+ return null;
819
+ }
820
+
821
+ // src/rules/no-log-only-catch.ts
822
+ var DEFAULT_IGNORE_PATTERNS2 = [
823
+ /\.test\./,
824
+ /\.spec\./,
825
+ /[\\/]__tests__[\\/]/
826
+ ];
827
+ function isLoggingCallStatement(statement) {
828
+ if (statement.type !== "ExpressionStatement") {
645
829
  return false;
646
830
  }
647
- return CONSOLE_METHODS.has(property.name);
831
+ return isLoggingCall(statement.expression);
648
832
  }
649
- var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
833
+ var no_log_only_catch_default = import_utils8.ESLintUtils.RuleCreator(
650
834
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
651
835
  )({
652
836
  name: "no-log-only-catch",
653
837
  meta: {
654
838
  type: "problem",
655
839
  docs: {
656
- description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
840
+ description: "Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead."
657
841
  },
658
842
  schema: [],
659
843
  messages: {
660
- noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
844
+ noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real.",
845
+ emptyCatch: "Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore."
661
846
  }
662
847
  },
663
848
  defaultOptions: [],
@@ -673,13 +858,16 @@ var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
673
858
  CatchClause(node) {
674
859
  const statements = node.body.body;
675
860
  if (statements.length === 0) {
676
- context.report({ node, messageId: "noLogOnlyCatch" });
861
+ if (context.sourceCode.getCommentsInside(node.body).length > 0) {
862
+ return;
863
+ }
864
+ context.report({ node, messageId: "emptyCatch" });
677
865
  return;
678
866
  }
679
- const everyStatementIsConsoleLog = statements.every(
680
- (statement) => isConsoleCallStatement(statement)
867
+ const everyStatementIsLogging = statements.every(
868
+ (statement) => isLoggingCallStatement(statement)
681
869
  );
682
- if (everyStatementIsConsoleLog) {
870
+ if (everyStatementIsLogging) {
683
871
  context.report({ node, messageId: "noLogOnlyCatch" });
684
872
  }
685
873
  }
@@ -688,8 +876,14 @@ var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
688
876
  });
689
877
 
690
878
  // src/rules/no-raw-env.ts
691
- var import_utils8 = require("@typescript-eslint/utils");
692
- var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
879
+ var import_utils9 = require("@typescript-eslint/utils");
880
+ function isProcessEnv(node) {
881
+ return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
882
+ }
883
+ function isImportMetaEnv(node) {
884
+ 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";
885
+ }
886
+ var no_raw_env_default = import_utils9.ESLintUtils.RuleCreator(
693
887
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
694
888
  )({
695
889
  name: "no-raw-env",
@@ -707,10 +901,7 @@ var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
707
901
  create(context) {
708
902
  return {
709
903
  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") {
904
+ if (isProcessEnv(node) || isImportMetaEnv(node)) {
714
905
  context.report({
715
906
  node,
716
907
  messageId: "noRawEnv"
@@ -722,39 +913,72 @@ var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
722
913
  });
723
914
 
724
915
  // src/rules/no-sentinel-return-on-catch.ts
725
- var import_utils9 = require("@typescript-eslint/utils");
916
+ var import_utils10 = require("@typescript-eslint/utils");
917
+ function sentinelKind(arg) {
918
+ if (arg === null) {
919
+ return null;
920
+ }
921
+ if (arg.type === import_utils10.AST_NODE_TYPES.Literal) {
922
+ if (arg.value === null) {
923
+ return "nullish";
924
+ }
925
+ if (typeof arg.value === "boolean") {
926
+ return "boolean";
927
+ }
928
+ if (typeof arg.value === "string") {
929
+ return "string";
930
+ }
931
+ return null;
932
+ }
933
+ if (arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
934
+ return "nullish";
935
+ }
936
+ if (arg.type === import_utils10.AST_NODE_TYPES.ArrayExpression) {
937
+ return "array";
938
+ }
939
+ if (arg.type === import_utils10.AST_NODE_TYPES.ObjectExpression) {
940
+ return "object";
941
+ }
942
+ return null;
943
+ }
726
944
  function isSentinelArgument(arg) {
727
945
  if (arg === null) {
728
946
  return false;
729
947
  }
730
- if (arg.type === import_utils9.AST_NODE_TYPES.Literal && arg.value === null) {
948
+ if (arg.type === import_utils10.AST_NODE_TYPES.Literal && arg.value === null) {
731
949
  return true;
732
950
  }
733
- if (arg.type === import_utils9.AST_NODE_TYPES.Literal && arg.value === false) {
951
+ if (arg.type === import_utils10.AST_NODE_TYPES.Literal && arg.value === false) {
734
952
  return true;
735
953
  }
736
- if (arg.type === import_utils9.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
954
+ if (arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
737
955
  return true;
738
956
  }
739
- if (arg.type === import_utils9.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
957
+ if (arg.type === import_utils10.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
740
958
  return true;
741
959
  }
742
- if (arg.type === import_utils9.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
960
+ if (arg.type === import_utils10.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
743
961
  return true;
744
962
  }
745
963
  return false;
746
964
  }
747
- function containsThrow(node) {
965
+ function isFunctionNode(node) {
966
+ 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;
967
+ }
968
+ function isNode(value) {
969
+ return typeof value === "object" && value !== null && typeof value.type === "string";
970
+ }
971
+ function walkWithinScope(node, visit) {
748
972
  let found = false;
749
- const visit = (current) => {
973
+ const recurse = (current) => {
750
974
  if (found) {
751
975
  return;
752
976
  }
753
- if (current.type === import_utils9.AST_NODE_TYPES.ThrowStatement) {
977
+ if (visit(current)) {
754
978
  found = true;
755
979
  return;
756
980
  }
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) {
981
+ if (isFunctionNode(current)) {
758
982
  return;
759
983
  }
760
984
  for (const key of Object.keys(current)) {
@@ -765,32 +989,111 @@ function containsThrow(node) {
765
989
  if (Array.isArray(value)) {
766
990
  for (const child of value) {
767
991
  if (isNode(child)) {
768
- visit(child);
992
+ recurse(child);
769
993
  }
770
994
  }
771
995
  } else if (isNode(value)) {
772
- visit(value);
996
+ recurse(value);
773
997
  }
774
998
  }
775
999
  };
776
- visit(node);
1000
+ recurse(node);
777
1001
  return found;
778
1002
  }
779
- function isNode(value) {
780
- return typeof value === "object" && value !== null && typeof value.type === "string";
1003
+ function containsThrow(node) {
1004
+ return walkWithinScope(
1005
+ node,
1006
+ (current) => current.type === import_utils10.AST_NODE_TYPES.ThrowStatement
1007
+ );
1008
+ }
1009
+ function argsIncludeBinding(args, caughtName) {
1010
+ if (caughtName === null) {
1011
+ return false;
1012
+ }
1013
+ return args.some(
1014
+ (arg) => arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === caughtName
1015
+ );
1016
+ }
1017
+ function logsOrReportsError(catchBody, caughtName) {
1018
+ return walkWithinScope(catchBody, (current) => {
1019
+ if (current.type !== import_utils10.AST_NODE_TYPES.CallExpression) {
1020
+ return false;
1021
+ }
1022
+ if (isLoggingCall(current)) {
1023
+ return true;
1024
+ }
1025
+ const name = calleeName(current.callee);
1026
+ return name !== null && REPORT_NAME_RE.test(name) && argsIncludeBinding(current.arguments, caughtName);
1027
+ });
1028
+ }
1029
+ function tryBlockOf(catchNode) {
1030
+ return catchNode.parent.block;
1031
+ }
1032
+ function isSafeParseExpression(arg) {
1033
+ if (arg === null) {
1034
+ return false;
1035
+ }
1036
+ 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") {
1037
+ return true;
1038
+ }
1039
+ if (arg.type === import_utils10.AST_NODE_TYPES.NewExpression && arg.callee.type === import_utils10.AST_NODE_TYPES.Identifier) {
1040
+ return arg.callee.name === "RegExp" || arg.callee.name === "URL";
1041
+ }
1042
+ return false;
1043
+ }
1044
+ function tryReturnsSafeParse(catchNode) {
1045
+ return walkWithinScope(
1046
+ tryBlockOf(catchNode),
1047
+ (current) => current.type === import_utils10.AST_NODE_TYPES.ReturnStatement && isSafeParseExpression(current.argument)
1048
+ );
1049
+ }
1050
+ function enclosingFunctionBody(node) {
1051
+ let current = node.parent;
1052
+ while (current !== void 0 && current !== null) {
1053
+ if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === import_utils10.AST_NODE_TYPES.BlockStatement) {
1054
+ return current.body;
1055
+ }
1056
+ current = current.parent;
1057
+ }
1058
+ return null;
1059
+ }
1060
+ function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
1061
+ const functionBody = enclosingFunctionBody(catchNode);
1062
+ if (functionBody === null) {
1063
+ return false;
1064
+ }
1065
+ return walkWithinScope(functionBody, (current) => {
1066
+ if (current.type !== import_utils10.AST_NODE_TYPES.ReturnStatement) {
1067
+ return false;
1068
+ }
1069
+ if (isWithin(current, catchNode.body)) {
1070
+ return false;
1071
+ }
1072
+ return sentinelKind(current.argument) === kind;
1073
+ });
1074
+ }
1075
+ function isWithin(node, ancestor) {
1076
+ let current = node;
1077
+ while (current !== void 0 && current !== null) {
1078
+ if (current === ancestor) {
1079
+ return true;
1080
+ }
1081
+ current = current.parent;
1082
+ }
1083
+ return false;
781
1084
  }
782
- var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
1085
+ var no_sentinel_return_on_catch_default = import_utils10.ESLintUtils.RuleCreator(
783
1086
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
784
1087
  )({
785
1088
  name: "no-sentinel-return-on-catch",
786
1089
  meta: {
787
1090
  type: "problem",
788
1091
  docs: {
789
- description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
1092
+ 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
1093
  },
791
1094
  schema: [],
792
1095
  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."
1096
+ 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
1097
  }
795
1098
  },
796
1099
  defaultOptions: [],
@@ -802,7 +1105,7 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
802
1105
  return;
803
1106
  }
804
1107
  const last = body[body.length - 1];
805
- if (last === void 0 || last.type !== import_utils9.AST_NODE_TYPES.ReturnStatement) {
1108
+ if (last === void 0 || last.type !== import_utils10.AST_NODE_TYPES.ReturnStatement) {
806
1109
  return;
807
1110
  }
808
1111
  if (!isSentinelArgument(last.argument)) {
@@ -811,6 +1114,17 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
811
1114
  if (containsThrow(node.body)) {
812
1115
  return;
813
1116
  }
1117
+ const caughtName = node.param?.type === import_utils10.AST_NODE_TYPES.Identifier ? node.param.name : null;
1118
+ if (logsOrReportsError(node.body, caughtName)) {
1119
+ return;
1120
+ }
1121
+ if (tryReturnsSafeParse(node)) {
1122
+ return;
1123
+ }
1124
+ const kind = sentinelKind(last.argument);
1125
+ if (kind !== null && functionReturnsSameSentinelKindElsewhere(node, kind)) {
1126
+ return;
1127
+ }
814
1128
  context.report({
815
1129
  node: last,
816
1130
  messageId: "noSentinelReturn"
@@ -821,14 +1135,92 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
821
1135
  });
822
1136
 
823
1137
  // src/rules/no-sequential-await.ts
824
- var import_utils10 = require("@typescript-eslint/utils");
1138
+ var import_utils11 = require("@typescript-eslint/utils");
1139
+ var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
1140
+ var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain/i;
825
1141
  function isFunctionLike(node) {
826
1142
  return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
827
1143
  }
828
1144
  function isLoop(node) {
829
1145
  return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
830
1146
  }
831
- var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
1147
+ function isNode2(value) {
1148
+ return typeof value === "object" && value !== null && typeof value.type === "string";
1149
+ }
1150
+ function visitScope(root, visit) {
1151
+ visit(root);
1152
+ for (const key of Object.keys(root)) {
1153
+ if (key === "parent") {
1154
+ continue;
1155
+ }
1156
+ const value = root[key];
1157
+ const children = Array.isArray(value) ? value : [value];
1158
+ for (const child of children) {
1159
+ if (isNode2(child) && !isFunctionLike(child) && !isLoop(child)) {
1160
+ visitScope(child, visit);
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1165
+ function collectAwaits(root) {
1166
+ const awaits = [];
1167
+ visitScope(root, (node) => {
1168
+ if (node.type === "AwaitExpression") {
1169
+ awaits.push(node);
1170
+ }
1171
+ });
1172
+ return awaits;
1173
+ }
1174
+ function hasEarlyExit(root) {
1175
+ let found = false;
1176
+ visitScope(root, (node) => {
1177
+ if (node.type === "ReturnStatement" || node.type === "BreakStatement" || node.type === "ContinueStatement") {
1178
+ found = true;
1179
+ }
1180
+ });
1181
+ return found;
1182
+ }
1183
+ function isTimerYield(node) {
1184
+ const arg = node.argument;
1185
+ return arg.type === "NewExpression" && arg.callee.type === "Identifier" && arg.callee.name === "Promise";
1186
+ }
1187
+ function referencesName(root, name) {
1188
+ let found = false;
1189
+ visitScope(root, (node) => {
1190
+ if (node.type === "Identifier" && node.name === name) {
1191
+ found = true;
1192
+ }
1193
+ });
1194
+ return found;
1195
+ }
1196
+ function isThreadedAccumulator(node) {
1197
+ const parent = node.parent;
1198
+ let target = null;
1199
+ if (parent.type === "AssignmentExpression" && parent.operator === "=" && parent.right === node && parent.left.type === "Identifier") {
1200
+ target = parent.left.name;
1201
+ } else if (parent.type === "VariableDeclarator" && parent.init === node && parent.id.type === "Identifier") {
1202
+ target = parent.id.name;
1203
+ }
1204
+ if (target === null) {
1205
+ return false;
1206
+ }
1207
+ return referencesName(node.argument, target);
1208
+ }
1209
+ function shouldReport(awaits, earlyExit, iterableText) {
1210
+ if (awaits.length === 0) {
1211
+ return false;
1212
+ }
1213
+ if (earlyExit) {
1214
+ return false;
1215
+ }
1216
+ if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
1217
+ return false;
1218
+ }
1219
+ return awaits.some(
1220
+ (node) => !isTimerYield(node) && !isThreadedAccumulator(node)
1221
+ );
1222
+ }
1223
+ var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
832
1224
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
833
1225
  )({
834
1226
  name: "no-sequential-await",
@@ -844,54 +1236,36 @@ var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
844
1236
  },
845
1237
  defaultOptions: [],
846
1238
  create(context) {
847
- function findAwaitInScope(node) {
848
- if (node.type === "AwaitExpression") {
849
- return node;
1239
+ function loopParts(node) {
1240
+ if (node.type === "ForStatement") {
1241
+ return [node.body, node.init, node.test, node.update];
850
1242
  }
851
- if (isFunctionLike(node)) {
852
- return null;
1243
+ if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
1244
+ return [node.body, node.right];
853
1245
  }
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
- }
1246
+ return [node.body, node.test];
1247
+ }
1248
+ function iterableTextOf(node) {
1249
+ if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
1250
+ return context.sourceCode.getText(node.right);
874
1251
  }
875
1252
  return null;
876
1253
  }
877
- function isNode4(value) {
878
- return typeof value === "object" && value !== null && typeof value.type === "string";
879
- }
880
1254
  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;
1255
+ const awaits = [];
1256
+ let earlyExit = false;
1257
+ for (const part of loopParts(node)) {
1258
+ if (part === null || isLoop(part)) {
1259
+ continue;
1260
+ }
1261
+ awaits.push(...collectAwaits(part));
1262
+ if (!earlyExit && hasEarlyExit(part)) {
1263
+ earlyExit = true;
893
1264
  }
894
1265
  }
1266
+ if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
1267
+ context.report({ node, messageId: "noSequentialAwait" });
1268
+ }
895
1269
  }
896
1270
  return {
897
1271
  ForStatement: checkLoop,
@@ -903,13 +1277,35 @@ var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
903
1277
  return;
904
1278
  }
905
1279
  checkLoop(node);
1280
+ },
1281
+ CallExpression(node) {
1282
+ const callee = node.callee;
1283
+ if (callee.type !== "MemberExpression" || callee.computed) {
1284
+ return;
1285
+ }
1286
+ if (callee.property.type !== "Identifier" || !ARRAY_ITERATION_METHODS.has(callee.property.name)) {
1287
+ return;
1288
+ }
1289
+ const callback = node.arguments[0];
1290
+ if (callback === void 0 || !isFunctionLike(callback) || !("async" in callback && callback.async)) {
1291
+ return;
1292
+ }
1293
+ if (callee.property.name !== "forEach" && node.parent.type !== "ExpressionStatement") {
1294
+ return;
1295
+ }
1296
+ const awaits = collectAwaits(callback.body);
1297
+ const earlyExit = hasEarlyExit(callback.body);
1298
+ const iterableText = context.sourceCode.getText(callee.object);
1299
+ if (shouldReport(awaits, earlyExit, iterableText)) {
1300
+ context.report({ node, messageId: "noSequentialAwait" });
1301
+ }
906
1302
  }
907
1303
  };
908
1304
  }
909
1305
  });
910
1306
 
911
1307
  // src/rules/no-string-concat-in-loop.ts
912
- var import_utils11 = require("@typescript-eslint/utils");
1308
+ var import_utils12 = require("@typescript-eslint/utils");
913
1309
  var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
914
1310
  "ForStatement",
915
1311
  "ForOfStatement",
@@ -954,6 +1350,21 @@ function isStringInitializedVariable(variable) {
954
1350
  }
955
1351
  return isStringLiteralInit(declarator.init);
956
1352
  }
1353
+ function isConcatOperand(node, target) {
1354
+ if (node.type === "Identifier") {
1355
+ return node.name === target;
1356
+ }
1357
+ if (node.type === "BinaryExpression" && node.operator === "+") {
1358
+ return isConcatOperand(node.left, target) || isConcatOperand(node.right, target);
1359
+ }
1360
+ return false;
1361
+ }
1362
+ function isConcatOntoTarget(rhs, target) {
1363
+ if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
1364
+ return false;
1365
+ }
1366
+ return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
1367
+ }
957
1368
  function isInsideLoopBody(node) {
958
1369
  let child = node;
959
1370
  let parent = node.parent;
@@ -969,7 +1380,7 @@ function isInsideLoopBody(node) {
969
1380
  }
970
1381
  return false;
971
1382
  }
972
- var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
1383
+ var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
973
1384
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
974
1385
  )({
975
1386
  name: "no-string-concat-in-loop",
@@ -987,10 +1398,11 @@ var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
987
1398
  create(context) {
988
1399
  return {
989
1400
  AssignmentExpression(node) {
990
- if (node.operator !== "+=") {
1401
+ if (node.left.type !== "Identifier") {
991
1402
  return;
992
1403
  }
993
- if (node.left.type !== "Identifier") {
1404
+ const isAccumulation = node.operator === "+=" || node.operator === "=" && isConcatOntoTarget(node.right, node.left.name);
1405
+ if (!isAccumulation) {
994
1406
  return;
995
1407
  }
996
1408
  if (!isInsideLoopBody(node)) {
@@ -1014,7 +1426,7 @@ var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
1014
1426
  });
1015
1427
 
1016
1428
  // src/rules/no-unnecessary-use-client.ts
1017
- var import_utils12 = require("@typescript-eslint/utils");
1429
+ var import_utils13 = require("@typescript-eslint/utils");
1018
1430
  var HOOK_REGEX = /^use([A-Z]|$)/;
1019
1431
  var EVENT_PROP_REGEX = /^on[A-Z]/;
1020
1432
  var ERROR_FILE_REGEX = /\b(?:global-)?error\.[jt]sx?$/;
@@ -1037,16 +1449,16 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
1037
1449
  ]);
1038
1450
  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
1451
  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";
1452
+ return node.type === import_utils13.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils13.AST_NODE_TYPES.Literal && node.expression.value === "use client";
1041
1453
  };
1042
1454
  var isGlobalReference = (node, context) => {
1043
1455
  if (!BROWSER_GLOBALS.has(node.name)) return false;
1044
1456
  const parent = node.parent;
1045
1457
  if (parent !== void 0) {
1046
- if (parent.type === import_utils12.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
1458
+ if (parent.type === import_utils13.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
1047
1459
  return false;
1048
1460
  }
1049
- if (parent.type === import_utils12.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
1461
+ if (parent.type === import_utils13.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
1050
1462
  return false;
1051
1463
  }
1052
1464
  if (parent.type.startsWith("TS")) {
@@ -1063,7 +1475,7 @@ var isGlobalReference = (node, context) => {
1063
1475
  }
1064
1476
  return true;
1065
1477
  };
1066
- var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1478
+ var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
1067
1479
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1068
1480
  )({
1069
1481
  name: "no-unnecessary-use-client",
@@ -1086,13 +1498,13 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1086
1498
  let directiveNode = null;
1087
1499
  let hasClientIndicator = false;
1088
1500
  const markIfHookOrContext = (callee) => {
1089
- if (callee.type === import_utils12.AST_NODE_TYPES.Identifier) {
1501
+ if (callee.type === import_utils13.AST_NODE_TYPES.Identifier) {
1090
1502
  if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
1091
1503
  hasClientIndicator = true;
1092
1504
  }
1093
1505
  return;
1094
1506
  }
1095
- if (callee.type === import_utils12.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils12.AST_NODE_TYPES.Identifier) {
1507
+ if (callee.type === import_utils13.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils13.AST_NODE_TYPES.Identifier) {
1096
1508
  const name = callee.property.name;
1097
1509
  if (HOOK_REGEX.test(name) || name === "createContext") {
1098
1510
  hasClientIndicator = true;
@@ -1102,7 +1514,7 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1102
1514
  return {
1103
1515
  Program(node) {
1104
1516
  for (const stmt of node.body) {
1105
- if (stmt.type !== import_utils12.AST_NODE_TYPES.ExpressionStatement) break;
1517
+ if (stmt.type !== import_utils13.AST_NODE_TYPES.ExpressionStatement) break;
1106
1518
  if (isUseClientDirective(stmt)) {
1107
1519
  directiveNode = stmt;
1108
1520
  break;
@@ -1115,7 +1527,7 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1115
1527
  },
1116
1528
  JSXAttribute(node) {
1117
1529
  if (directiveNode === null) return;
1118
- if (node.name.type === import_utils12.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
1530
+ if (node.name.type === import_utils13.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
1119
1531
  hasClientIndicator = true;
1120
1532
  }
1121
1533
  },
@@ -1164,8 +1576,8 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
1164
1576
  });
1165
1577
 
1166
1578
  // src/rules/prefer-discriminated-union.ts
1167
- var import_utils13 = require("@typescript-eslint/utils");
1168
1579
  var import_utils14 = require("@typescript-eslint/utils");
1580
+ var import_utils15 = require("@typescript-eslint/utils");
1169
1581
  var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
1170
1582
  "success",
1171
1583
  "ok",
@@ -1175,26 +1587,26 @@ var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
1175
1587
  ]);
1176
1588
  var MIN_OPTIONAL_MEMBERS = 2;
1177
1589
  function getMemberName(member) {
1178
- if (member.type !== import_utils14.AST_NODE_TYPES.TSPropertySignature) {
1590
+ if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
1179
1591
  return null;
1180
1592
  }
1181
1593
  const { key } = member;
1182
- if (key.type === import_utils14.AST_NODE_TYPES.Identifier) {
1594
+ if (key.type === import_utils15.AST_NODE_TYPES.Identifier) {
1183
1595
  return key.name;
1184
1596
  }
1185
- if (key.type === import_utils14.AST_NODE_TYPES.Literal && typeof key.value === "string") {
1597
+ if (key.type === import_utils15.AST_NODE_TYPES.Literal && typeof key.value === "string") {
1186
1598
  return key.value;
1187
1599
  }
1188
1600
  return null;
1189
1601
  }
1190
1602
  function isBooleanTyped(member) {
1191
- return member.typeAnnotation?.typeAnnotation.type === import_utils14.AST_NODE_TYPES.TSBooleanKeyword;
1603
+ return member.typeAnnotation?.typeAnnotation.type === import_utils15.AST_NODE_TYPES.TSBooleanKeyword;
1192
1604
  }
1193
1605
  function looksLikeMutuallyExclusiveState(typeLiteral) {
1194
1606
  let hasStatusBoolean = false;
1195
1607
  let optionalCount = 0;
1196
1608
  for (const member of typeLiteral.members) {
1197
- if (member.type !== import_utils14.AST_NODE_TYPES.TSPropertySignature) {
1609
+ if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
1198
1610
  continue;
1199
1611
  }
1200
1612
  if (member.optional) {
@@ -1207,7 +1619,7 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
1207
1619
  }
1208
1620
  return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
1209
1621
  }
1210
- var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
1622
+ var prefer_discriminated_union_default = import_utils14.ESLintUtils.RuleCreator(
1211
1623
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1212
1624
  )({
1213
1625
  name: "prefer-discriminated-union",
@@ -1235,7 +1647,7 @@ var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
1235
1647
  TSInterfaceDeclaration(node) {
1236
1648
  const synthetic = {
1237
1649
  ...node.body,
1238
- type: import_utils14.AST_NODE_TYPES.TSTypeLiteral,
1650
+ type: import_utils15.AST_NODE_TYPES.TSTypeLiteral,
1239
1651
  members: node.body.body
1240
1652
  };
1241
1653
  checkTypeLiteral(synthetic, node);
@@ -1248,13 +1660,13 @@ var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
1248
1660
  });
1249
1661
 
1250
1662
  // src/rules/prefer-schema-for-api-payload.ts
1251
- var import_utils15 = require("@typescript-eslint/utils");
1663
+ var import_utils16 = require("@typescript-eslint/utils");
1252
1664
  var unwrap = (node) => {
1253
1665
  let current = node;
1254
1666
  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) {
1667
+ 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
1668
  current = current.expression;
1257
- } else if (current.type === import_utils15.AST_NODE_TYPES.ChainExpression) {
1669
+ } else if (current.type === import_utils16.AST_NODE_TYPES.ChainExpression) {
1258
1670
  current = current.expression;
1259
1671
  } else {
1260
1672
  break;
@@ -1265,18 +1677,18 @@ var unwrap = (node) => {
1265
1677
  var isJsonCall = (node) => {
1266
1678
  let current = unwrap(node);
1267
1679
  if (current === null) return false;
1268
- if (current.type === import_utils15.AST_NODE_TYPES.AwaitExpression) {
1680
+ if (current.type === import_utils16.AST_NODE_TYPES.AwaitExpression) {
1269
1681
  current = unwrap(current.argument);
1270
1682
  }
1271
- if (current === null || current.type !== import_utils15.AST_NODE_TYPES.CallExpression) {
1683
+ if (current === null || current.type !== import_utils16.AST_NODE_TYPES.CallExpression) {
1272
1684
  return false;
1273
1685
  }
1274
1686
  const callee = unwrap(current.callee);
1275
- if (callee === null || callee.type !== import_utils15.AST_NODE_TYPES.MemberExpression) {
1687
+ if (callee === null || callee.type !== import_utils16.AST_NODE_TYPES.MemberExpression) {
1276
1688
  return false;
1277
1689
  }
1278
1690
  const property = unwrap(callee.property);
1279
- return property !== null && property.type === import_utils15.AST_NODE_TYPES.Identifier && property.name === "json";
1691
+ return property !== null && property.type === import_utils16.AST_NODE_TYPES.Identifier && property.name === "json";
1280
1692
  };
1281
1693
  var findVariable2 = (scope, name) => {
1282
1694
  let current = scope;
@@ -1287,15 +1699,39 @@ var findVariable2 = (scope, name) => {
1287
1699
  }
1288
1700
  return null;
1289
1701
  };
1702
+ var GUARD_NAME_RE = /^is[A-Z]/;
1703
+ var isGuardTestPosition = (node) => {
1704
+ let current = node;
1705
+ let parent = current.parent;
1706
+ while (parent !== void 0 && parent !== null) {
1707
+ switch (parent.type) {
1708
+ case import_utils16.AST_NODE_TYPES.UnaryExpression:
1709
+ case import_utils16.AST_NODE_TYPES.LogicalExpression:
1710
+ case import_utils16.AST_NODE_TYPES.ChainExpression:
1711
+ current = parent;
1712
+ parent = parent.parent;
1713
+ continue;
1714
+ case import_utils16.AST_NODE_TYPES.IfStatement:
1715
+ case import_utils16.AST_NODE_TYPES.ConditionalExpression:
1716
+ case import_utils16.AST_NODE_TYPES.WhileStatement:
1717
+ case import_utils16.AST_NODE_TYPES.DoWhileStatement:
1718
+ case import_utils16.AST_NODE_TYPES.ForStatement:
1719
+ return parent.test === current;
1720
+ default:
1721
+ return false;
1722
+ }
1723
+ }
1724
+ return false;
1725
+ };
1290
1726
  var isUnvalidatedVariableRef = (node, scope, tracked) => {
1291
1727
  const unwrapped = unwrap(node);
1292
- if (unwrapped === null || unwrapped.type !== import_utils15.AST_NODE_TYPES.Identifier) {
1728
+ if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
1293
1729
  return false;
1294
1730
  }
1295
1731
  const variable = findVariable2(scope, unwrapped.name);
1296
1732
  return variable !== null && tracked.has(variable);
1297
1733
  };
1298
- var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreator(
1734
+ var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreator(
1299
1735
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1300
1736
  )({
1301
1737
  name: "prefer-schema-for-api-payload",
@@ -1323,11 +1759,11 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1323
1759
  return {
1324
1760
  VariableDeclarator(node) {
1325
1761
  const scope = context.sourceCode.getScope(node);
1326
- if (node.id.type === import_utils15.AST_NODE_TYPES.Identifier) {
1762
+ if (node.id.type === import_utils16.AST_NODE_TYPES.Identifier) {
1327
1763
  trackInitializer(node);
1328
1764
  return;
1329
1765
  }
1330
- if (node.id.type === import_utils15.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils15.AST_NODE_TYPES.ArrayPattern) {
1766
+ if (node.id.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
1331
1767
  if (isJsonCall(node.init)) {
1332
1768
  context.report({ node: node.id, messageId: "unparsedJsonAccess" });
1333
1769
  return;
@@ -1339,7 +1775,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1339
1775
  },
1340
1776
  AssignmentExpression(node) {
1341
1777
  const scope = context.sourceCode.getScope(node);
1342
- if (node.left.type === import_utils15.AST_NODE_TYPES.Identifier) {
1778
+ if (node.left.type === import_utils16.AST_NODE_TYPES.Identifier) {
1343
1779
  const variable = findVariable2(scope, node.left.name);
1344
1780
  if (variable === null) return;
1345
1781
  if (isJsonCall(node.right)) {
@@ -1349,7 +1785,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1349
1785
  }
1350
1786
  return;
1351
1787
  }
1352
- if (node.left.type === import_utils15.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils15.AST_NODE_TYPES.ArrayPattern) {
1788
+ if (node.left.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
1353
1789
  if (isJsonCall(node.right)) {
1354
1790
  context.report({
1355
1791
  node: node.left,
@@ -1365,18 +1801,34 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1365
1801
  }
1366
1802
  }
1367
1803
  },
1804
+ CallExpression(node) {
1805
+ if (node.callee.type !== import_utils16.AST_NODE_TYPES.Identifier) return;
1806
+ if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {
1807
+ return;
1808
+ }
1809
+ const scope = context.sourceCode.getScope(node);
1810
+ for (const arg of node.arguments) {
1811
+ if (arg.type === import_utils16.AST_NODE_TYPES.SpreadElement) continue;
1812
+ const unwrapped = unwrap(arg);
1813
+ if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
1814
+ continue;
1815
+ }
1816
+ const variable = findVariable2(scope, unwrapped.name);
1817
+ if (variable !== null) unvalidatedVariables.delete(variable);
1818
+ }
1819
+ },
1368
1820
  MemberExpression(node) {
1369
1821
  const scope = context.sourceCode.getScope(node);
1370
1822
  const obj = unwrap(node.object);
1371
1823
  if (isJsonCall(obj)) {
1372
1824
  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")) {
1825
+ 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
1826
  return;
1375
1827
  }
1376
1828
  context.report({ node, messageId: "unparsedJsonAccess" });
1377
1829
  return;
1378
1830
  }
1379
- if (obj !== null && obj.type === import_utils15.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
1831
+ if (obj !== null && obj.type === import_utils16.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
1380
1832
  context.report({ node, messageId: "unparsedJsonAccess" });
1381
1833
  const variable = findVariable2(scope, obj.name);
1382
1834
  if (variable !== null) {
@@ -1389,7 +1841,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
1389
1841
  });
1390
1842
 
1391
1843
  // src/rules/prefer-semantic-colors.ts
1392
- var import_utils16 = require("@typescript-eslint/utils");
1844
+ var import_utils17 = require("@typescript-eslint/utils");
1393
1845
 
1394
1846
  // src/rules/_tailwind.ts
1395
1847
  var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
@@ -1426,12 +1878,41 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
1426
1878
  "lightingColor"
1427
1879
  ]);
1428
1880
  var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
1881
+ var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
1882
+ var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
1883
+ "mask",
1884
+ "clipPath",
1885
+ "defs",
1886
+ "pattern",
1887
+ "linearGradient",
1888
+ "radialGradient"
1889
+ ]);
1890
+ var SVG_EXEMPT_COLOR_VALUES = /* @__PURE__ */ new Set([
1891
+ "#fff",
1892
+ "#ffffff",
1893
+ "#000",
1894
+ "#000000",
1895
+ "transparent",
1896
+ "none",
1897
+ "currentcolor",
1898
+ "inherit"
1899
+ ]);
1900
+ var isInsideSvgDefsContainer = (node) => {
1901
+ let current = node.parent;
1902
+ while (current !== void 0 && current !== null) {
1903
+ if (current.type === import_utils17.AST_NODE_TYPES.JSXElement && current.openingElement.name.type === import_utils17.AST_NODE_TYPES.JSXIdentifier && SVG_DEFS_CONTAINERS.has(current.openingElement.name.name)) {
1904
+ return true;
1905
+ }
1906
+ current = current.parent;
1907
+ }
1908
+ return false;
1909
+ };
1429
1910
  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;
1911
+ if (key.type === import_utils17.AST_NODE_TYPES.Identifier) return key.name;
1912
+ if (key.type === import_utils17.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
1432
1913
  return null;
1433
1914
  };
1434
- var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1915
+ var prefer_semantic_colors_default = import_utils17.ESLintUtils.RuleCreator(
1435
1916
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
1436
1917
  )({
1437
1918
  name: "prefer-semantic-colors",
@@ -1449,6 +1930,7 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1449
1930
  },
1450
1931
  defaultOptions: [],
1451
1932
  create(context) {
1933
+ if (STORIES_FILE_RE.test(context.filename)) return {};
1452
1934
  const reportClasses = (value, node) => {
1453
1935
  for (const token of classTokens(value)) {
1454
1936
  const base = tailwindBase(token);
@@ -1462,27 +1944,27 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1462
1944
  const checkClassNode = (node) => {
1463
1945
  if (node === null) return;
1464
1946
  switch (node.type) {
1465
- case import_utils16.AST_NODE_TYPES.Literal:
1947
+ case import_utils17.AST_NODE_TYPES.Literal:
1466
1948
  if (typeof node.value === "string") reportClasses(node.value, node);
1467
1949
  break;
1468
- case import_utils16.AST_NODE_TYPES.TemplateLiteral:
1950
+ case import_utils17.AST_NODE_TYPES.TemplateLiteral:
1469
1951
  for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? "", quasi);
1470
1952
  break;
1471
- case import_utils16.AST_NODE_TYPES.ArrayExpression:
1953
+ case import_utils17.AST_NODE_TYPES.ArrayExpression:
1472
1954
  for (const element of node.elements) {
1473
- if (element !== null && element.type !== import_utils16.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
1955
+ if (element !== null && element.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
1474
1956
  }
1475
1957
  break;
1476
- case import_utils16.AST_NODE_TYPES.ObjectExpression:
1958
+ case import_utils17.AST_NODE_TYPES.ObjectExpression:
1477
1959
  for (const property of node.properties) {
1478
- if (property.type === import_utils16.AST_NODE_TYPES.Property) checkClassNode(property.value);
1960
+ if (property.type === import_utils17.AST_NODE_TYPES.Property) checkClassNode(property.value);
1479
1961
  }
1480
1962
  break;
1481
- case import_utils16.AST_NODE_TYPES.ConditionalExpression:
1963
+ case import_utils17.AST_NODE_TYPES.ConditionalExpression:
1482
1964
  checkClassNode(node.consequent);
1483
1965
  checkClassNode(node.alternate);
1484
1966
  break;
1485
- case import_utils16.AST_NODE_TYPES.LogicalExpression:
1967
+ case import_utils17.AST_NODE_TYPES.LogicalExpression:
1486
1968
  checkClassNode(node.right);
1487
1969
  break;
1488
1970
  default:
@@ -1490,29 +1972,29 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1490
1972
  }
1491
1973
  };
1492
1974
  const checkColorValueNode = (node) => {
1493
- if (node.type === import_utils16.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
1975
+ if (node.type === import_utils17.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
1494
1976
  context.report({ node, messageId: "inlineColor", data: { value: node.value } });
1495
1977
  }
1496
1978
  };
1497
1979
  return {
1498
1980
  "JSXAttribute[name.name='className']"(node) {
1499
1981
  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) {
1982
+ if (node.value.type === import_utils17.AST_NODE_TYPES.Literal) checkClassNode(node.value);
1983
+ else if (node.value.type === import_utils17.AST_NODE_TYPES.JSXExpressionContainer) {
1984
+ if (node.value.expression.type !== import_utils17.AST_NODE_TYPES.JSXEmptyExpression) {
1503
1985
  checkClassNode(node.value.expression);
1504
1986
  }
1505
1987
  }
1506
1988
  },
1507
1989
  CallExpression(node) {
1508
- if (node.callee.type === import_utils16.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
1990
+ if (node.callee.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
1509
1991
  for (const arg of node.arguments) {
1510
- if (arg.type !== import_utils16.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
1992
+ if (arg.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
1511
1993
  }
1512
1994
  }
1513
1995
  },
1514
1996
  VariableDeclarator(node) {
1515
- if (node.id.type === import_utils16.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
1997
+ if (node.id.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
1516
1998
  checkClassNode(node.init);
1517
1999
  }
1518
2000
  },
@@ -1520,9 +2002,16 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1520
2002
  const name = propName(node.key);
1521
2003
  if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);
1522
2004
  },
1523
- // SVG presentation attributes: <path fill="#000" stroke="#fff" />
2005
+ // SVG presentation attributes: <path fill="#7c3aed" stroke="#7c3aed" />.
2006
+ // Neutral drawing literals and anything inside an SVG defs container are
2007
+ // structural, not UI tokens, so they never fire.
1524
2008
  "JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
1525
- if (node.value?.type === import_utils16.AST_NODE_TYPES.Literal) checkColorValueNode(node.value);
2009
+ if (node.value?.type !== import_utils17.AST_NODE_TYPES.Literal) return;
2010
+ if (typeof node.value.value === "string" && SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())) {
2011
+ return;
2012
+ }
2013
+ if (isInsideSvgDefsContainer(node)) return;
2014
+ checkColorValueNode(node.value);
1526
2015
  },
1527
2016
  // Inline style objects: style={{ color: "#111827", backgroundColor: "#fff" }}
1528
2017
  "JSXAttribute[name.name='style'] ObjectExpression > Property"(node) {
@@ -1534,7 +2023,7 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
1534
2023
  });
1535
2024
 
1536
2025
  // src/rules/prefer-server-actions.ts
1537
- var import_utils17 = require("@typescript-eslint/utils");
2026
+ var import_utils18 = require("@typescript-eslint/utils");
1538
2027
  var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
1539
2028
  var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
1540
2029
  var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
@@ -1613,7 +2102,7 @@ function getPropertyNode(objNode, propName2) {
1613
2102
  }
1614
2103
  return null;
1615
2104
  }
1616
- var prefer_server_actions_default = import_utils17.ESLintUtils.RuleCreator(
2105
+ var prefer_server_actions_default = import_utils18.ESLintUtils.RuleCreator(
1617
2106
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1618
2107
  )({
1619
2108
  name: "prefer-server-actions",
@@ -1681,14 +2170,38 @@ var prefer_server_actions_default = import_utils17.ESLintUtils.RuleCreator(
1681
2170
  });
1682
2171
 
1683
2172
  // src/rules/prefer-shadcn.ts
1684
- var import_utils18 = require("@typescript-eslint/utils");
2173
+ var import_utils19 = require("@typescript-eslint/utils");
1685
2174
  var REPLACEMENTS = {
1686
- input: "Input",
1687
2175
  select: "Select",
1688
2176
  textarea: "Textarea",
1689
2177
  dialog: "Dialog"
1690
2178
  };
1691
- var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
2179
+ var INPUT_TYPE_REPLACEMENTS = {
2180
+ checkbox: "Checkbox",
2181
+ radio: "RadioGroup",
2182
+ range: "Slider"
2183
+ };
2184
+ var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
2185
+ var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
2186
+ var literalTypeAttr = (node) => {
2187
+ for (const attribute of node.attributes) {
2188
+ if (attribute.type !== import_utils19.AST_NODE_TYPES.JSXAttribute || attribute.name.type !== import_utils19.AST_NODE_TYPES.JSXIdentifier || attribute.name.name !== "type") {
2189
+ continue;
2190
+ }
2191
+ if (attribute.value?.type === import_utils19.AST_NODE_TYPES.Literal && typeof attribute.value.value === "string") {
2192
+ return { kind: "literal", value: attribute.value.value.toLowerCase() };
2193
+ }
2194
+ return { kind: "dynamic" };
2195
+ }
2196
+ return null;
2197
+ };
2198
+ var resolveInputReplacement = (node) => {
2199
+ const typeAttr = literalTypeAttr(node);
2200
+ if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
2201
+ if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
2202
+ return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
2203
+ };
2204
+ var prefer_shadcn_default = import_utils19.ESLintUtils.RuleCreator(
1692
2205
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1693
2206
  )({
1694
2207
  name: "prefer-shadcn",
@@ -1710,8 +2223,8 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
1710
2223
  return;
1711
2224
  }
1712
2225
  const elementName = node.name.name;
1713
- const replacement = REPLACEMENTS[elementName];
1714
- if (replacement === void 0) {
2226
+ const replacement = elementName === "input" ? resolveInputReplacement(node) : REPLACEMENTS[elementName];
2227
+ if (replacement === void 0 || replacement === null) {
1715
2228
  return;
1716
2229
  }
1717
2230
  context.report({
@@ -1720,7 +2233,7 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
1720
2233
  data: {
1721
2234
  element: elementName,
1722
2235
  replacement,
1723
- lowercase: elementName
2236
+ lowercase: kebabCase(replacement)
1724
2237
  }
1725
2238
  });
1726
2239
  }
@@ -1729,41 +2242,57 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
1729
2242
  });
1730
2243
 
1731
2244
  // src/rules/require-assert-never.ts
1732
- var import_utils19 = require("@typescript-eslint/utils");
2245
+ var import_utils20 = require("@typescript-eslint/utils");
1733
2246
  var isAssertNeverCall = (expression) => {
1734
- if (expression.type !== import_utils19.AST_NODE_TYPES.CallExpression) return false;
2247
+ if (expression.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
1735
2248
  const callee = expression.callee;
1736
- if (callee.type === import_utils19.AST_NODE_TYPES.Identifier) {
2249
+ if (callee.type === import_utils20.AST_NODE_TYPES.Identifier) {
1737
2250
  return callee.name === "assertNever";
1738
2251
  }
1739
- if (callee.type === import_utils19.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils19.AST_NODE_TYPES.Identifier) {
2252
+ if (callee.type === import_utils20.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils20.AST_NODE_TYPES.Identifier) {
1740
2253
  return callee.property.name === "assertNever";
1741
2254
  }
1742
2255
  return false;
1743
2256
  };
1744
2257
  var statementContainsAssertNever = (statement) => {
1745
- if (statement.type === import_utils19.AST_NODE_TYPES.ExpressionStatement) {
2258
+ if (statement.type === import_utils20.AST_NODE_TYPES.ExpressionStatement) {
1746
2259
  return isAssertNeverCall(statement.expression);
1747
2260
  }
1748
- if (statement.type === import_utils19.AST_NODE_TYPES.ThrowStatement) {
2261
+ if (statement.type === import_utils20.AST_NODE_TYPES.ThrowStatement) {
1749
2262
  return isAssertNeverCall(statement.argument);
1750
2263
  }
1751
- if (statement.type === import_utils19.AST_NODE_TYPES.ReturnStatement) {
2264
+ if (statement.type === import_utils20.AST_NODE_TYPES.ReturnStatement) {
1752
2265
  return statement.argument !== null && isAssertNeverCall(statement.argument);
1753
2266
  }
1754
- if (statement.type === import_utils19.AST_NODE_TYPES.BlockStatement) {
2267
+ if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
1755
2268
  return statement.body.some(statementContainsAssertNever);
1756
2269
  }
1757
2270
  return false;
1758
2271
  };
1759
2272
  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) {
2273
+ if (statement.type === import_utils20.AST_NODE_TYPES.EmptyStatement) return false;
2274
+ if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
1762
2275
  return statement.body.some(isRuntimeHandlingStatement);
1763
2276
  }
1764
2277
  return true;
1765
2278
  };
1766
- var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
2279
+ var isFallthroughDefault = (node, defaultIndex) => {
2280
+ const defaultCase = node.cases[defaultIndex];
2281
+ return defaultCase !== void 0 && defaultCase.consequent.length === 0 && defaultIndex < node.cases.length - 1;
2282
+ };
2283
+ var isCommentOnlyNoopDefault = (defaultCase, sourceCode) => {
2284
+ if (defaultCase.consequent.length === 0) {
2285
+ const defaultToken = sourceCode.getFirstToken(defaultCase);
2286
+ const colonToken = defaultToken ? sourceCode.getTokenAfter(defaultToken) : null;
2287
+ return colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0;
2288
+ }
2289
+ const only = defaultCase.consequent[0];
2290
+ if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === import_utils20.AST_NODE_TYPES.BlockStatement && only.body.length === 0) {
2291
+ return sourceCode.getCommentsInside(only).length > 0;
2292
+ }
2293
+ return false;
2294
+ };
2295
+ var require_assert_never_default = import_utils20.ESLintUtils.RuleCreator(
1767
2296
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1768
2297
  )({
1769
2298
  name: "require-assert-never",
@@ -1781,12 +2310,16 @@ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
1781
2310
  create(context) {
1782
2311
  return {
1783
2312
  SwitchStatement(node) {
1784
- const defaultCase = node.cases.find(
2313
+ const defaultIndex = node.cases.findIndex(
1785
2314
  (caseNode) => caseNode.test === null
1786
2315
  );
1787
- if (!defaultCase) return;
2316
+ if (defaultIndex === -1) return;
2317
+ const defaultCase = node.cases[defaultIndex];
2318
+ if (defaultCase === void 0) return;
1788
2319
  if (defaultCase.consequent.some(statementContainsAssertNever)) return;
1789
2320
  if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;
2321
+ if (isFallthroughDefault(node, defaultIndex)) return;
2322
+ if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;
1790
2323
  context.report({
1791
2324
  node: defaultCase,
1792
2325
  messageId: "missingAssertNever"
@@ -1797,19 +2330,19 @@ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
1797
2330
  });
1798
2331
 
1799
2332
  // src/rules/require-zod-form-validation.ts
1800
- var import_utils20 = require("@typescript-eslint/utils");
2333
+ var import_utils21 = require("@typescript-eslint/utils");
1801
2334
  var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
1802
2335
  var looksLikeZodSchema = (node) => {
1803
2336
  let current = node;
1804
2337
  while (true) {
1805
- if (current.type === import_utils20.AST_NODE_TYPES.Identifier) {
2338
+ if (current.type === import_utils21.AST_NODE_TYPES.Identifier) {
1806
2339
  return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
1807
2340
  }
1808
- if (current.type === import_utils20.AST_NODE_TYPES.CallExpression) {
2341
+ if (current.type === import_utils21.AST_NODE_TYPES.CallExpression) {
1809
2342
  current = current.callee;
1810
2343
  continue;
1811
2344
  }
1812
- if (current.type === import_utils20.AST_NODE_TYPES.MemberExpression) {
2345
+ if (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
1813
2346
  current = current.object;
1814
2347
  continue;
1815
2348
  }
@@ -1817,25 +2350,25 @@ var looksLikeZodSchema = (node) => {
1817
2350
  }
1818
2351
  };
1819
2352
  var isZodParseCall = (node) => {
1820
- if (node.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
2353
+ if (node.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
1821
2354
  const callee = node.callee;
1822
- if (callee.type !== import_utils20.AST_NODE_TYPES.MemberExpression) return false;
2355
+ if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
1823
2356
  if (callee.computed) return false;
1824
- if (callee.property.type !== import_utils20.AST_NODE_TYPES.Identifier) return false;
2357
+ if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
1825
2358
  const method = callee.property.name;
1826
2359
  if (method !== "parse" && method !== "safeParse") return false;
1827
2360
  return looksLikeZodSchema(callee.object);
1828
2361
  };
1829
2362
  var isFormDataMethodCall = (node) => {
1830
2363
  let current = node;
1831
- if (current.type === import_utils20.AST_NODE_TYPES.AwaitExpression) {
2364
+ if (current.type === import_utils21.AST_NODE_TYPES.AwaitExpression) {
1832
2365
  current = current.argument;
1833
2366
  }
1834
- if (current.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
2367
+ if (current.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
1835
2368
  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";
2369
+ 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
2370
  };
1838
- var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator(
2371
+ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator(
1839
2372
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1840
2373
  )({
1841
2374
  name: "require-zod-form-validation",
@@ -1852,14 +2385,14 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
1852
2385
  defaultOptions: [],
1853
2386
  create(context) {
1854
2387
  const isFormSourceIdentifier = (node) => {
1855
- if (node.type !== import_utils20.AST_NODE_TYPES.Identifier) return false;
2388
+ if (node.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
1856
2389
  if (/formdata/i.test(node.name)) return true;
1857
2390
  let scope = context.sourceCode.getScope(node);
1858
2391
  while (scope !== null) {
1859
2392
  const variable = scope.set.get(node.name);
1860
2393
  if (variable !== void 0 && variable.defs.length === 1) {
1861
2394
  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) {
2395
+ if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils21.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
1863
2396
  return isFormDataMethodCall(def.node.init);
1864
2397
  }
1865
2398
  return false;
@@ -1870,8 +2403,8 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
1870
2403
  };
1871
2404
  const isFormDataGetCall = (node) => {
1872
2405
  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") {
2406
+ if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
2407
+ if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
1875
2408
  return false;
1876
2409
  }
1877
2410
  return isFormSourceIdentifier(callee.object);
@@ -1894,15 +2427,15 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
1894
2427
  });
1895
2428
 
1896
2429
  // src/rules/zod-naming-convention.ts
1897
- var import_utils21 = require("@typescript-eslint/utils");
2430
+ var import_utils22 = require("@typescript-eslint/utils");
1898
2431
  var calleeChainStartsWithZ = (node) => {
1899
2432
  let current = node;
1900
- while (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
2433
+ while (current.type === import_utils22.AST_NODE_TYPES.MemberExpression) {
1901
2434
  const receiver = current.object;
1902
- if (receiver.type === import_utils21.AST_NODE_TYPES.Identifier && receiver.name === "z") {
2435
+ if (receiver.type === import_utils22.AST_NODE_TYPES.Identifier && receiver.name === "z") {
1903
2436
  return true;
1904
2437
  }
1905
- if (receiver.type === import_utils21.AST_NODE_TYPES.CallExpression) {
2438
+ if (receiver.type === import_utils22.AST_NODE_TYPES.CallExpression) {
1906
2439
  current = receiver.callee;
1907
2440
  continue;
1908
2441
  }
@@ -1910,7 +2443,7 @@ var calleeChainStartsWithZ = (node) => {
1910
2443
  }
1911
2444
  return false;
1912
2445
  };
1913
- var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
2446
+ var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
1914
2447
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1915
2448
  )({
1916
2449
  name: "zod-naming-convention",
@@ -1930,11 +2463,11 @@ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
1930
2463
  VariableDeclarator(node) {
1931
2464
  const init = node.init;
1932
2465
  if (init === null || init === void 0) return;
1933
- if (init.type !== import_utils21.AST_NODE_TYPES.CallExpression) return;
2466
+ if (init.type !== import_utils22.AST_NODE_TYPES.CallExpression) return;
1934
2467
  const callee = init.callee;
1935
- if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return;
2468
+ if (callee.type !== import_utils22.AST_NODE_TYPES.MemberExpression) return;
1936
2469
  if (!calleeChainStartsWithZ(callee)) return;
1937
- if (node.id.type !== import_utils21.AST_NODE_TYPES.Identifier) return;
2470
+ if (node.id.type !== import_utils22.AST_NODE_TYPES.Identifier) return;
1938
2471
  const variableName = node.id.name;
1939
2472
  if (variableName.startsWith("Z")) return;
1940
2473
  context.report({
@@ -1947,7 +2480,7 @@ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
1947
2480
  });
1948
2481
 
1949
2482
  // src/rules/no-cors-wildcard-with-credentials.ts
1950
- var import_utils22 = require("@typescript-eslint/utils");
2483
+ var import_utils23 = require("@typescript-eslint/utils");
1951
2484
  var ACAO_HEADER = "access-control-allow-origin";
1952
2485
  var ACAC_HEADER = "access-control-allow-credentials";
1953
2486
  var HEADER_SET_METHODS = /* @__PURE__ */ new Set(["setheader", "set", "append"]);
@@ -1979,17 +2512,17 @@ function subtreeContainsStarLiteral(node) {
1979
2512
  const value = node[key];
1980
2513
  if (Array.isArray(value)) {
1981
2514
  for (const child of value) {
1982
- if (isNode2(child) && subtreeContainsStarLiteral(child)) {
2515
+ if (isNode3(child) && subtreeContainsStarLiteral(child)) {
1983
2516
  return true;
1984
2517
  }
1985
2518
  }
1986
- } else if (isNode2(value) && subtreeContainsStarLiteral(value)) {
2519
+ } else if (isNode3(value) && subtreeContainsStarLiteral(value)) {
1987
2520
  return true;
1988
2521
  }
1989
2522
  }
1990
2523
  return false;
1991
2524
  }
1992
- function isNode2(value) {
2525
+ function isNode3(value) {
1993
2526
  return typeof value === "object" && value !== null && typeof value.type === "string";
1994
2527
  }
1995
2528
  function propertyKeyName(prop) {
@@ -2005,7 +2538,7 @@ function propertyKeyName(prop) {
2005
2538
  }
2006
2539
  return void 0;
2007
2540
  }
2008
- function calleeName(node) {
2541
+ function calleeName2(node) {
2009
2542
  const callee = node.callee;
2010
2543
  if (callee.type === "Identifier") {
2011
2544
  return callee.name;
@@ -2016,7 +2549,7 @@ function calleeName(node) {
2016
2549
  return void 0;
2017
2550
  }
2018
2551
  function isCorsWildcardCredentialsCall(node) {
2019
- const name = calleeName(node);
2552
+ const name = calleeName2(node);
2020
2553
  if (name === void 0 || name.toLowerCase() !== "cors") {
2021
2554
  return false;
2022
2555
  }
@@ -2089,7 +2622,7 @@ function enclosingScope(node) {
2089
2622
  }
2090
2623
  return void 0;
2091
2624
  }
2092
- var no_cors_wildcard_with_credentials_default = import_utils22.ESLintUtils.RuleCreator(
2625
+ var no_cors_wildcard_with_credentials_default = import_utils23.ESLintUtils.RuleCreator(
2093
2626
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2094
2627
  )({
2095
2628
  name: "no-cors-wildcard-with-credentials",
@@ -2157,12 +2690,12 @@ var no_cors_wildcard_with_credentials_default = import_utils22.ESLintUtils.RuleC
2157
2690
  });
2158
2691
 
2159
2692
  // src/rules/no-fat-try-blocks.ts
2160
- var import_utils23 = require("@typescript-eslint/utils");
2693
+ var import_utils24 = require("@typescript-eslint/utils");
2161
2694
  var MAX_TRY_BODY_STATEMENTS = 3;
2162
2695
  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
2696
+ import_utils24.AST_NODE_TYPES.FunctionDeclaration,
2697
+ import_utils24.AST_NODE_TYPES.FunctionExpression,
2698
+ import_utils24.AST_NODE_TYPES.ArrowFunctionExpression
2166
2699
  ]);
2167
2700
  var PURE_METHODS = /* @__PURE__ */ new Set([
2168
2701
  "map",
@@ -2255,25 +2788,25 @@ var PURE_CONSTRUCTORS = /* @__PURE__ */ new Set([
2255
2788
  "Response",
2256
2789
  "AbortController"
2257
2790
  ]);
2258
- function isNode3(value) {
2791
+ function isNode4(value) {
2259
2792
  return typeof value === "object" && value !== null && typeof value.type === "string";
2260
2793
  }
2261
2794
  function isPureCall(node) {
2262
2795
  const callee = node.callee;
2263
- if (callee.type !== import_utils23.AST_NODE_TYPES.MemberExpression) {
2796
+ if (callee.type !== import_utils24.AST_NODE_TYPES.MemberExpression) {
2264
2797
  return false;
2265
2798
  }
2266
2799
  const property = callee.property;
2267
- if (property.type !== import_utils23.AST_NODE_TYPES.Identifier) {
2800
+ if (property.type !== import_utils24.AST_NODE_TYPES.Identifier) {
2268
2801
  return false;
2269
2802
  }
2270
- if (callee.object.type === import_utils23.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
2803
+ if (callee.object.type === import_utils24.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
2271
2804
  return true;
2272
2805
  }
2273
2806
  return PURE_METHODS.has(property.name);
2274
2807
  }
2275
2808
  function isPureNew(node) {
2276
- return node.callee.type === import_utils23.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
2809
+ return node.callee.type === import_utils24.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
2277
2810
  }
2278
2811
  function subtreeMatches(stmt, predicate) {
2279
2812
  let found = false;
@@ -2295,11 +2828,11 @@ function subtreeMatches(stmt, predicate) {
2295
2828
  const value = current[key];
2296
2829
  if (Array.isArray(value)) {
2297
2830
  for (const child of value) {
2298
- if (isNode3(child)) {
2831
+ if (isNode4(child)) {
2299
2832
  visit(child);
2300
2833
  }
2301
2834
  }
2302
- } else if (isNode3(value)) {
2835
+ } else if (isNode4(value)) {
2303
2836
  visit(value);
2304
2837
  }
2305
2838
  if (found) {
@@ -2310,14 +2843,14 @@ function subtreeMatches(stmt, predicate) {
2310
2843
  visit(stmt);
2311
2844
  return found;
2312
2845
  }
2313
- var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === import_utils23.AST_NODE_TYPES.AwaitExpression);
2846
+ var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === import_utils24.AST_NODE_TYPES.AwaitExpression);
2314
2847
  var hasThrowingCallOrNew = (stmt) => subtreeMatches(
2315
2848
  stmt,
2316
- (n) => n.type === import_utils23.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils23.AST_NODE_TYPES.NewExpression && !isPureNew(n)
2849
+ (n) => n.type === import_utils24.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils24.AST_NODE_TYPES.NewExpression && !isPureNew(n)
2317
2850
  );
2318
2851
  function unwrap2(expr) {
2319
2852
  let current = expr;
2320
- while (current.type === import_utils23.AST_NODE_TYPES.ChainExpression || current.type === import_utils23.AST_NODE_TYPES.TSNonNullExpression) {
2853
+ while (current.type === import_utils24.AST_NODE_TYPES.ChainExpression || current.type === import_utils24.AST_NODE_TYPES.TSNonNullExpression) {
2321
2854
  current = current.expression;
2322
2855
  }
2323
2856
  return current;
@@ -2326,7 +2859,7 @@ function canThrow(stmt) {
2326
2859
  if (hasAwait(stmt)) {
2327
2860
  return true;
2328
2861
  }
2329
- if (stmt.type === import_utils23.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils23.AST_NODE_TYPES.CallExpression) {
2862
+ if (stmt.type === import_utils24.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils24.AST_NODE_TYPES.CallExpression) {
2330
2863
  return false;
2331
2864
  }
2332
2865
  return hasThrowingCallOrNew(stmt);
@@ -2337,9 +2870,9 @@ function handlerRethrows(handler) {
2337
2870
  }
2338
2871
  const body = handler.body.body;
2339
2872
  const last = body[body.length - 1];
2340
- return last !== void 0 && last.type === import_utils23.AST_NODE_TYPES.ThrowStatement;
2873
+ return last !== void 0 && last.type === import_utils24.AST_NODE_TYPES.ThrowStatement;
2341
2874
  }
2342
- var no_fat_try_blocks_default = import_utils23.ESLintUtils.RuleCreator(
2875
+ var no_fat_try_blocks_default = import_utils24.ESLintUtils.RuleCreator(
2343
2876
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
2344
2877
  )({
2345
2878
  name: "no-fat-try-blocks",
@@ -2380,8 +2913,8 @@ var no_fat_try_blocks_default = import_utils23.ESLintUtils.RuleCreator(
2380
2913
  });
2381
2914
 
2382
2915
  // src/rules/no-secret-in-log.ts
2383
- var import_utils24 = require("@typescript-eslint/utils");
2384
- var LOG_METHODS = /* @__PURE__ */ new Set([
2916
+ var import_utils25 = require("@typescript-eslint/utils");
2917
+ var LOG_METHODS2 = /* @__PURE__ */ new Set([
2385
2918
  "debug",
2386
2919
  "info",
2387
2920
  "warn",
@@ -2394,7 +2927,7 @@ var LOG_METHODS = /* @__PURE__ */ new Set([
2394
2927
  "fatal",
2395
2928
  "success"
2396
2929
  ]);
2397
- var LOGGER_NAMES = /* @__PURE__ */ new Set([
2930
+ var LOGGER_NAMES2 = /* @__PURE__ */ new Set([
2398
2931
  "logger",
2399
2932
  "log",
2400
2933
  "logging",
@@ -2535,12 +3068,12 @@ function isSecretKeyword(name) {
2535
3068
  function isLoggerExpr(expr) {
2536
3069
  switch (expr.type) {
2537
3070
  case "Identifier":
2538
- return LOGGER_NAMES.has(expr.name.toLowerCase());
3071
+ return LOGGER_NAMES2.has(expr.name.toLowerCase());
2539
3072
  case "MemberExpression": {
2540
3073
  const { property, object } = expr;
2541
3074
  if (!expr.computed && property.type === "Identifier") {
2542
3075
  const lowered = property.name.toLowerCase();
2543
- if (LOGGER_NAMES.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
3076
+ if (LOGGER_NAMES2.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
2544
3077
  return true;
2545
3078
  }
2546
3079
  }
@@ -2578,7 +3111,7 @@ function propertyKeyName2(prop) {
2578
3111
  }
2579
3112
  return null;
2580
3113
  }
2581
- var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
3114
+ var no_secret_in_log_default = import_utils25.ESLintUtils.RuleCreator(
2582
3115
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
2583
3116
  )({
2584
3117
  name: "no-secret-in-log",
@@ -2597,7 +3130,7 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
2597
3130
  return {
2598
3131
  CallExpression(node) {
2599
3132
  const callee = node.callee;
2600
- if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS.has(callee.property.name)) {
3133
+ if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS2.has(callee.property.name)) {
2601
3134
  return;
2602
3135
  }
2603
3136
  if (!isLoggerExpr(callee.object)) {
@@ -2614,6 +3147,16 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
2614
3147
  }
2615
3148
  continue;
2616
3149
  }
3150
+ if (arg.type === "MemberExpression") {
3151
+ if (!arg.computed && arg.property.type === "Identifier" && isSecretKeyword(arg.property.name)) {
3152
+ context.report({
3153
+ node: arg,
3154
+ messageId: "noSecretInLog",
3155
+ data: { name: arg.property.name }
3156
+ });
3157
+ }
3158
+ continue;
3159
+ }
2617
3160
  if (arg.type === "ObjectExpression") {
2618
3161
  for (const prop of arg.properties) {
2619
3162
  if (prop.type !== "Property") {
@@ -2636,7 +3179,7 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
2636
3179
  });
2637
3180
 
2638
3181
  // src/rules/prefer-string-literal-union.ts
2639
- var import_utils25 = require("@typescript-eslint/utils");
3182
+ var import_utils26 = require("@typescript-eslint/utils");
2640
3183
  var ts = __toESM(require("typescript"), 1);
2641
3184
  var CHOICE_TOKENS = /* @__PURE__ */ new Set([
2642
3185
  "status",
@@ -2679,19 +3222,19 @@ function isChoiceLikeName(name) {
2679
3222
  return CHOICE_TOKENS.has(lastWord(name));
2680
3223
  }
2681
3224
  function keyName(key) {
2682
- if (key.type === import_utils25.AST_NODE_TYPES.Identifier) {
3225
+ if (key.type === import_utils26.AST_NODE_TYPES.Identifier) {
2683
3226
  return key.name;
2684
3227
  }
2685
- if (key.type === import_utils25.AST_NODE_TYPES.Literal && typeof key.value === "string") {
3228
+ if (key.type === import_utils26.AST_NODE_TYPES.Literal && typeof key.value === "string") {
2686
3229
  return key.value;
2687
3230
  }
2688
3231
  return null;
2689
3232
  }
2690
3233
  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";
3234
+ return t.type === import_utils26.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils26.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
2692
3235
  }
2693
3236
  function isStringLiteralUnion(node) {
2694
- if (node?.type !== import_utils25.AST_NODE_TYPES.TSUnionType) {
3237
+ if (node?.type !== import_utils26.AST_NODE_TYPES.TSUnionType) {
2695
3238
  return false;
2696
3239
  }
2697
3240
  return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
@@ -2700,13 +3243,32 @@ function typeHasRawString(type) {
2700
3243
  const parts = type.isUnion() ? type.types : [type];
2701
3244
  return parts.some((t) => (t.flags & ts.TypeFlags.String) !== 0);
2702
3245
  }
3246
+ function isExternalSourceFile(sf) {
3247
+ if (sf === void 0) {
3248
+ return false;
3249
+ }
3250
+ return sf.isDeclarationFile || sf.fileName.includes("/node_modules/");
3251
+ }
3252
+ function symbolIsExternallyDeclared(sym) {
3253
+ return sym?.declarations?.some((d) => isExternalSourceFile(d.getSourceFile())) ?? false;
3254
+ }
3255
+ function bindingSourceExpression(decl) {
3256
+ let node = decl.parent;
3257
+ while (!ts.isForOfStatement(node) && !(ts.isVariableDeclaration(node) && node.initializer !== void 0)) {
3258
+ if (node.parent === void 0) {
3259
+ return void 0;
3260
+ }
3261
+ node = node.parent;
3262
+ }
3263
+ return ts.isForOfStatement(node) ? node.expression : node.initializer;
3264
+ }
2703
3265
  function refKey(node) {
2704
- if (node.type === import_utils25.AST_NODE_TYPES.Identifier) {
3266
+ if (node.type === import_utils26.AST_NODE_TYPES.Identifier) {
2705
3267
  return node.name;
2706
3268
  }
2707
- if (node.type === import_utils25.AST_NODE_TYPES.MemberExpression && !node.computed) {
3269
+ if (node.type === import_utils26.AST_NODE_TYPES.MemberExpression && !node.computed) {
2708
3270
  const inner = refKey(node.object);
2709
- if (inner === null || node.property.type !== import_utils25.AST_NODE_TYPES.Identifier) {
3271
+ if (inner === null || node.property.type !== import_utils26.AST_NODE_TYPES.Identifier) {
2710
3272
  return null;
2711
3273
  }
2712
3274
  return `${inner}.${node.property.name}`;
@@ -2714,12 +3276,12 @@ function refKey(node) {
2714
3276
  return null;
2715
3277
  }
2716
3278
  function strLiteral(node) {
2717
- if (node.type === import_utils25.AST_NODE_TYPES.Literal && typeof node.value === "string") {
3279
+ if (node.type === import_utils26.AST_NODE_TYPES.Literal && typeof node.value === "string") {
2718
3280
  return node.value;
2719
3281
  }
2720
3282
  return null;
2721
3283
  }
2722
- var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator(
3284
+ var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator(
2723
3285
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2724
3286
  )({
2725
3287
  name: "prefer-string-literal-union",
@@ -2743,7 +3305,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2743
3305
  }
2744
3306
  let services;
2745
3307
  try {
2746
- services = import_utils25.ESLintUtils.getParserServices(context);
3308
+ services = import_utils26.ESLintUtils.getParserServices(context);
2747
3309
  } catch {
2748
3310
  services = null;
2749
3311
  }
@@ -2757,6 +3319,37 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2757
3319
  }
2758
3320
  return typeHasRawString(services.getTypeAtLocation(node));
2759
3321
  }
3322
+ function originIsExternal(node, depth) {
3323
+ if (node === void 0 || services === null || depth > 6) {
3324
+ return false;
3325
+ }
3326
+ const checker = services.program.getTypeChecker();
3327
+ if (ts.isParenthesizedExpression(node) || ts.isNonNullExpression(node) || ts.isAsExpression(node)) {
3328
+ return originIsExternal(node.expression, depth + 1);
3329
+ }
3330
+ if (ts.isPropertyAccessExpression(node)) {
3331
+ return symbolIsExternallyDeclared(checker.getSymbolAtLocation(node.name));
3332
+ }
3333
+ if (ts.isCallExpression(node)) {
3334
+ return originIsExternal(node.expression, depth + 1);
3335
+ }
3336
+ if (ts.isIdentifier(node)) {
3337
+ const decl = checker.getSymbolAtLocation(node)?.valueDeclaration;
3338
+ if (decl === void 0) {
3339
+ return false;
3340
+ }
3341
+ if (ts.isVariableDeclaration(decl) && decl.initializer !== void 0) {
3342
+ return originIsExternal(decl.initializer, depth + 1);
3343
+ }
3344
+ if (ts.isBindingElement(decl)) {
3345
+ return originIsExternal(bindingSourceExpression(decl), depth + 1);
3346
+ }
3347
+ }
3348
+ return false;
3349
+ }
3350
+ function operandIsFlaggable(node) {
3351
+ return operandIsRawString(node) && !originIsExternal(services?.esTreeNodeToTSNodeMap.get(node), 0);
3352
+ }
2760
3353
  function pushScope() {
2761
3354
  scopeStack.push({ clusters: /* @__PURE__ */ new Map() });
2762
3355
  }
@@ -2796,7 +3389,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2796
3389
  containersWithUnion.add(container);
2797
3390
  return;
2798
3391
  }
2799
- if (typeNode?.type !== import_utils25.AST_NODE_TYPES.TSStringKeyword) {
3392
+ if (typeNode?.type !== import_utils26.AST_NODE_TYPES.TSStringKeyword) {
2800
3393
  return;
2801
3394
  }
2802
3395
  const name = keyName(key);
@@ -2821,18 +3414,18 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2821
3414
  const rightKey = refKey(node.right);
2822
3415
  const leftLit = strLiteral(node.left);
2823
3416
  if (leftKey !== null && rightLit !== null) {
2824
- if (operandIsRawString(node.left)) {
3417
+ if (operandIsFlaggable(node.left)) {
2825
3418
  accumulate(leftKey, [rightLit], node);
2826
3419
  }
2827
3420
  } else if (rightKey !== null && leftLit !== null) {
2828
- if (operandIsRawString(node.right)) {
3421
+ if (operandIsFlaggable(node.right)) {
2829
3422
  accumulate(rightKey, [leftLit], node);
2830
3423
  }
2831
3424
  }
2832
3425
  },
2833
3426
  SwitchStatement(node) {
2834
3427
  const key = refKey(node.discriminant);
2835
- if (key === null || !operandIsRawString(node.discriminant)) {
3428
+ if (key === null || !operandIsFlaggable(node.discriminant)) {
2836
3429
  return;
2837
3430
  }
2838
3431
  const literals = [];
@@ -2884,10 +3477,10 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2884
3477
  }
2885
3478
  };
2886
3479
  function refKeyText(node) {
2887
- if (node.type === import_utils25.AST_NODE_TYPES.BinaryExpression) {
3480
+ if (node.type === import_utils26.AST_NODE_TYPES.BinaryExpression) {
2888
3481
  return refKey(node.left) ?? refKey(node.right) ?? "value";
2889
3482
  }
2890
- if (node.type === import_utils25.AST_NODE_TYPES.SwitchStatement) {
3483
+ if (node.type === import_utils26.AST_NODE_TYPES.SwitchStatement) {
2891
3484
  return refKey(node.discriminant) ?? "value";
2892
3485
  }
2893
3486
  return "value";
@@ -2896,7 +3489,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
2896
3489
  });
2897
3490
 
2898
3491
  // src/rules/single-public-export.ts
2899
- var import_utils26 = require("@typescript-eslint/utils");
3492
+ var import_utils27 = require("@typescript-eslint/utils");
2900
3493
  var JUNK_DRAWER_STEMS = /* @__PURE__ */ new Set([
2901
3494
  "util",
2902
3495
  "utils",
@@ -2923,20 +3516,20 @@ var TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/i;
2923
3516
  var SCRIPT_EXT_RE = /\.[cm]?[jt]sx?$/i;
2924
3517
  var basename = (filename) => filename.split(/[/\\]/).pop() ?? filename;
2925
3518
  var stemOf = (base) => base.replace(SCRIPT_EXT_RE, "");
2926
- var kebabCase = (name) => {
3519
+ var kebabCase2 = (name) => {
2927
3520
  let normalized = name;
2928
3521
  for (const [pattern, replacement] of ACRONYM_OVERRIDES) {
2929
3522
  normalized = normalized.replace(pattern, replacement);
2930
3523
  }
2931
3524
  return normalized.replace(CAMEL_BOUNDARY_RE, "-").toLowerCase();
2932
3525
  };
2933
- var isFunctionExpression2 = (node) => node !== null && (node.type === import_utils26.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils26.AST_NODE_TYPES.FunctionExpression);
3526
+ var isFunctionExpression = (node) => node !== null && (node.type === import_utils27.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils27.AST_NODE_TYPES.FunctionExpression);
2934
3527
  var functionConstName = (decl) => {
2935
3528
  if (decl.declarations.length !== 1) return null;
2936
3529
  const [declarator] = decl.declarations;
2937
3530
  if (declarator === void 0) return null;
2938
- if (declarator.id.type !== import_utils26.AST_NODE_TYPES.Identifier) return null;
2939
- if (!isFunctionExpression2(declarator.init)) return null;
3531
+ if (declarator.id.type !== import_utils27.AST_NODE_TYPES.Identifier) return null;
3532
+ if (!isFunctionExpression(declarator.init)) return null;
2940
3533
  return declarator.id.name;
2941
3534
  };
2942
3535
  var summarizeExports = (body) => {
@@ -2949,20 +3542,20 @@ var summarizeExports = (body) => {
2949
3542
  };
2950
3543
  for (const statement of body) {
2951
3544
  switch (statement.type) {
2952
- case import_utils26.AST_NODE_TYPES.ExportAllDeclaration:
3545
+ case import_utils27.AST_NODE_TYPES.ExportAllDeclaration:
2953
3546
  hasReExport = true;
2954
3547
  break;
2955
- case import_utils26.AST_NODE_TYPES.ExportDefaultDeclaration: {
3548
+ case import_utils27.AST_NODE_TYPES.ExportDefaultDeclaration: {
2956
3549
  names += 1;
2957
3550
  const decl = statement.declaration;
2958
- if (decl.type === import_utils26.AST_NODE_TYPES.FunctionDeclaration && decl.id !== null) {
3551
+ if (decl.type === import_utils27.AST_NODE_TYPES.FunctionDeclaration && decl.id !== null) {
2959
3552
  candidate = { name: decl.id.name, node: statement };
2960
- } else if (decl.type === import_utils26.AST_NODE_TYPES.ClassDeclaration && decl.id !== null) {
3553
+ } else if (decl.type === import_utils27.AST_NODE_TYPES.ClassDeclaration && decl.id !== null) {
2961
3554
  candidate = { name: decl.id.name, node: statement };
2962
3555
  }
2963
3556
  break;
2964
3557
  }
2965
- case import_utils26.AST_NODE_TYPES.ExportNamedDeclaration: {
3558
+ case import_utils27.AST_NODE_TYPES.ExportNamedDeclaration: {
2966
3559
  if (statement.source !== null) {
2967
3560
  hasReExport = true;
2968
3561
  break;
@@ -2973,15 +3566,15 @@ var summarizeExports = (body) => {
2973
3566
  break;
2974
3567
  }
2975
3568
  switch (decl.type) {
2976
- case import_utils26.AST_NODE_TYPES.FunctionDeclaration:
3569
+ case import_utils27.AST_NODE_TYPES.FunctionDeclaration:
2977
3570
  if (decl.id !== null) addCandidate(decl.id.name, statement);
2978
3571
  else names += 1;
2979
3572
  break;
2980
- case import_utils26.AST_NODE_TYPES.ClassDeclaration:
3573
+ case import_utils27.AST_NODE_TYPES.ClassDeclaration:
2981
3574
  if (decl.id !== null) addCandidate(decl.id.name, statement);
2982
3575
  else names += 1;
2983
3576
  break;
2984
- case import_utils26.AST_NODE_TYPES.VariableDeclaration: {
3577
+ case import_utils27.AST_NODE_TYPES.VariableDeclaration: {
2985
3578
  const fnName = functionConstName(decl);
2986
3579
  if (fnName !== null && decl.declarations.length === 1) {
2987
3580
  addCandidate(fnName, statement);
@@ -3001,7 +3594,7 @@ var summarizeExports = (body) => {
3001
3594
  }
3002
3595
  return { names, hasReExport, candidate };
3003
3596
  };
3004
- var single_public_export_default = import_utils26.ESLintUtils.RuleCreator(
3597
+ var single_public_export_default = import_utils27.ESLintUtils.RuleCreator(
3005
3598
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
3006
3599
  )({
3007
3600
  name: "single-public-export",
@@ -3028,7 +3621,7 @@ var single_public_export_default = import_utils26.ESLintUtils.RuleCreator(
3028
3621
  if (hasReExport) return;
3029
3622
  if (names !== 1 || candidate === null) return;
3030
3623
  if (CONVENTIONAL_BUCKET_EXPORTS.has(candidate.name)) return;
3031
- const expected = kebabCase(candidate.name);
3624
+ const expected = kebabCase2(candidate.name);
3032
3625
  if (stem === expected) return;
3033
3626
  context.report({
3034
3627
  node: candidate.node,
@@ -3071,7 +3664,7 @@ var rules = {
3071
3664
  var plugin = {
3072
3665
  meta: {
3073
3666
  name: "@sarj/eslint-plugin",
3074
- version: "2.3.3"
3667
+ version: "2.4.0"
3075
3668
  },
3076
3669
  rules,
3077
3670
  configs: {