@sarj/eslint-plugin 4.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -35,7 +35,7 @@ __export(index_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
- // src/rules/ban-loose-type-guards-in-tests.ts
38
+ // src/rules/enforce-file-structure.ts
39
39
  var import_utils = require("@typescript-eslint/utils");
40
40
 
41
41
  // src/rules/_paths.ts
@@ -61,63 +61,27 @@ function isScriptFile(filename) {
61
61
  return SCRIPT_FILE_RE.test(filename);
62
62
  }
63
63
 
64
- // src/rules/ban-loose-type-guards-in-tests.ts
65
- var ban_loose_type_guards_in_tests_default = import_utils.ESLintUtils.RuleCreator(
66
- (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
67
- )({
68
- name: "ban-loose-type-guards-in-tests",
69
- meta: {
70
- type: "problem",
71
- docs: {
72
- description: "Disallow loose type guards (`typeof` and `in`) in test files. Enforce Zod schemas or strict matchers for type validation."
73
- },
74
- schema: [],
75
- messages: {
76
- banLooseTypeGuardsInTests: "Do not use `typeof` or `in` for type validation in tests. Use Zod schemas or strict assertion matchers instead."
77
- }
78
- },
79
- defaultOptions: [],
80
- create(context) {
81
- if (!isTestFile(context.filename)) {
82
- return {};
83
- }
84
- return {
85
- UnaryExpression(node) {
86
- if (node.operator === "typeof") {
87
- context.report({ node, messageId: "banLooseTypeGuardsInTests" });
88
- }
89
- },
90
- BinaryExpression(node) {
91
- if (node.operator === "in") {
92
- context.report({ node, messageId: "banLooseTypeGuardsInTests" });
93
- }
94
- }
95
- };
96
- }
97
- });
98
-
99
64
  // src/rules/enforce-file-structure.ts
100
- var import_utils2 = require("@typescript-eslint/utils");
101
65
  var classifyStatement = (statement) => {
102
66
  switch (statement.type) {
103
- case import_utils2.AST_NODE_TYPES.ImportDeclaration:
67
+ case import_utils.AST_NODE_TYPES.ImportDeclaration:
104
68
  return "import";
105
- case import_utils2.AST_NODE_TYPES.ExportAllDeclaration:
69
+ case import_utils.AST_NODE_TYPES.ExportAllDeclaration:
106
70
  return "reexport";
107
- case import_utils2.AST_NODE_TYPES.ExportNamedDeclaration:
71
+ case import_utils.AST_NODE_TYPES.ExportNamedDeclaration:
108
72
  return statement.declaration === null ? "reexport" : "body";
109
73
  default:
110
74
  return "body";
111
75
  }
112
76
  };
113
- var isStringDirective = (statement) => statement.type === import_utils2.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils2.AST_NODE_TYPES.Literal && typeof statement.expression.value === "string" && statement.expression.value.startsWith("use ");
77
+ 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 ");
114
78
  var isUseServerDirective = (statement) => {
115
- if (statement.type !== import_utils2.AST_NODE_TYPES.ExpressionStatement) return false;
79
+ if (statement.type !== import_utils.AST_NODE_TYPES.ExpressionStatement) return false;
116
80
  const expr = statement.expression;
117
- if (expr.type !== import_utils2.AST_NODE_TYPES.Literal) return false;
81
+ if (expr.type !== import_utils.AST_NODE_TYPES.Literal) return false;
118
82
  return expr.value === "use server";
119
83
  };
120
- var enforce_file_structure_default = import_utils2.ESLintUtils.RuleCreator(
84
+ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
121
85
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
122
86
  )({
123
87
  name: "enforce-file-structure",
@@ -177,7 +141,7 @@ var enforce_file_structure_default = import_utils2.ESLintUtils.RuleCreator(
177
141
  });
178
142
 
179
143
  // src/rules/no-client-side-data-fetching.ts
180
- var import_utils3 = require("@typescript-eslint/utils");
144
+ var import_utils2 = require("@typescript-eslint/utils");
181
145
  var FETCH_LIBS = /* @__PURE__ */ new Set(["axios", "ky", "superagent"]);
182
146
  var HTTP_METHOD_NAMES = /* @__PURE__ */ new Set([
183
147
  "get",
@@ -201,25 +165,25 @@ var ANALYTICS_SEGMENTS = /* @__PURE__ */ new Set([
201
165
  ]);
202
166
  function isEffectHookCall(node) {
203
167
  const callee = node.callee;
204
- if (callee.type === import_utils3.AST_NODE_TYPES.Identifier) {
168
+ if (callee.type === import_utils2.AST_NODE_TYPES.Identifier) {
205
169
  return callee.name === "useEffect" || callee.name === "useLayoutEffect";
206
170
  }
207
- if (callee.type === import_utils3.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils3.AST_NODE_TYPES.Identifier && callee.object.name === "React" && callee.property.type === import_utils3.AST_NODE_TYPES.Identifier) {
171
+ if (callee.type === import_utils2.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils2.AST_NODE_TYPES.Identifier && callee.object.name === "React" && callee.property.type === import_utils2.AST_NODE_TYPES.Identifier) {
208
172
  return callee.property.name === "useEffect" || callee.property.name === "useLayoutEffect";
209
173
  }
210
174
  return false;
211
175
  }
212
176
  function readMethodProperty(optionsArg) {
213
- if (!optionsArg || optionsArg.type !== import_utils3.AST_NODE_TYPES.ObjectExpression) {
177
+ if (!optionsArg || optionsArg.type !== import_utils2.AST_NODE_TYPES.ObjectExpression) {
214
178
  return null;
215
179
  }
216
180
  for (const prop of optionsArg.properties) {
217
- if (prop.type !== import_utils3.AST_NODE_TYPES.Property) continue;
181
+ if (prop.type !== import_utils2.AST_NODE_TYPES.Property) continue;
218
182
  if (prop.computed) continue;
219
183
  const key = prop.key;
220
- const matchesMethodKey = key.type === import_utils3.AST_NODE_TYPES.Identifier && key.name === "method" || key.type === import_utils3.AST_NODE_TYPES.Literal && key.value === "method";
184
+ const matchesMethodKey = key.type === import_utils2.AST_NODE_TYPES.Identifier && key.name === "method" || key.type === import_utils2.AST_NODE_TYPES.Literal && key.value === "method";
221
185
  if (!matchesMethodKey) continue;
222
- if (prop.value.type === import_utils3.AST_NODE_TYPES.Literal && typeof prop.value.value === "string") {
186
+ if (prop.value.type === import_utils2.AST_NODE_TYPES.Literal && typeof prop.value.value === "string") {
223
187
  return prop.value.value.toUpperCase();
224
188
  }
225
189
  return null;
@@ -228,23 +192,23 @@ function readMethodProperty(optionsArg) {
228
192
  }
229
193
  function isFetchCall(node) {
230
194
  const callee = node.callee;
231
- if (callee.type === import_utils3.AST_NODE_TYPES.Identifier && callee.name === "fetch") {
195
+ if (callee.type === import_utils2.AST_NODE_TYPES.Identifier && callee.name === "fetch") {
232
196
  const method = readMethodProperty(node.arguments[1]);
233
197
  if (method !== null && method !== "GET") {
234
198
  return false;
235
199
  }
236
200
  return true;
237
201
  }
238
- if (callee.type === import_utils3.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils3.AST_NODE_TYPES.Identifier && FETCH_LIBS.has(callee.object.name) && callee.property.type === import_utils3.AST_NODE_TYPES.Identifier) {
202
+ if (callee.type === import_utils2.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils2.AST_NODE_TYPES.Identifier && FETCH_LIBS.has(callee.object.name) && callee.property.type === import_utils2.AST_NODE_TYPES.Identifier) {
239
203
  return HTTP_METHOD_NAMES.has(callee.property.name);
240
204
  }
241
- if (callee.type === import_utils3.AST_NODE_TYPES.Identifier && (callee.name === "axios" || callee.name === "ky")) {
205
+ if (callee.type === import_utils2.AST_NODE_TYPES.Identifier && (callee.name === "axios" || callee.name === "ky")) {
242
206
  const firstArg = node.arguments[0];
243
207
  const secondArg = node.arguments[1];
244
208
  let configArg;
245
- if (firstArg?.type === import_utils3.AST_NODE_TYPES.ObjectExpression) {
209
+ if (firstArg?.type === import_utils2.AST_NODE_TYPES.ObjectExpression) {
246
210
  configArg = firstArg;
247
- } else if (secondArg?.type === import_utils3.AST_NODE_TYPES.ObjectExpression) {
211
+ } else if (secondArg?.type === import_utils2.AST_NODE_TYPES.ObjectExpression) {
248
212
  configArg = secondArg;
249
213
  }
250
214
  const method = readMethodProperty(configArg);
@@ -258,13 +222,13 @@ function isFetchCall(node) {
258
222
  function extractUrlString(node) {
259
223
  const firstArg = node.arguments[0];
260
224
  if (!firstArg) return "";
261
- if (firstArg.type === import_utils3.AST_NODE_TYPES.Literal && typeof firstArg.value === "string") {
225
+ if (firstArg.type === import_utils2.AST_NODE_TYPES.Literal && typeof firstArg.value === "string") {
262
226
  return firstArg.value;
263
227
  }
264
- if (firstArg.type === import_utils3.AST_NODE_TYPES.TemplateLiteral) {
228
+ if (firstArg.type === import_utils2.AST_NODE_TYPES.TemplateLiteral) {
265
229
  return firstArg.quasis.map((q) => q.value.cooked).join("");
266
230
  }
267
- if (firstArg.type === import_utils3.AST_NODE_TYPES.Identifier) {
231
+ if (firstArg.type === import_utils2.AST_NODE_TYPES.Identifier) {
268
232
  return firstArg.name;
269
233
  }
270
234
  return "";
@@ -274,7 +238,7 @@ function isAnalyticsCall(node) {
274
238
  if (url === "") return false;
275
239
  return url.split(/[/.]/).some((segment) => ANALYTICS_SEGMENTS.has(segment));
276
240
  }
277
- var no_client_side_data_fetching_default = import_utils3.ESLintUtils.RuleCreator(
241
+ var no_client_side_data_fetching_default = import_utils2.ESLintUtils.RuleCreator(
278
242
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
279
243
  )({
280
244
  name: "no-client-side-data-fetching",
@@ -312,10 +276,10 @@ var no_client_side_data_fetching_default = import_utils3.ESLintUtils.RuleCreator
312
276
  });
313
277
 
314
278
  // src/rules/no-comment-cruft.ts
315
- var import_utils5 = require("@typescript-eslint/utils");
279
+ var import_utils4 = require("@typescript-eslint/utils");
316
280
 
317
281
  // src/rules/_comments.ts
318
- var import_utils4 = require("@typescript-eslint/utils");
282
+ var import_utils3 = require("@typescript-eslint/utils");
319
283
  var REF_RE = /https?:\/\/|\bRFC[- ]?\d+|\bPEP[- ]?\d+|\bCVE-\d{4}|\b(?!UTF-|SHA-|ISO-|AES-|CRC-|MD-|PCM-|EOF-|API-|BASE-)[A-Z][A-Z0-9]{1,9}-\d[A-Z0-9]{0,5}\b|(?<![&\w])#\d{2,6}\b|@[a-z][\w.-]*\.(?:us|com|ai|io|net|org|dev)\b/;
320
284
  var VERSION_RE = /(?:>=|<=|==|<|>)\s*v?\d+\.\d+|\bv\d+\.\d+|\b(?:since|until|as of)\s+(?:v?\d+\.\d+|Python\s*\d)/i;
321
285
  var UNITS_RE = /[~<>]?\d+(?:\.\d+)?\s?(?:ms|s\b|sec\b|seconds?\b|min\b|minutes?\b|hours?\b|days?\b|KB|MB|MiB|GiB|kHz|Hz|bytes?\b|bit\b|-bit\b|%|px\b|rps\b|qps\b)|\b[1-5]xx\b|\b(?:301|302|304|307|308|400|401|403|404|405|409|410|412|422|425|429|500|501|502|503|504)\b/;
@@ -401,10 +365,10 @@ var NARRATION_MAX_WORDS = 6;
401
365
  var NARRATION_MIN_CONTENT = 1;
402
366
  var TOKEN_PLURAL_MIN = 4;
403
367
  var RESTATABLE_STATEMENTS = /* @__PURE__ */ new Set([
404
- import_utils4.AST_NODE_TYPES.ExpressionStatement,
405
- import_utils4.AST_NODE_TYPES.ReturnStatement,
406
- import_utils4.AST_NODE_TYPES.ThrowStatement,
407
- import_utils4.AST_NODE_TYPES.VariableDeclaration
368
+ import_utils3.AST_NODE_TYPES.ExpressionStatement,
369
+ import_utils3.AST_NODE_TYPES.ReturnStatement,
370
+ import_utils3.AST_NODE_TYPES.ThrowStatement,
371
+ import_utils3.AST_NODE_TYPES.VariableDeclaration
408
372
  ]);
409
373
  var NARRATION_VERB_RE = /^(?:add|append|assign|build|calculate|call|check|clear|close|compute|convert|copy|count|create|declare|decrement|define|delete|extract|fetch|filter|find|format|generate|get|handle|increment|init|initialise|initialize|insert|iterate|join|load|log|loop|make|map|merge|open|parse|print|process|push|read|remove|render|reset|return|save|send|set|setup|sort|split|start|stop|store|update|validate|wrap|write)(?:s|es|d|ed|ing)?$/i;
410
374
  var NARRATION_STOPWORDS = /* @__PURE__ */ new Set([
@@ -469,19 +433,19 @@ function headTokens(source) {
469
433
  function isTrivialInitializer(node) {
470
434
  return node.declarations.every((declarator) => {
471
435
  const init = declarator.init;
472
- if (init == null || init.type === import_utils4.AST_NODE_TYPES.Literal) return true;
473
- return init.type === import_utils4.AST_NODE_TYPES.ArrayExpression && init.elements.length === 0 || init.type === import_utils4.AST_NODE_TYPES.ObjectExpression && init.properties.length === 0;
436
+ if (init == null || init.type === import_utils3.AST_NODE_TYPES.Literal) return true;
437
+ return init.type === import_utils3.AST_NODE_TYPES.ArrayExpression && init.elements.length === 0 || init.type === import_utils3.AST_NODE_TYPES.ObjectExpression && init.properties.length === 0;
474
438
  });
475
439
  }
476
440
  function restatableStatementBelow(comment, sourceCode) {
477
441
  const token = sourceCode.getTokenAfter(comment, { includeComments: false });
478
442
  if (token === null || token.loc.start.line !== comment.loc.end.line + 1) return null;
479
- for (let node = sourceCode.getNodeByRangeIndex(token.range[0]); node != null && node.type !== import_utils4.AST_NODE_TYPES.Program; node = node.parent) {
443
+ for (let node = sourceCode.getNodeByRangeIndex(token.range[0]); node != null && node.type !== import_utils3.AST_NODE_TYPES.Program; node = node.parent) {
480
444
  if (!RESTATABLE_STATEMENTS.has(node.type)) continue;
481
445
  if (node.loc.start.line !== token.loc.start.line || node.loc.end.line !== node.loc.start.line) {
482
446
  return null;
483
447
  }
484
- if (node.type === import_utils4.AST_NODE_TYPES.VariableDeclaration && isTrivialInitializer(node)) {
448
+ if (node.type === import_utils3.AST_NODE_TYPES.VariableDeclaration && isTrivialInitializer(node)) {
485
449
  return null;
486
450
  }
487
451
  return sourceCode.getText(node);
@@ -576,7 +540,8 @@ var DUMMY_TRANSLATION_RE = /^(?:increment|return|returns|get|gets|set\b(?! up\b)
576
540
  var DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;
577
541
  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\.)/;
578
542
  var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
579
- var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
543
+ var ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$/;
544
+ var CALL_RE = /^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
580
545
  var PSEUDOCODE_RE = /%\w+%|\[opt\]|(?:^|\s)<[A-Za-z]\w*>|…|\.\.\./;
581
546
  function stripCommentMarker(line) {
582
547
  return line.replace(/^\s*\/{1,2}/, "").replace(/^\s*\*+/, "").trim();
@@ -590,11 +555,12 @@ function isBanner(text) {
590
555
  if (BANNER_FULL_RE.test(t) || isRegionMarker(t)) return true;
591
556
  return BANNER_RUN_RE.test(t) && !DIAGRAM_ARROW_RE.test(t);
592
557
  }
593
- function looksLikeCode(text) {
558
+ function looksLikeCode(text, allowCall = true) {
594
559
  const t = text.trim();
595
560
  if (!t) return false;
596
561
  if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;
597
- return CALL_OR_ASSIGN_RE.test(t);
562
+ if (ASSIGN_RE.test(t)) return true;
563
+ return allowCall && CALL_RE.test(t);
598
564
  }
599
565
  function hasPseudocode(text) {
600
566
  return PSEUDOCODE_RE.test(text);
@@ -612,20 +578,24 @@ function isProse(text) {
612
578
  }
613
579
  return false;
614
580
  }
615
- var JUSTIFICATION_RE = /\b(?:because|since|until|due to|so that|otherwise|which is why|in order to|to avoid|to work around|to prevent)\b/i;
581
+ var JUSTIFICATION_RE = /\b(?:because|since|until|due to|so that|so we|so it|so the|otherwise|which is why|in order to|to avoid|to work around|to prevent|backwards? compat(?:ibility)?|for compatibility)\b/i;
582
+ function restatesWholeStatement(body, statement) {
583
+ return statement !== null && restatesStatementHead(body, statement.replaceAll("(", " "));
584
+ }
616
585
  function isRedundantNarration(body, statementBelow, standalone, isolatedEnumeration, nested) {
617
586
  const t = body.trim();
618
587
  if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;
619
588
  if (standalone) {
620
- if (STEP_NARRATION_RE.test(t)) return true;
621
- if (META_COMMENTARY_RE.test(t) && !JUSTIFICATION_RE.test(t)) return true;
589
+ const justified = JUSTIFICATION_RE.test(t);
590
+ if (STEP_NARRATION_RE.test(t) && !justified) return true;
591
+ if (META_COMMENTARY_RE.test(t) && !justified) return true;
622
592
  if (HELPER_OPENER_RE.test(t) || LETS_RE.test(t)) return true;
623
593
  const words = t.split(/\s+/);
624
- if (words.length <= 4 && DUMMY_TRANSLATION_RE.test(t) && !/[():=]/.test(t)) {
594
+ if (words.length > 1 && words.length <= 4 && DUMMY_TRANSLATION_RE.test(t) && !/[():=]/.test(t)) {
625
595
  const lowerT = t.toLowerCase();
626
596
  const rationaleWords = ["when", "because", "if", "so that", "due to", "for", "instead of", "to prevent", "to avoid", "only"];
627
- if (!rationaleWords.some((w) => lowerT.includes(w))) {
628
- if (words.length > 1) return true;
597
+ if (!rationaleWords.some((w) => lowerT.includes(w)) && restatesWholeStatement(t, statementBelow)) {
598
+ return true;
629
599
  }
630
600
  }
631
601
  if (!nested && isSectionLabel(t)) return true;
@@ -634,13 +604,17 @@ function isRedundantNarration(body, statementBelow, standalone, isolatedEnumerat
634
604
  return restatesStatementHead(t, statementBelow);
635
605
  }
636
606
  var STATEMENT_CONTAINERS = /* @__PURE__ */ new Set([
637
- import_utils5.AST_NODE_TYPES.Program,
638
- import_utils5.AST_NODE_TYPES.BlockStatement,
639
- import_utils5.AST_NODE_TYPES.ClassBody,
640
- import_utils5.AST_NODE_TYPES.StaticBlock,
641
- import_utils5.AST_NODE_TYPES.SwitchCase,
642
- import_utils5.AST_NODE_TYPES.TSModuleBlock,
643
- import_utils5.AST_NODE_TYPES.TSInterfaceBody
607
+ import_utils4.AST_NODE_TYPES.Program,
608
+ import_utils4.AST_NODE_TYPES.BlockStatement,
609
+ import_utils4.AST_NODE_TYPES.ClassBody,
610
+ import_utils4.AST_NODE_TYPES.StaticBlock,
611
+ import_utils4.AST_NODE_TYPES.SwitchCase,
612
+ import_utils4.AST_NODE_TYPES.TSModuleBlock,
613
+ import_utils4.AST_NODE_TYPES.TSInterfaceBody
614
+ ]);
615
+ var TYPE_MEMBER_CONTAINERS = /* @__PURE__ */ new Set([
616
+ import_utils4.AST_NODE_TYPES.TSInterfaceBody,
617
+ import_utils4.AST_NODE_TYPES.TSTypeLiteral
644
618
  ]);
645
619
  function runCitesAReference(comments, index) {
646
620
  for (let i = index; i >= 0; i--) {
@@ -669,10 +643,10 @@ function hasIllustrationLeadInAbove(comments, index) {
669
643
  }
670
644
  return false;
671
645
  }
672
- function hasCommentedOutCode(texts, precedingProse) {
646
+ function hasCommentedOutCode(texts, precedingProse, allowCall) {
673
647
  for (let i = 0; i < texts.length; i++) {
674
648
  const line = texts[i];
675
- if (line === void 0 || !looksLikeCode(line) || hasPseudocode(line)) {
649
+ if (line === void 0 || !looksLikeCode(line, allowCall) || hasPseudocode(line)) {
676
650
  continue;
677
651
  }
678
652
  const prev = i > 0 ? texts[i - 1] : void 0;
@@ -681,7 +655,7 @@ function hasCommentedOutCode(texts, precedingProse) {
681
655
  }
682
656
  return false;
683
657
  }
684
- var no_comment_cruft_default = import_utils5.ESLintUtils.RuleCreator(
658
+ var no_comment_cruft_default = import_utils4.ESLintUtils.RuleCreator(
685
659
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
686
660
  )({
687
661
  name: "no-comment-cruft",
@@ -766,7 +740,9 @@ var no_comment_cruft_default = import_utils5.ESLintUtils.RuleCreator(
766
740
  }
767
741
  const prev = comments[i - 1];
768
742
  const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
769
- if (hasCommentedOutCode(texts, precedingProse) && !hasIllustrationLeadInAbove(comments, i)) {
743
+ const container = sourceCode.getNodeByRangeIndex(comment.range[0]);
744
+ const inTypeMembers = container !== null && TYPE_MEMBER_CONTAINERS.has(container.type);
745
+ if (hasCommentedOutCode(texts, precedingProse, !inTypeMembers) && !hasIllustrationLeadInAbove(comments, i)) {
770
746
  context.report({ node: comment, messageId: "commentedOutCode" });
771
747
  continue;
772
748
  }
@@ -774,7 +750,6 @@ var no_comment_cruft_default = import_utils5.ESLintUtils.RuleCreator(
774
750
  const body = texts[0];
775
751
  const statement = restatableStatementBelow(comment, sourceCode);
776
752
  const standalone = !isInsideCommentRun(comments, i);
777
- const container = sourceCode.getNodeByRangeIndex(comment.range[0]);
778
753
  const nested = container !== null && !STATEMENT_CONTAINERS.has(container.type);
779
754
  if (body !== void 0 && !runCitesAReference(comments, i) && isRedundantNarration(body, statement, standalone, enumerated.length === 1, nested)) {
780
755
  context.report({ node: comment, messageId: "redundantNarration" });
@@ -788,7 +763,7 @@ var no_comment_cruft_default = import_utils5.ESLintUtils.RuleCreator(
788
763
  });
789
764
 
790
765
  // src/rules/no-enum.ts
791
- var import_utils6 = require("@typescript-eslint/utils");
766
+ var import_utils5 = require("@typescript-eslint/utils");
792
767
  var DEFAULT_IGNORE_PATTERNS = [
793
768
  /[\\/]generated[\\/]/,
794
769
  /[\\/]openapi-gen[\\/]/,
@@ -808,7 +783,7 @@ function hasGeneratedMarker(sourceText) {
808
783
  const head = sourceText.slice(0, 1024);
809
784
  return /@generated\b/.test(head);
810
785
  }
811
- var no_enum_default = import_utils6.ESLintUtils.RuleCreator(
786
+ var no_enum_default = import_utils5.ESLintUtils.RuleCreator(
812
787
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
813
788
  )({
814
789
  name: "no-enum",
@@ -859,7 +834,7 @@ var no_enum_default = import_utils6.ESLintUtils.RuleCreator(
859
834
  });
860
835
 
861
836
  // src/rules/no-insecure-random-id.ts
862
- var import_utils7 = require("@typescript-eslint/utils");
837
+ var import_utils6 = require("@typescript-eslint/utils");
863
838
  var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
864
839
  var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
865
840
  var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
@@ -1000,7 +975,7 @@ function isConcatenatedIntoPathOrDomId(node) {
1000
975
  collectStaticStringParts(top, parts);
1001
976
  return parts.some((part) => PATH_OR_DOM_MARKER.test(part));
1002
977
  }
1003
- var no_insecure_random_id_default = import_utils7.ESLintUtils.RuleCreator(
978
+ var no_insecure_random_id_default = import_utils6.ESLintUtils.RuleCreator(
1004
979
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1005
980
  )({
1006
981
  name: "no-insecure-random-id",
@@ -1044,7 +1019,7 @@ var no_insecure_random_id_default = import_utils7.ESLintUtils.RuleCreator(
1044
1019
  });
1045
1020
 
1046
1021
  // src/rules/no-json-stringify-error.ts
1047
- var import_utils8 = require("@typescript-eslint/utils");
1022
+ var import_utils7 = require("@typescript-eslint/utils");
1048
1023
  var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
1049
1024
  var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
1050
1025
  var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
@@ -1178,7 +1153,7 @@ function isGuardedByInstanceofError(node, argExpr, sourceCode) {
1178
1153
  function isJsonStringify(callee) {
1179
1154
  return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
1180
1155
  }
1181
- var no_json_stringify_error_default = import_utils8.ESLintUtils.RuleCreator(
1156
+ var no_json_stringify_error_default = import_utils7.ESLintUtils.RuleCreator(
1182
1157
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1183
1158
  )({
1184
1159
  name: "no-json-stringify-error",
@@ -1228,10 +1203,10 @@ var no_json_stringify_error_default = import_utils8.ESLintUtils.RuleCreator(
1228
1203
  });
1229
1204
 
1230
1205
  // src/rules/no-log-only-catch.ts
1231
- var import_utils10 = require("@typescript-eslint/utils");
1206
+ var import_utils9 = require("@typescript-eslint/utils");
1232
1207
 
1233
1208
  // src/rules/_logging.ts
1234
- var import_utils9 = require("@typescript-eslint/utils");
1209
+ var import_utils8 = require("@typescript-eslint/utils");
1235
1210
  var LOG_METHODS = /* @__PURE__ */ new Set([
1236
1211
  "debug",
1237
1212
  "info",
@@ -1333,12 +1308,92 @@ function createLogMatcher(options = {}) {
1333
1308
  }
1334
1309
 
1335
1310
  // src/rules/no-log-only-catch.ts
1336
- var DEFAULT_IGNORE_PATTERNS2 = [
1337
- /\.test\./,
1338
- /\.spec\./,
1339
- /[\\/]__tests__[\\/]/
1340
- ];
1341
- var no_log_only_catch_default = import_utils10.ESLintUtils.RuleCreator(
1311
+ var BENCHMARK_DIR_RE = /(?:^|[\\/])benchmarks?[\\/]/;
1312
+ var SINGLE_STATEMENT_HOSTS = /* @__PURE__ */ new Set([
1313
+ import_utils9.AST_NODE_TYPES.DoWhileStatement,
1314
+ import_utils9.AST_NODE_TYPES.ForInStatement,
1315
+ import_utils9.AST_NODE_TYPES.ForOfStatement,
1316
+ import_utils9.AST_NODE_TYPES.ForStatement,
1317
+ import_utils9.AST_NODE_TYPES.IfStatement,
1318
+ import_utils9.AST_NODE_TYPES.WhileStatement
1319
+ ]);
1320
+ var FUNCTION_TYPES = /* @__PURE__ */ new Set([
1321
+ import_utils9.AST_NODE_TYPES.ArrowFunctionExpression,
1322
+ import_utils9.AST_NODE_TYPES.FunctionDeclaration,
1323
+ import_utils9.AST_NODE_TYPES.FunctionExpression
1324
+ ]);
1325
+ function statementSlot(node) {
1326
+ const parent = node.parent;
1327
+ if (parent === void 0) return null;
1328
+ let list;
1329
+ switch (parent.type) {
1330
+ case import_utils9.AST_NODE_TYPES.BlockStatement:
1331
+ case import_utils9.AST_NODE_TYPES.Program:
1332
+ case import_utils9.AST_NODE_TYPES.StaticBlock:
1333
+ list = parent.body;
1334
+ break;
1335
+ case import_utils9.AST_NODE_TYPES.SwitchCase:
1336
+ list = parent.consequent;
1337
+ break;
1338
+ default:
1339
+ return null;
1340
+ }
1341
+ const index = list.indexOf(node);
1342
+ return index === -1 ? null : { list, index };
1343
+ }
1344
+ function hasFollowingStatement(node) {
1345
+ for (let current = node; current !== void 0 && !FUNCTION_TYPES.has(current.type); current = current.parent) {
1346
+ const slot = statementSlot(current);
1347
+ if (slot !== null && slot.index < slot.list.length - 1) return true;
1348
+ }
1349
+ return false;
1350
+ }
1351
+ function fallbackFollowsTry(tryStatement) {
1352
+ const body = tryStatement.block.body;
1353
+ const last = body.at(-1);
1354
+ return last?.type === import_utils9.AST_NODE_TYPES.ReturnStatement && hasFollowingStatement(tryStatement);
1355
+ }
1356
+ function isSeedValue(node) {
1357
+ const inner = node.type === import_utils9.AST_NODE_TYPES.TSAsExpression ? node.expression : node;
1358
+ switch (inner.type) {
1359
+ case import_utils9.AST_NODE_TYPES.Literal:
1360
+ return true;
1361
+ case import_utils9.AST_NODE_TYPES.Identifier:
1362
+ return inner.name === "undefined";
1363
+ case import_utils9.AST_NODE_TYPES.UnaryExpression:
1364
+ return inner.argument.type === import_utils9.AST_NODE_TYPES.Literal;
1365
+ case import_utils9.AST_NODE_TYPES.ArrayExpression:
1366
+ return inner.elements.length === 0;
1367
+ case import_utils9.AST_NODE_TYPES.ObjectExpression:
1368
+ return inner.properties.length === 0;
1369
+ default:
1370
+ return false;
1371
+ }
1372
+ }
1373
+ function seededFallbackHandled(tryStatement, scope) {
1374
+ const slot = statementSlot(tryStatement);
1375
+ if (slot === null || slot.index === 0) return false;
1376
+ const previous = slot.list[slot.index - 1];
1377
+ if (previous?.type !== import_utils9.AST_NODE_TYPES.VariableDeclaration || previous.kind === "const") {
1378
+ return false;
1379
+ }
1380
+ const declarator = previous.declarations[0];
1381
+ if (previous.declarations.length !== 1 || declarator === void 0) return false;
1382
+ if (declarator.id.type !== import_utils9.AST_NODE_TYPES.Identifier) return false;
1383
+ if (declarator.init == null || !isSeedValue(declarator.init)) return false;
1384
+ const variable = import_utils9.ASTUtils.findVariable(scope, declarator.id.name);
1385
+ if (variable === null) return false;
1386
+ const [tryStart, tryEnd] = tryStatement.block.range;
1387
+ let writtenInTry = false;
1388
+ let readAfter = false;
1389
+ for (const reference of variable.references) {
1390
+ const [start] = reference.identifier.range;
1391
+ if (reference.isWrite() && start >= tryStart && start < tryEnd) writtenInTry = true;
1392
+ if (reference.isRead() && start >= tryStatement.range[1]) readAfter = true;
1393
+ }
1394
+ return writtenInTry && readAfter;
1395
+ }
1396
+ var no_log_only_catch_default = import_utils9.ESLintUtils.RuleCreator(
1342
1397
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1343
1398
  )({
1344
1399
  name: "no-log-only-catch",
@@ -1363,26 +1418,40 @@ var no_log_only_catch_default = import_utils10.ESLintUtils.RuleCreator(
1363
1418
  create(context, [loggingOptions]) {
1364
1419
  const matcher = createLogMatcher(loggingOptions);
1365
1420
  const filename = context.filename;
1421
+ const sourceCode = context.sourceCode;
1366
1422
  function isLoggingCallStatement(statement) {
1367
1423
  if (statement.type !== "ExpressionStatement") {
1368
1424
  return false;
1369
1425
  }
1370
1426
  return matcher.isLoggingCall(statement.expression);
1371
1427
  }
1372
- const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS2.some(
1373
- (re) => re.test(filename)
1374
- );
1375
- if (isIgnoredByDefault) {
1428
+ function hasCommentDirectlyAbove(node) {
1429
+ const above = sourceCode.getCommentsBefore(node).at(-1);
1430
+ return above !== void 0 && above.loc.end.line === node.loc.start.line - 1;
1431
+ }
1432
+ function hasAdjacentRationale(node) {
1433
+ const tryStatement = node.parent;
1434
+ if (hasCommentDirectlyAbove(tryStatement) || hasCommentDirectlyAbove(node)) return true;
1435
+ const block = tryStatement.parent;
1436
+ if (block?.type !== import_utils9.AST_NODE_TYPES.BlockStatement || block.body.length !== 1 || block.parent === void 0 || !SINGLE_STATEMENT_HOSTS.has(block.parent.type)) {
1437
+ return false;
1438
+ }
1439
+ return hasCommentDirectlyAbove(block.parent);
1440
+ }
1441
+ if (isTestFile(filename) || BENCHMARK_DIR_RE.test(filename.replaceAll("\\", "/"))) {
1376
1442
  return {};
1377
1443
  }
1378
1444
  return {
1379
1445
  CatchClause(node) {
1380
1446
  const statements = node.body.body;
1381
- const isDocumented = context.sourceCode.getCommentsInside(node.body).length > 0;
1447
+ const isDocumented = sourceCode.getCommentsInside(node.body).length > 0 || hasAdjacentRationale(node);
1382
1448
  if (statements.length === 0) {
1383
1449
  if (isDocumented) {
1384
1450
  return;
1385
1451
  }
1452
+ if (fallbackFollowsTry(node.parent) || seededFallbackHandled(node.parent, sourceCode.getScope(node))) {
1453
+ return;
1454
+ }
1386
1455
  context.report({ node, messageId: "emptyCatch" });
1387
1456
  return;
1388
1457
  }
@@ -1401,11 +1470,12 @@ var no_log_only_catch_default = import_utils10.ESLintUtils.RuleCreator(
1401
1470
  });
1402
1471
 
1403
1472
  // src/rules/no-raw-env.ts
1404
- var import_utils11 = require("@typescript-eslint/utils");
1473
+ var import_utils10 = require("@typescript-eslint/utils");
1405
1474
  var CONFIG_FILE_RE = /(^|[\\/])[\w.-]+\.config\.[cm]?[jt]sx?$/;
1406
1475
  var ENV_BOUNDARY_FILE_RE = /(^|[\\/])(?:env|client-env|server-env|client-settings|server-settings)\.[cm]?[jt]sx?$/;
1476
+ var ENV_VALIDATION_MARKER_RE = /\bcreateEnv\s*\(|\bz\.object\s*\(|\.(?:safeParse|parse)\s*\(/;
1407
1477
  function isValidatedEnvBoundary(filename, sourceText) {
1408
- return ENV_BOUNDARY_FILE_RE.test(filename.replaceAll("\\", "/")) && /\bz\.object\s*\(/.test(sourceText) && /\.parse\s*\(/.test(sourceText);
1478
+ return ENV_BOUNDARY_FILE_RE.test(filename.replaceAll("\\", "/")) && ENV_VALIDATION_MARKER_RE.test(sourceText);
1409
1479
  }
1410
1480
  function isProcessEnv(node) {
1411
1481
  return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
@@ -1420,9 +1490,15 @@ var BUILD_TIME_CONSTANTS = /* @__PURE__ */ new Set([
1420
1490
  "PROD",
1421
1491
  "SSR"
1422
1492
  ]);
1423
- function isBuildTimeConstantAccess(node) {
1493
+ var PLATFORM_MARKERS = /* @__PURE__ */ new Set([
1494
+ "NEXT_RUNTIME",
1495
+ "VERCEL",
1496
+ "VERCEL_ENV",
1497
+ "CI"
1498
+ ]);
1499
+ function isExemptVariableAccess(node) {
1424
1500
  const parent = node.parent;
1425
- return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
1501
+ return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && (BUILD_TIME_CONSTANTS.has(parent.property.name) || PLATFORM_MARKERS.has(parent.property.name));
1426
1502
  }
1427
1503
  function isWriteTarget(node) {
1428
1504
  const access = node.parent.type === "MemberExpression" && node.parent.object === node ? node.parent : node;
@@ -1439,7 +1515,7 @@ function isWholeEnvSpread(node) {
1439
1515
  const parent = node.parent;
1440
1516
  return parent.type === "SpreadElement" && parent.argument === node;
1441
1517
  }
1442
- var no_raw_env_default = import_utils11.ESLintUtils.RuleCreator(
1518
+ var no_raw_env_default = import_utils10.ESLintUtils.RuleCreator(
1443
1519
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1444
1520
  )({
1445
1521
  name: "no-raw-env",
@@ -1461,7 +1537,7 @@ var no_raw_env_default = import_utils11.ESLintUtils.RuleCreator(
1461
1537
  }
1462
1538
  return {
1463
1539
  MemberExpression(node) {
1464
- if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node) && !isWriteTarget(node) && !isWholeEnvSpread(node)) {
1540
+ if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isExemptVariableAccess(node) && !isWriteTarget(node) && !isWholeEnvSpread(node)) {
1465
1541
  context.report({
1466
1542
  node,
1467
1543
  messageId: "noRawEnv"
@@ -1473,12 +1549,12 @@ var no_raw_env_default = import_utils11.ESLintUtils.RuleCreator(
1473
1549
  });
1474
1550
 
1475
1551
  // src/rules/no-sentinel-return-on-catch.ts
1476
- var import_utils12 = require("@typescript-eslint/utils");
1552
+ var import_utils11 = require("@typescript-eslint/utils");
1477
1553
  function sentinelKind(arg) {
1478
1554
  if (arg === null) {
1479
1555
  return null;
1480
1556
  }
1481
- if (arg.type === import_utils12.AST_NODE_TYPES.Literal) {
1557
+ if (arg.type === import_utils11.AST_NODE_TYPES.Literal) {
1482
1558
  if (arg.value === null) {
1483
1559
  return "nullish";
1484
1560
  }
@@ -1490,13 +1566,13 @@ function sentinelKind(arg) {
1490
1566
  }
1491
1567
  return null;
1492
1568
  }
1493
- if (arg.type === import_utils12.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
1569
+ if (arg.type === import_utils11.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
1494
1570
  return "nullish";
1495
1571
  }
1496
- if (arg.type === import_utils12.AST_NODE_TYPES.ArrayExpression) {
1572
+ if (arg.type === import_utils11.AST_NODE_TYPES.ArrayExpression) {
1497
1573
  return "array";
1498
1574
  }
1499
- if (arg.type === import_utils12.AST_NODE_TYPES.ObjectExpression) {
1575
+ if (arg.type === import_utils11.AST_NODE_TYPES.ObjectExpression) {
1500
1576
  return "object";
1501
1577
  }
1502
1578
  return null;
@@ -1505,25 +1581,25 @@ function isSentinelArgument(arg) {
1505
1581
  if (arg === null) {
1506
1582
  return false;
1507
1583
  }
1508
- if (arg.type === import_utils12.AST_NODE_TYPES.Literal && arg.value === null) {
1584
+ if (arg.type === import_utils11.AST_NODE_TYPES.Literal && arg.value === null) {
1509
1585
  return true;
1510
1586
  }
1511
- if (arg.type === import_utils12.AST_NODE_TYPES.Literal && arg.value === false) {
1587
+ if (arg.type === import_utils11.AST_NODE_TYPES.Literal && arg.value === false) {
1512
1588
  return true;
1513
1589
  }
1514
- if (arg.type === import_utils12.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
1590
+ if (arg.type === import_utils11.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
1515
1591
  return true;
1516
1592
  }
1517
- if (arg.type === import_utils12.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
1593
+ if (arg.type === import_utils11.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
1518
1594
  return true;
1519
1595
  }
1520
- if (arg.type === import_utils12.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
1596
+ if (arg.type === import_utils11.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
1521
1597
  return true;
1522
1598
  }
1523
1599
  return false;
1524
1600
  }
1525
1601
  function isFunctionNode(node) {
1526
- return node.type === import_utils12.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils12.AST_NODE_TYPES.FunctionExpression || node.type === import_utils12.AST_NODE_TYPES.ArrowFunctionExpression;
1602
+ return node.type === import_utils11.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils11.AST_NODE_TYPES.FunctionExpression || node.type === import_utils11.AST_NODE_TYPES.ArrowFunctionExpression;
1527
1603
  }
1528
1604
  function isNode(value) {
1529
1605
  return typeof value === "object" && value !== null && typeof value.type === "string";
@@ -1563,24 +1639,24 @@ function walkWithinScope(node, visit) {
1563
1639
  function containsThrow(node) {
1564
1640
  return walkWithinScope(
1565
1641
  node,
1566
- (current) => current.type === import_utils12.AST_NODE_TYPES.ThrowStatement
1642
+ (current) => current.type === import_utils11.AST_NODE_TYPES.ThrowStatement
1567
1643
  );
1568
1644
  }
1569
1645
  function bindsName(param, name) {
1570
1646
  switch (param.type) {
1571
- case import_utils12.AST_NODE_TYPES.Identifier:
1647
+ case import_utils11.AST_NODE_TYPES.Identifier:
1572
1648
  return param.name === name;
1573
- case import_utils12.AST_NODE_TYPES.AssignmentPattern:
1649
+ case import_utils11.AST_NODE_TYPES.AssignmentPattern:
1574
1650
  return bindsName(param.left, name);
1575
- case import_utils12.AST_NODE_TYPES.RestElement:
1651
+ case import_utils11.AST_NODE_TYPES.RestElement:
1576
1652
  return bindsName(param.argument, name);
1577
- case import_utils12.AST_NODE_TYPES.ArrayPattern:
1653
+ case import_utils11.AST_NODE_TYPES.ArrayPattern:
1578
1654
  return param.elements.some(
1579
1655
  (element) => element !== null && bindsName(element, name)
1580
1656
  );
1581
- case import_utils12.AST_NODE_TYPES.ObjectPattern:
1657
+ case import_utils11.AST_NODE_TYPES.ObjectPattern:
1582
1658
  return param.properties.some(
1583
- (property) => property.type === import_utils12.AST_NODE_TYPES.RestElement ? bindsName(property.argument, name) : bindsName(property.value, name)
1659
+ (property) => property.type === import_utils11.AST_NODE_TYPES.RestElement ? bindsName(property.argument, name) : bindsName(property.value, name)
1584
1660
  );
1585
1661
  default:
1586
1662
  return false;
@@ -1595,7 +1671,7 @@ function subtreeReadsName(node, name) {
1595
1671
  if (found) {
1596
1672
  return;
1597
1673
  }
1598
- if (current.type === import_utils12.AST_NODE_TYPES.Identifier && current.name === name) {
1674
+ if (current.type === import_utils11.AST_NODE_TYPES.Identifier && current.name === name) {
1599
1675
  found = true;
1600
1676
  return;
1601
1677
  }
@@ -1606,10 +1682,10 @@ function subtreeReadsName(node, name) {
1606
1682
  if (key === "parent") {
1607
1683
  continue;
1608
1684
  }
1609
- if (key === "key" && current.type === import_utils12.AST_NODE_TYPES.Property && !current.computed) {
1685
+ if (key === "key" && current.type === import_utils11.AST_NODE_TYPES.Property && !current.computed) {
1610
1686
  continue;
1611
1687
  }
1612
- if (key === "property" && current.type === import_utils12.AST_NODE_TYPES.MemberExpression && !current.computed) {
1688
+ if (key === "property" && current.type === import_utils11.AST_NODE_TYPES.MemberExpression && !current.computed) {
1613
1689
  continue;
1614
1690
  }
1615
1691
  const value = current[key];
@@ -1642,10 +1718,10 @@ var SAFE_PARSE_CONSTRUCTORS = /* @__PURE__ */ new Set([
1642
1718
  "URLPattern"
1643
1719
  ]);
1644
1720
  function isParseShapedNode(node) {
1645
- if (node.type === import_utils12.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils12.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils12.AST_NODE_TYPES.Identifier) {
1721
+ if (node.type === import_utils11.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils11.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils11.AST_NODE_TYPES.Identifier) {
1646
1722
  return node.callee.property.name === "parse";
1647
1723
  }
1648
- if (node.type === import_utils12.AST_NODE_TYPES.NewExpression && node.callee.type === import_utils12.AST_NODE_TYPES.Identifier) {
1724
+ if (node.type === import_utils11.AST_NODE_TYPES.NewExpression && node.callee.type === import_utils11.AST_NODE_TYPES.Identifier) {
1649
1725
  return SAFE_PARSE_CONSTRUCTORS.has(node.callee.name);
1650
1726
  }
1651
1727
  return false;
@@ -1656,10 +1732,10 @@ var BODY_DECODE_METHODS = /* @__PURE__ */ new Set([
1656
1732
  "arrayBuffer"
1657
1733
  ]);
1658
1734
  function isBodyDecodeNode(node) {
1659
- return node.type === import_utils12.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils12.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils12.AST_NODE_TYPES.Identifier && BODY_DECODE_METHODS.has(node.callee.property.name);
1735
+ return node.type === import_utils11.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils11.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils11.AST_NODE_TYPES.Identifier && BODY_DECODE_METHODS.has(node.callee.property.name);
1660
1736
  }
1661
1737
  function returnsMatching(stmt, predicate) {
1662
- return stmt.type === import_utils12.AST_NODE_TYPES.ReturnStatement && stmt.argument !== null && walkWithinScope(stmt.argument, predicate);
1738
+ return stmt.type === import_utils11.AST_NODE_TYPES.ReturnStatement && stmt.argument !== null && walkWithinScope(stmt.argument, predicate);
1663
1739
  }
1664
1740
  function enclosingReturnTypeNode(node) {
1665
1741
  let current = node.parent;
@@ -1677,11 +1753,11 @@ function enclosingFunctionName(node) {
1677
1753
  let current = node.parent;
1678
1754
  while (current !== void 0 && current !== null) {
1679
1755
  if (isFunctionNode(current)) {
1680
- if ("id" in current && isNode(current.id) && current.id.type === import_utils12.AST_NODE_TYPES.Identifier) {
1756
+ if ("id" in current && isNode(current.id) && current.id.type === import_utils11.AST_NODE_TYPES.Identifier) {
1681
1757
  return current.id.name;
1682
1758
  }
1683
1759
  const parent = current.parent;
1684
- if (parent?.type === import_utils12.AST_NODE_TYPES.VariableDeclarator && parent.id.type === import_utils12.AST_NODE_TYPES.Identifier) {
1760
+ if (parent?.type === import_utils11.AST_NODE_TYPES.VariableDeclarator && parent.id.type === import_utils11.AST_NODE_TYPES.Identifier) {
1685
1761
  return parent.id.name;
1686
1762
  }
1687
1763
  return null;
@@ -1702,10 +1778,10 @@ function isDeclaredBooleanPredicate(catchNode, kind) {
1702
1778
  return false;
1703
1779
  }
1704
1780
  let declared = enclosingReturnTypeNode(catchNode);
1705
- if (declared?.type === import_utils12.AST_NODE_TYPES.TSTypeReference && declared.typeName.type === import_utils12.AST_NODE_TYPES.Identifier && declared.typeName.name === "Promise") {
1781
+ if (declared?.type === import_utils11.AST_NODE_TYPES.TSTypeReference && declared.typeName.type === import_utils11.AST_NODE_TYPES.Identifier && declared.typeName.name === "Promise") {
1706
1782
  declared = declared.typeArguments?.params[0] ?? null;
1707
1783
  }
1708
- return declared?.type === import_utils12.AST_NODE_TYPES.TSBooleanKeyword;
1784
+ return declared?.type === import_utils11.AST_NODE_TYPES.TSBooleanKeyword;
1709
1785
  }
1710
1786
  function tryReturnsSafeParse(catchNode) {
1711
1787
  const tryBlock = tryBlockOf(catchNode);
@@ -1721,7 +1797,7 @@ function tryReturnsSafeParse(catchNode) {
1721
1797
  function enclosingFunctionBody(node) {
1722
1798
  let current = node.parent;
1723
1799
  while (current !== void 0 && current !== null) {
1724
- if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === import_utils12.AST_NODE_TYPES.BlockStatement) {
1800
+ if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === import_utils11.AST_NODE_TYPES.BlockStatement) {
1725
1801
  return current.body;
1726
1802
  }
1727
1803
  current = current.parent;
@@ -1734,7 +1810,7 @@ function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
1734
1810
  return false;
1735
1811
  }
1736
1812
  return walkWithinScope(functionBody, (current) => {
1737
- if (current.type !== import_utils12.AST_NODE_TYPES.ReturnStatement) {
1813
+ if (current.type !== import_utils11.AST_NODE_TYPES.ReturnStatement) {
1738
1814
  return false;
1739
1815
  }
1740
1816
  if (isWithin(current, catchNode.body)) {
@@ -1753,13 +1829,13 @@ function returnedSentinelKinds(arg) {
1753
1829
  kinds.add(direct);
1754
1830
  return kinds;
1755
1831
  }
1756
- if (arg.type === import_utils12.AST_NODE_TYPES.ConditionalExpression) {
1832
+ if (arg.type === import_utils11.AST_NODE_TYPES.ConditionalExpression) {
1757
1833
  for (const branch of [arg.consequent, arg.alternate]) {
1758
1834
  for (const nested of returnedSentinelKinds(branch)) {
1759
1835
  kinds.add(nested);
1760
1836
  }
1761
1837
  }
1762
- } else if (arg.type === import_utils12.AST_NODE_TYPES.LogicalExpression && (arg.operator === "??" || arg.operator === "||")) {
1838
+ } else if (arg.type === import_utils11.AST_NODE_TYPES.LogicalExpression && (arg.operator === "??" || arg.operator === "||")) {
1763
1839
  for (const nested of returnedSentinelKinds(arg.right)) {
1764
1840
  kinds.add(nested);
1765
1841
  }
@@ -1776,7 +1852,7 @@ function isWithin(node, ancestor) {
1776
1852
  }
1777
1853
  return false;
1778
1854
  }
1779
- var no_sentinel_return_on_catch_default = import_utils12.ESLintUtils.RuleCreator(
1855
+ var no_sentinel_return_on_catch_default = import_utils11.ESLintUtils.RuleCreator(
1780
1856
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1781
1857
  )({
1782
1858
  name: "no-sentinel-return-on-catch",
@@ -1804,7 +1880,7 @@ var no_sentinel_return_on_catch_default = import_utils12.ESLintUtils.RuleCreator
1804
1880
  const matcher = createLogMatcher(loggingOptions);
1805
1881
  function logsOrReportsError(catchBody, caughtName) {
1806
1882
  return walkWithinScope(catchBody, (current) => {
1807
- if (current.type !== import_utils12.AST_NODE_TYPES.CallExpression) {
1883
+ if (current.type !== import_utils11.AST_NODE_TYPES.CallExpression) {
1808
1884
  return false;
1809
1885
  }
1810
1886
  if (matcher.isLoggingCall(current)) {
@@ -1821,7 +1897,7 @@ var no_sentinel_return_on_catch_default = import_utils12.ESLintUtils.RuleCreator
1821
1897
  return;
1822
1898
  }
1823
1899
  const last = body[body.length - 1];
1824
- if (last === void 0 || last.type !== import_utils12.AST_NODE_TYPES.ReturnStatement) {
1900
+ if (last === void 0 || last.type !== import_utils11.AST_NODE_TYPES.ReturnStatement) {
1825
1901
  return;
1826
1902
  }
1827
1903
  if (!isSentinelArgument(last.argument)) {
@@ -1830,7 +1906,7 @@ var no_sentinel_return_on_catch_default = import_utils12.ESLintUtils.RuleCreator
1830
1906
  if (containsThrow(node.body)) {
1831
1907
  return;
1832
1908
  }
1833
- const caughtName = node.param?.type === import_utils12.AST_NODE_TYPES.Identifier ? node.param.name : null;
1909
+ const caughtName = node.param?.type === import_utils11.AST_NODE_TYPES.Identifier ? node.param.name : null;
1834
1910
  if (logsOrReportsError(node.body, caughtName)) {
1835
1911
  return;
1836
1912
  }
@@ -1857,7 +1933,7 @@ var no_sentinel_return_on_catch_default = import_utils12.ESLintUtils.RuleCreator
1857
1933
  });
1858
1934
 
1859
1935
  // src/rules/no-string-concat-in-loop.ts
1860
- var import_utils13 = require("@typescript-eslint/utils");
1936
+ var import_utils12 = require("@typescript-eslint/utils");
1861
1937
  var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
1862
1938
  "ForStatement",
1863
1939
  "ForOfStatement",
@@ -1942,7 +2018,7 @@ function enclosingLoop(node) {
1942
2018
  }
1943
2019
  return null;
1944
2020
  }
1945
- var no_string_concat_in_loop_default = import_utils13.ESLintUtils.RuleCreator(
2021
+ var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
1946
2022
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
1947
2023
  )({
1948
2024
  name: "no-string-concat-in-loop",
@@ -2005,7 +2081,7 @@ var no_string_concat_in_loop_default = import_utils13.ESLintUtils.RuleCreator(
2005
2081
  });
2006
2082
 
2007
2083
  // src/rules/no-unnecessary-use-client.ts
2008
- var import_utils14 = require("@typescript-eslint/utils");
2084
+ var import_utils13 = require("@typescript-eslint/utils");
2009
2085
  var HOOK_REGEX = /^use([A-Z]|$)/;
2010
2086
  var EVENT_PROP_REGEX = /^on[A-Z]/;
2011
2087
  var ERROR_FILE_REGEX = /\b(?:global-)?error\.[jt]sx?$/;
@@ -2031,13 +2107,13 @@ var CLIENT_ONLY_PACKAGES_REGEX = /^(?:@radix-ui\/|framer-motion|react-dom|react-
2031
2107
  var isBareSpecifier = (source) => !source.startsWith(".") && !source.startsWith("/") && !source.startsWith("@/") && !source.startsWith("~");
2032
2108
  var jsxRootName = (name) => {
2033
2109
  let current = name;
2034
- while (current.type === import_utils14.AST_NODE_TYPES.JSXMemberExpression) {
2110
+ while (current.type === import_utils13.AST_NODE_TYPES.JSXMemberExpression) {
2035
2111
  current = current.object;
2036
2112
  }
2037
- return current.type === import_utils14.AST_NODE_TYPES.JSXIdentifier ? current.name : "";
2113
+ return current.type === import_utils13.AST_NODE_TYPES.JSXIdentifier ? current.name : "";
2038
2114
  };
2039
2115
  var subtreeReadsImportedBinding = (node, imported) => {
2040
- if (node.type === import_utils14.AST_NODE_TYPES.Identifier) {
2116
+ if (node.type === import_utils13.AST_NODE_TYPES.Identifier) {
2041
2117
  return imported.has(node.name);
2042
2118
  }
2043
2119
  for (const key of Object.keys(node)) {
@@ -2052,16 +2128,16 @@ var subtreeReadsImportedBinding = (node, imported) => {
2052
2128
  return false;
2053
2129
  };
2054
2130
  var isUseClientDirective = (node) => {
2055
- return node.type === import_utils14.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils14.AST_NODE_TYPES.Literal && node.expression.value === "use client";
2131
+ return node.type === import_utils13.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils13.AST_NODE_TYPES.Literal && node.expression.value === "use client";
2056
2132
  };
2057
2133
  var isGlobalReference = (node, context) => {
2058
2134
  if (!BROWSER_GLOBALS.has(node.name)) return false;
2059
2135
  const parent = node.parent;
2060
2136
  if (parent !== void 0) {
2061
- if (parent.type === import_utils14.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
2137
+ if (parent.type === import_utils13.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
2062
2138
  return false;
2063
2139
  }
2064
- if (parent.type === import_utils14.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
2140
+ if (parent.type === import_utils13.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
2065
2141
  return false;
2066
2142
  }
2067
2143
  if (parent.type.startsWith("TS")) {
@@ -2078,7 +2154,7 @@ var isGlobalReference = (node, context) => {
2078
2154
  }
2079
2155
  return true;
2080
2156
  };
2081
- var no_unnecessary_use_client_default = import_utils14.ESLintUtils.RuleCreator(
2157
+ var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
2082
2158
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2083
2159
  )({
2084
2160
  name: "no-unnecessary-use-client",
@@ -2103,13 +2179,13 @@ var no_unnecessary_use_client_default = import_utils14.ESLintUtils.RuleCreator(
2103
2179
  const importedLocals = /* @__PURE__ */ new Set();
2104
2180
  const externalLocals = /* @__PURE__ */ new Set();
2105
2181
  const markIfHookOrContext = (callee) => {
2106
- if (callee.type === import_utils14.AST_NODE_TYPES.Identifier) {
2182
+ if (callee.type === import_utils13.AST_NODE_TYPES.Identifier) {
2107
2183
  if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
2108
2184
  hasClientIndicator = true;
2109
2185
  }
2110
2186
  return;
2111
2187
  }
2112
- if (callee.type === import_utils14.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils14.AST_NODE_TYPES.Identifier) {
2188
+ if (callee.type === import_utils13.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils13.AST_NODE_TYPES.Identifier) {
2113
2189
  const name = callee.property.name;
2114
2190
  if (HOOK_REGEX.test(name) || name === "createContext") {
2115
2191
  hasClientIndicator = true;
@@ -2119,7 +2195,7 @@ var no_unnecessary_use_client_default = import_utils14.ESLintUtils.RuleCreator(
2119
2195
  return {
2120
2196
  Program(node) {
2121
2197
  for (const stmt of node.body) {
2122
- if (stmt.type !== import_utils14.AST_NODE_TYPES.ExpressionStatement) break;
2198
+ if (stmt.type !== import_utils13.AST_NODE_TYPES.ExpressionStatement) break;
2123
2199
  if (isUseClientDirective(stmt)) {
2124
2200
  directiveNode = stmt;
2125
2201
  break;
@@ -2132,7 +2208,7 @@ var no_unnecessary_use_client_default = import_utils14.ESLintUtils.RuleCreator(
2132
2208
  },
2133
2209
  JSXAttribute(node) {
2134
2210
  if (directiveNode === null) return;
2135
- if (node.name.type === import_utils14.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
2211
+ if (node.name.type === import_utils13.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
2136
2212
  hasClientIndicator = true;
2137
2213
  }
2138
2214
  },
@@ -2199,8 +2275,8 @@ var no_unnecessary_use_client_default = import_utils14.ESLintUtils.RuleCreator(
2199
2275
  });
2200
2276
 
2201
2277
  // src/rules/prefer-discriminated-union.ts
2278
+ var import_utils14 = require("@typescript-eslint/utils");
2202
2279
  var import_utils15 = require("@typescript-eslint/utils");
2203
- var import_utils16 = require("@typescript-eslint/utils");
2204
2280
  var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
2205
2281
  "success",
2206
2282
  "ok",
@@ -2210,27 +2286,27 @@ var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
2210
2286
  ]);
2211
2287
  var MIN_OPTIONAL_MEMBERS = 2;
2212
2288
  function getMemberName(member) {
2213
- if (member.type !== import_utils16.AST_NODE_TYPES.TSPropertySignature) {
2289
+ if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
2214
2290
  return null;
2215
2291
  }
2216
2292
  const { key } = member;
2217
- if (key.type === import_utils16.AST_NODE_TYPES.Identifier) {
2293
+ if (key.type === import_utils15.AST_NODE_TYPES.Identifier) {
2218
2294
  return key.name;
2219
2295
  }
2220
- if (key.type === import_utils16.AST_NODE_TYPES.Literal && typeof key.value === "string") {
2296
+ if (key.type === import_utils15.AST_NODE_TYPES.Literal && typeof key.value === "string") {
2221
2297
  return key.value;
2222
2298
  }
2223
2299
  return null;
2224
2300
  }
2225
2301
  function isBooleanTyped(member) {
2226
- return member.typeAnnotation?.typeAnnotation.type === import_utils16.AST_NODE_TYPES.TSBooleanKeyword;
2302
+ return member.typeAnnotation?.typeAnnotation.type === import_utils15.AST_NODE_TYPES.TSBooleanKeyword;
2227
2303
  }
2228
2304
  function looksLikeMutuallyExclusiveState(typeLiteral) {
2229
2305
  let hasStatusBoolean = false;
2230
2306
  let optionalCount = 0;
2231
2307
  let optionalPayloadCount = 0;
2232
2308
  for (const member of typeLiteral.members) {
2233
- if (member.type !== import_utils16.AST_NODE_TYPES.TSPropertySignature) {
2309
+ if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
2234
2310
  continue;
2235
2311
  }
2236
2312
  if (member.optional) {
@@ -2246,7 +2322,7 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
2246
2322
  }
2247
2323
  return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS && optionalPayloadCount >= 1;
2248
2324
  }
2249
- var prefer_discriminated_union_default = import_utils15.ESLintUtils.RuleCreator(
2325
+ var prefer_discriminated_union_default = import_utils14.ESLintUtils.RuleCreator(
2250
2326
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2251
2327
  )({
2252
2328
  name: "prefer-discriminated-union",
@@ -2274,7 +2350,7 @@ var prefer_discriminated_union_default = import_utils15.ESLintUtils.RuleCreator(
2274
2350
  TSInterfaceDeclaration(node) {
2275
2351
  const synthetic = {
2276
2352
  ...node.body,
2277
- type: import_utils16.AST_NODE_TYPES.TSTypeLiteral,
2353
+ type: import_utils15.AST_NODE_TYPES.TSTypeLiteral,
2278
2354
  members: node.body.body
2279
2355
  };
2280
2356
  checkTypeLiteral(synthetic, node);
@@ -2287,13 +2363,13 @@ var prefer_discriminated_union_default = import_utils15.ESLintUtils.RuleCreator(
2287
2363
  });
2288
2364
 
2289
2365
  // src/rules/prefer-schema-for-api-payload.ts
2290
- var import_utils17 = require("@typescript-eslint/utils");
2366
+ var import_utils16 = require("@typescript-eslint/utils");
2291
2367
  var unwrap = (node) => {
2292
2368
  let current = node;
2293
2369
  while (current !== null && current !== void 0) {
2294
- if (current.type === import_utils17.AST_NODE_TYPES.TSAsExpression || current.type === import_utils17.AST_NODE_TYPES.TSTypeAssertion || current.type === import_utils17.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils17.AST_NODE_TYPES.TSSatisfiesExpression) {
2370
+ 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) {
2295
2371
  current = current.expression;
2296
- } else if (current.type === import_utils17.AST_NODE_TYPES.ChainExpression) {
2372
+ } else if (current.type === import_utils16.AST_NODE_TYPES.ChainExpression) {
2297
2373
  current = current.expression;
2298
2374
  } else {
2299
2375
  break;
@@ -2301,28 +2377,40 @@ var unwrap = (node) => {
2301
2377
  }
2302
2378
  return current ?? null;
2303
2379
  };
2380
+ var PROMISE_CHAIN_METHODS = /* @__PURE__ */ new Set([
2381
+ "then",
2382
+ "catch",
2383
+ "finally"
2384
+ ]);
2385
+ var isSchemaParseReference = (node) => {
2386
+ const inner = unwrap(node);
2387
+ return inner !== null && inner.type === import_utils16.AST_NODE_TYPES.MemberExpression && !inner.computed && inner.property.type === import_utils16.AST_NODE_TYPES.Identifier && (inner.property.name === "parse" || inner.property.name === "safeParse");
2388
+ };
2304
2389
  var isRawPayloadSource = (node) => {
2305
2390
  let current = unwrap(node);
2306
2391
  if (current === null) return false;
2307
- if (current.type === import_utils17.AST_NODE_TYPES.AwaitExpression) {
2392
+ if (current.type === import_utils16.AST_NODE_TYPES.AwaitExpression) {
2308
2393
  current = unwrap(current.argument);
2309
2394
  }
2310
- if (current === null || current.type !== import_utils17.AST_NODE_TYPES.CallExpression) {
2395
+ if (current === null || current.type !== import_utils16.AST_NODE_TYPES.CallExpression) {
2311
2396
  return false;
2312
2397
  }
2313
2398
  const callee = unwrap(current.callee);
2314
- if (callee === null || callee.type !== import_utils17.AST_NODE_TYPES.MemberExpression) {
2399
+ if (callee === null || callee.type !== import_utils16.AST_NODE_TYPES.MemberExpression) {
2315
2400
  return false;
2316
2401
  }
2317
2402
  const property = unwrap(callee.property);
2318
- if (property === null || property.type !== import_utils17.AST_NODE_TYPES.Identifier) {
2403
+ if (property === null || property.type !== import_utils16.AST_NODE_TYPES.Identifier) {
2319
2404
  return false;
2320
2405
  }
2321
2406
  if (property.name === "json") {
2322
2407
  return true;
2323
2408
  }
2409
+ if (PROMISE_CHAIN_METHODS.has(property.name)) {
2410
+ return !current.arguments.some(isSchemaParseReference) && isRawPayloadSource(callee.object);
2411
+ }
2324
2412
  const object = unwrap(callee.object);
2325
- return property.name === "parse" && object !== null && object.type === import_utils17.AST_NODE_TYPES.Identifier && object.name === "JSON" && // ...but not `JSON.parse(readFileSync(p, "utf8"))` — see isLocalFileRead.
2413
+ return property.name === "parse" && object !== null && object.type === import_utils16.AST_NODE_TYPES.Identifier && object.name === "JSON" && // ...but not `JSON.parse(readFileSync(p, "utf8"))` — see isLocalFileRead.
2326
2414
  !isLocalFileRead(current.arguments[0]);
2327
2415
  };
2328
2416
  var FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;
@@ -2330,9 +2418,9 @@ var isLocalFileRead = (node) => {
2330
2418
  let found = false;
2331
2419
  const visit = (current) => {
2332
2420
  if (found || current === null || current === void 0) return;
2333
- if (current.type === import_utils17.AST_NODE_TYPES.CallExpression) {
2421
+ if (current.type === import_utils16.AST_NODE_TYPES.CallExpression) {
2334
2422
  const callee = unwrap(current.callee);
2335
- const name = callee?.type === import_utils17.AST_NODE_TYPES.Identifier ? callee.name : callee?.type === import_utils17.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils17.AST_NODE_TYPES.Identifier ? callee.property.name : null;
2423
+ const name = callee?.type === import_utils16.AST_NODE_TYPES.Identifier ? callee.name : callee?.type === import_utils16.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils16.AST_NODE_TYPES.Identifier ? callee.property.name : null;
2336
2424
  if (name !== null && FILE_READ_RE.test(name)) {
2337
2425
  found = true;
2338
2426
  return;
@@ -2354,15 +2442,15 @@ var isLocalFileRead = (node) => {
2354
2442
  var ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;
2355
2443
  var isInsideAssertion = (node) => {
2356
2444
  for (let current = node.parent; current !== void 0 && current !== null; current = current.parent) {
2357
- if (current.type !== import_utils17.AST_NODE_TYPES.CallExpression) continue;
2445
+ if (current.type !== import_utils16.AST_NODE_TYPES.CallExpression) continue;
2358
2446
  let callee = current.callee;
2359
- while (callee.type === import_utils17.AST_NODE_TYPES.MemberExpression) {
2447
+ while (callee.type === import_utils16.AST_NODE_TYPES.MemberExpression) {
2360
2448
  callee = callee.object;
2361
2449
  }
2362
- if (callee.type === import_utils17.AST_NODE_TYPES.CallExpression) {
2450
+ if (callee.type === import_utils16.AST_NODE_TYPES.CallExpression) {
2363
2451
  callee = callee.callee;
2364
2452
  }
2365
- if (callee.type === import_utils17.AST_NODE_TYPES.Identifier && ASSERTION_CALLEE_RE.test(callee.name)) {
2453
+ if (callee.type === import_utils16.AST_NODE_TYPES.Identifier && ASSERTION_CALLEE_RE.test(callee.name)) {
2366
2454
  return true;
2367
2455
  }
2368
2456
  }
@@ -2377,23 +2465,43 @@ var findVariable2 = (scope, name) => {
2377
2465
  }
2378
2466
  return null;
2379
2467
  };
2380
- var GUARD_NAME_RE = /^is[A-Z]/;
2468
+ var GUARD_NAME_RE = /^(?:is|validate|parse|assert|decode|coerce)[A-Z]/;
2469
+ var isValidationRead = (node) => {
2470
+ let current = node;
2471
+ let parent = current.parent;
2472
+ while (parent !== null && parent !== void 0 && (parent.type === import_utils16.AST_NODE_TYPES.TSAsExpression || parent.type === import_utils16.AST_NODE_TYPES.TSTypeAssertion || parent.type === import_utils16.AST_NODE_TYPES.TSNonNullExpression || parent.type === import_utils16.AST_NODE_TYPES.TSSatisfiesExpression || parent.type === import_utils16.AST_NODE_TYPES.ChainExpression)) {
2473
+ current = parent;
2474
+ parent = parent.parent;
2475
+ }
2476
+ if (parent === null || parent === void 0) return false;
2477
+ if (parent.type === import_utils16.AST_NODE_TYPES.UnaryExpression && parent.operator === "typeof" && parent.argument === current) {
2478
+ return true;
2479
+ }
2480
+ if (parent.type !== import_utils16.AST_NODE_TYPES.CallExpression || !parent.arguments.some((arg) => arg === current)) {
2481
+ return false;
2482
+ }
2483
+ const callee = parent.callee;
2484
+ if (callee.type === import_utils16.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils16.AST_NODE_TYPES.Identifier && callee.object.name === "Array" && callee.property.type === import_utils16.AST_NODE_TYPES.Identifier && callee.property.name === "isArray") {
2485
+ return parent.arguments.length === 1;
2486
+ }
2487
+ return callee.type === import_utils16.AST_NODE_TYPES.Identifier && GUARD_NAME_RE.test(callee.name);
2488
+ };
2381
2489
  var isGuardTestPosition = (node) => {
2382
2490
  let current = node;
2383
2491
  let parent = current.parent;
2384
2492
  while (parent !== void 0 && parent !== null) {
2385
2493
  switch (parent.type) {
2386
- case import_utils17.AST_NODE_TYPES.UnaryExpression:
2387
- case import_utils17.AST_NODE_TYPES.LogicalExpression:
2388
- case import_utils17.AST_NODE_TYPES.ChainExpression:
2494
+ case import_utils16.AST_NODE_TYPES.UnaryExpression:
2495
+ case import_utils16.AST_NODE_TYPES.LogicalExpression:
2496
+ case import_utils16.AST_NODE_TYPES.ChainExpression:
2389
2497
  current = parent;
2390
2498
  parent = parent.parent;
2391
2499
  continue;
2392
- case import_utils17.AST_NODE_TYPES.IfStatement:
2393
- case import_utils17.AST_NODE_TYPES.ConditionalExpression:
2394
- case import_utils17.AST_NODE_TYPES.WhileStatement:
2395
- case import_utils17.AST_NODE_TYPES.DoWhileStatement:
2396
- case import_utils17.AST_NODE_TYPES.ForStatement:
2500
+ case import_utils16.AST_NODE_TYPES.IfStatement:
2501
+ case import_utils16.AST_NODE_TYPES.ConditionalExpression:
2502
+ case import_utils16.AST_NODE_TYPES.WhileStatement:
2503
+ case import_utils16.AST_NODE_TYPES.DoWhileStatement:
2504
+ case import_utils16.AST_NODE_TYPES.ForStatement:
2397
2505
  return parent.test === current;
2398
2506
  default:
2399
2507
  return false;
@@ -2403,13 +2511,13 @@ var isGuardTestPosition = (node) => {
2403
2511
  };
2404
2512
  var isUnvalidatedVariableRef = (node, scope, tracked) => {
2405
2513
  const unwrapped = unwrap(node);
2406
- if (unwrapped === null || unwrapped.type !== import_utils17.AST_NODE_TYPES.Identifier) {
2514
+ if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
2407
2515
  return false;
2408
2516
  }
2409
2517
  const variable = findVariable2(scope, unwrapped.name);
2410
2518
  return variable !== null && tracked.has(variable);
2411
2519
  };
2412
- var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreator(
2520
+ var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreator(
2413
2521
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2414
2522
  )({
2415
2523
  name: "prefer-schema-for-api-payload",
@@ -2429,6 +2537,14 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2429
2537
  return {};
2430
2538
  }
2431
2539
  const unvalidatedVariables = /* @__PURE__ */ new Set();
2540
+ const isFullyNarrowedPattern = (declarator) => {
2541
+ const declared = context.sourceCode.getDeclaredVariables(declarator);
2542
+ return declared.length > 0 && declared.every(
2543
+ (variable) => variable.references.some(
2544
+ (reference) => isValidationRead(reference.identifier)
2545
+ )
2546
+ );
2547
+ };
2432
2548
  const trackInitializer = (declarator) => {
2433
2549
  if (!isRawPayloadSource(declarator.init)) return;
2434
2550
  const declaredVars = context.sourceCode.getDeclaredVariables(declarator);
@@ -2440,13 +2556,15 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2440
2556
  return {
2441
2557
  VariableDeclarator(node) {
2442
2558
  const scope = context.sourceCode.getScope(node);
2443
- if (node.id.type === import_utils17.AST_NODE_TYPES.Identifier) {
2559
+ if (node.id.type === import_utils16.AST_NODE_TYPES.Identifier) {
2444
2560
  trackInitializer(node);
2445
2561
  return;
2446
2562
  }
2447
- if (node.id.type === import_utils17.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils17.AST_NODE_TYPES.ArrayPattern) {
2563
+ if (node.id.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
2448
2564
  if (isRawPayloadSource(node.init)) {
2449
- context.report({ node: node.id, messageId: "unparsedJsonAccess" });
2565
+ if (!isFullyNarrowedPattern(node)) {
2566
+ context.report({ node: node.id, messageId: "unparsedJsonAccess" });
2567
+ }
2450
2568
  return;
2451
2569
  }
2452
2570
  if (isUnvalidatedVariableRef(node.init, scope, unvalidatedVariables)) {
@@ -2456,7 +2574,7 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2456
2574
  },
2457
2575
  AssignmentExpression(node) {
2458
2576
  const scope = context.sourceCode.getScope(node);
2459
- if (node.left.type === import_utils17.AST_NODE_TYPES.Identifier) {
2577
+ if (node.left.type === import_utils16.AST_NODE_TYPES.Identifier) {
2460
2578
  const variable = findVariable2(scope, node.left.name);
2461
2579
  if (variable === null) return;
2462
2580
  if (isRawPayloadSource(node.right)) {
@@ -2466,7 +2584,7 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2466
2584
  }
2467
2585
  return;
2468
2586
  }
2469
- if (node.left.type === import_utils17.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils17.AST_NODE_TYPES.ArrayPattern) {
2587
+ if (node.left.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
2470
2588
  if (isRawPayloadSource(node.right)) {
2471
2589
  context.report({
2472
2590
  node: node.left,
@@ -2483,15 +2601,15 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2483
2601
  }
2484
2602
  },
2485
2603
  CallExpression(node) {
2486
- if (node.callee.type !== import_utils17.AST_NODE_TYPES.Identifier) return;
2604
+ if (node.callee.type !== import_utils16.AST_NODE_TYPES.Identifier) return;
2487
2605
  if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {
2488
2606
  return;
2489
2607
  }
2490
2608
  const scope = context.sourceCode.getScope(node);
2491
2609
  for (const arg of node.arguments) {
2492
- if (arg.type === import_utils17.AST_NODE_TYPES.SpreadElement) continue;
2610
+ if (arg.type === import_utils16.AST_NODE_TYPES.SpreadElement) continue;
2493
2611
  const unwrapped = unwrap(arg);
2494
- if (unwrapped === null || unwrapped.type !== import_utils17.AST_NODE_TYPES.Identifier) {
2612
+ if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
2495
2613
  continue;
2496
2614
  }
2497
2615
  const variable = findVariable2(scope, unwrapped.name);
@@ -2500,17 +2618,18 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2500
2618
  },
2501
2619
  MemberExpression(node) {
2502
2620
  if (isInsideAssertion(node)) return;
2621
+ if (isValidationRead(node)) return;
2503
2622
  const scope = context.sourceCode.getScope(node);
2504
2623
  const obj = unwrap(node.object);
2505
2624
  if (isRawPayloadSource(obj)) {
2506
2625
  const parent = node.parent;
2507
- if (parent.type === import_utils17.AST_NODE_TYPES.CallExpression && parent.callee === node && node.property.type === import_utils17.AST_NODE_TYPES.Identifier && (node.property.name === "parse" || node.property.name === "safeParse")) {
2626
+ 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" || PROMISE_CHAIN_METHODS.has(node.property.name))) {
2508
2627
  return;
2509
2628
  }
2510
2629
  context.report({ node, messageId: "unparsedJsonAccess" });
2511
2630
  return;
2512
2631
  }
2513
- if (obj !== null && obj.type === import_utils17.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
2632
+ if (obj !== null && obj.type === import_utils16.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
2514
2633
  context.report({ node, messageId: "unparsedJsonAccess" });
2515
2634
  const variable = findVariable2(scope, obj.name);
2516
2635
  if (variable !== null) {
@@ -2523,7 +2642,7 @@ var prefer_schema_for_api_payload_default = import_utils17.ESLintUtils.RuleCreat
2523
2642
  });
2524
2643
 
2525
2644
  // src/rules/prefer-semantic-colors.ts
2526
- var import_utils18 = require("@typescript-eslint/utils");
2645
+ var import_utils17 = require("@typescript-eslint/utils");
2527
2646
  var import_fs = require("fs");
2528
2647
  var import_path = require("path");
2529
2648
 
@@ -2535,7 +2654,8 @@ var classTokens = (value) => value.split(/\s+/).filter(Boolean);
2535
2654
  var COLOR_PREFIXES = "text|bg|border(?:-[trblxyse])?|ring(?:-offset)?|fill|stroke|from|via|to|divide|decoration|placeholder|accent|caret|shadow|outline";
2536
2655
  var PALETTE = "red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|slate|gray|zinc|neutral|stone";
2537
2656
  var COLOR_FN = "rgba?|hsla?|hwb|oklch|oklab|lab|lch|color";
2538
- var RAW_PALETTE_RE = new RegExp(`^(?:${COLOR_PREFIXES})-(?:${PALETTE})-\\d{2,3}(?:/\\d{1,3})?$`);
2657
+ var PALETTE_STEP = "50|[1-9]00|950";
2658
+ var RAW_PALETTE_RE = new RegExp(`^(?:${COLOR_PREFIXES})-(?:${PALETTE})-(?:${PALETTE_STEP})(?:/\\d{1,3})?$`);
2539
2659
  var ARBITRARY_COLOR_RE = new RegExp(
2540
2660
  `^(?:${COLOR_PREFIXES})-\\[(?:#[0-9a-fA-F]{3,8}|(?:${COLOR_FN})\\([^\\]]*\\))\\]$`,
2541
2661
  "i"
@@ -2562,6 +2682,7 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
2562
2682
  "lightingColor"
2563
2683
  ]);
2564
2684
  var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
2685
+ var CSS_VAR_REFERENCE_RE = /var\(\s*--/;
2565
2686
  var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
2566
2687
  var SEMANTIC_TOKEN_RE = /--(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\b|(?:bg|text|border)-(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\b/;
2567
2688
  var DETECTION_FILES = [
@@ -2598,8 +2719,8 @@ var SVG_EXEMPT_COLOR_VALUES = /* @__PURE__ */ new Set([
2598
2719
  ]);
2599
2720
  function jsxElementName(node) {
2600
2721
  const name = node.openingElement.name;
2601
- if (name.type === import_utils18.AST_NODE_TYPES.JSXIdentifier) return name.name;
2602
- if (name.type === import_utils18.AST_NODE_TYPES.JSXMemberExpression && name.property.type === import_utils18.AST_NODE_TYPES.JSXIdentifier) {
2722
+ if (name.type === import_utils17.AST_NODE_TYPES.JSXIdentifier) return name.name;
2723
+ if (name.type === import_utils17.AST_NODE_TYPES.JSXMemberExpression && name.property.type === import_utils17.AST_NODE_TYPES.JSXIdentifier) {
2603
2724
  return name.property.name;
2604
2725
  }
2605
2726
  return null;
@@ -2607,10 +2728,25 @@ function jsxElementName(node) {
2607
2728
  function isSvgLikeElementName(name) {
2608
2729
  return name === "svg" || SVG_DEFS_CONTAINERS.has(name) || /svg$/i.test(name);
2609
2730
  }
2731
+ var SVG_SHAPE_PRIMITIVES = /* @__PURE__ */ new Set([
2732
+ "circle",
2733
+ "ellipse",
2734
+ "g",
2735
+ "line",
2736
+ "path",
2737
+ "polygon",
2738
+ "polyline",
2739
+ "rect",
2740
+ "stop",
2741
+ "text",
2742
+ "tspan",
2743
+ "use"
2744
+ ]);
2745
+ var EMAIL_OR_PDF_IMPORT_RE = /@react-(?:email|pdf)\//;
2610
2746
  var isInsideSvg = (node) => {
2611
2747
  let current = node.parent;
2612
2748
  while (current !== void 0 && current !== null) {
2613
- if (current.type === import_utils18.AST_NODE_TYPES.JSXElement) {
2749
+ if (current.type === import_utils17.AST_NODE_TYPES.JSXElement) {
2614
2750
  const name = jsxElementName(current);
2615
2751
  if (name !== null && isSvgLikeElementName(name)) return true;
2616
2752
  }
@@ -2621,7 +2757,7 @@ var isInsideSvg = (node) => {
2621
2757
  var isInsideIconFactoryPath = (node) => {
2622
2758
  let current = node.parent;
2623
2759
  while (current !== void 0 && current !== null) {
2624
- if (current.type === import_utils18.AST_NODE_TYPES.Property && propName(current.key) === "path" && current.parent.type === import_utils18.AST_NODE_TYPES.ObjectExpression && current.parent.parent.type === import_utils18.AST_NODE_TYPES.CallExpression && current.parent.parent.callee.type === import_utils18.AST_NODE_TYPES.Identifier && current.parent.parent.callee.name === "createIcon") {
2760
+ if (current.type === import_utils17.AST_NODE_TYPES.Property && propName(current.key) === "path" && current.parent.type === import_utils17.AST_NODE_TYPES.ObjectExpression && current.parent.parent.type === import_utils17.AST_NODE_TYPES.CallExpression && current.parent.parent.callee.type === import_utils17.AST_NODE_TYPES.Identifier && current.parent.parent.callee.name === "createIcon") {
2625
2761
  return true;
2626
2762
  }
2627
2763
  current = current.parent;
@@ -2671,11 +2807,11 @@ var hasSemanticTokenSystem = (filename) => {
2671
2807
  return answer;
2672
2808
  };
2673
2809
  var propName = (key) => {
2674
- if (key.type === import_utils18.AST_NODE_TYPES.Identifier) return key.name;
2675
- if (key.type === import_utils18.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
2810
+ if (key.type === import_utils17.AST_NODE_TYPES.Identifier) return key.name;
2811
+ if (key.type === import_utils17.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
2676
2812
  return null;
2677
2813
  };
2678
- var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2814
+ var prefer_semantic_colors_default = import_utils17.ESLintUtils.RuleCreator(
2679
2815
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
2680
2816
  )({
2681
2817
  name: "prefer-semantic-colors",
@@ -2702,6 +2838,7 @@ var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2702
2838
  defaultOptions: [{}],
2703
2839
  create(context, [options]) {
2704
2840
  if (STORIES_FILE_RE.test(context.filename)) return {};
2841
+ if (EMAIL_OR_PDF_IMPORT_RE.test(context.sourceCode.getText())) return {};
2705
2842
  if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {
2706
2843
  return {};
2707
2844
  }
@@ -2710,7 +2847,7 @@ var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2710
2847
  const base = tailwindBase(token);
2711
2848
  if (RAW_PALETTE_RE.test(base)) {
2712
2849
  context.report({ node, messageId: "rawPalette", data: { class: token } });
2713
- } else if (ARBITRARY_COLOR_RE.test(base)) {
2850
+ } else if (ARBITRARY_COLOR_RE.test(base) && !CSS_VAR_REFERENCE_RE.test(base)) {
2714
2851
  context.report({ node, messageId: "arbitraryColor", data: { class: token } });
2715
2852
  }
2716
2853
  }
@@ -2718,27 +2855,27 @@ var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2718
2855
  const checkClassNode = (node) => {
2719
2856
  if (node === null) return;
2720
2857
  switch (node.type) {
2721
- case import_utils18.AST_NODE_TYPES.Literal:
2858
+ case import_utils17.AST_NODE_TYPES.Literal:
2722
2859
  if (typeof node.value === "string") reportClasses(node.value, node);
2723
2860
  break;
2724
- case import_utils18.AST_NODE_TYPES.TemplateLiteral:
2861
+ case import_utils17.AST_NODE_TYPES.TemplateLiteral:
2725
2862
  for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? "", quasi);
2726
2863
  break;
2727
- case import_utils18.AST_NODE_TYPES.ArrayExpression:
2864
+ case import_utils17.AST_NODE_TYPES.ArrayExpression:
2728
2865
  for (const element of node.elements) {
2729
- if (element !== null && element.type !== import_utils18.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
2866
+ if (element !== null && element.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
2730
2867
  }
2731
2868
  break;
2732
- case import_utils18.AST_NODE_TYPES.ObjectExpression:
2869
+ case import_utils17.AST_NODE_TYPES.ObjectExpression:
2733
2870
  for (const property of node.properties) {
2734
- if (property.type === import_utils18.AST_NODE_TYPES.Property) checkClassNode(property.value);
2871
+ if (property.type === import_utils17.AST_NODE_TYPES.Property) checkClassNode(property.value);
2735
2872
  }
2736
2873
  break;
2737
- case import_utils18.AST_NODE_TYPES.ConditionalExpression:
2874
+ case import_utils17.AST_NODE_TYPES.ConditionalExpression:
2738
2875
  checkClassNode(node.consequent);
2739
2876
  checkClassNode(node.alternate);
2740
2877
  break;
2741
- case import_utils18.AST_NODE_TYPES.LogicalExpression:
2878
+ case import_utils17.AST_NODE_TYPES.LogicalExpression:
2742
2879
  checkClassNode(node.right);
2743
2880
  break;
2744
2881
  default:
@@ -2746,29 +2883,29 @@ var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2746
2883
  }
2747
2884
  };
2748
2885
  const checkColorValueNode = (node) => {
2749
- if (node.type === import_utils18.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
2886
+ if (node.type === import_utils17.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value) && !CSS_VAR_REFERENCE_RE.test(node.value)) {
2750
2887
  context.report({ node, messageId: "inlineColor", data: { value: node.value } });
2751
2888
  }
2752
2889
  };
2753
2890
  return {
2754
2891
  "JSXAttribute[name.name='className']"(node) {
2755
2892
  if (node.value === null) return;
2756
- if (node.value.type === import_utils18.AST_NODE_TYPES.Literal) checkClassNode(node.value);
2757
- else if (node.value.type === import_utils18.AST_NODE_TYPES.JSXExpressionContainer) {
2758
- if (node.value.expression.type !== import_utils18.AST_NODE_TYPES.JSXEmptyExpression) {
2893
+ if (node.value.type === import_utils17.AST_NODE_TYPES.Literal) checkClassNode(node.value);
2894
+ else if (node.value.type === import_utils17.AST_NODE_TYPES.JSXExpressionContainer) {
2895
+ if (node.value.expression.type !== import_utils17.AST_NODE_TYPES.JSXEmptyExpression) {
2759
2896
  checkClassNode(node.value.expression);
2760
2897
  }
2761
2898
  }
2762
2899
  },
2763
2900
  CallExpression(node) {
2764
- if (node.callee.type === import_utils18.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
2901
+ if (node.callee.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
2765
2902
  for (const arg of node.arguments) {
2766
- if (arg.type !== import_utils18.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
2903
+ if (arg.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
2767
2904
  }
2768
2905
  }
2769
2906
  },
2770
2907
  VariableDeclarator(node) {
2771
- if (node.id.type === import_utils18.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
2908
+ if (node.id.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
2772
2909
  checkClassNode(node.init);
2773
2910
  }
2774
2911
  },
@@ -2780,7 +2917,11 @@ var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2780
2917
  // Neutral drawing literals and anything inside an SVG defs container are
2781
2918
  // structural, not UI tokens, so they never fire.
2782
2919
  "JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
2783
- if (node.value?.type !== import_utils18.AST_NODE_TYPES.Literal) return;
2920
+ if (node.value?.type !== import_utils17.AST_NODE_TYPES.Literal) return;
2921
+ const owner = node.parent.name;
2922
+ if (owner.type === import_utils17.AST_NODE_TYPES.JSXIdentifier && SVG_SHAPE_PRIMITIVES.has(owner.name)) {
2923
+ return;
2924
+ }
2784
2925
  if (typeof node.value.value === "string" && SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())) {
2785
2926
  return;
2786
2927
  }
@@ -2797,7 +2938,7 @@ var prefer_semantic_colors_default = import_utils18.ESLintUtils.RuleCreator(
2797
2938
  });
2798
2939
 
2799
2940
  // src/rules/prefer-server-actions.ts
2800
- var import_utils19 = require("@typescript-eslint/utils");
2941
+ var import_utils18 = require("@typescript-eslint/utils");
2801
2942
  var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
2802
2943
  var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
2803
2944
  var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
@@ -2877,7 +3018,7 @@ function getPropertyNode(objNode, propName2) {
2877
3018
  }
2878
3019
  return null;
2879
3020
  }
2880
- var prefer_server_actions_default = import_utils19.ESLintUtils.RuleCreator(
3021
+ var prefer_server_actions_default = import_utils18.ESLintUtils.RuleCreator(
2881
3022
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
2882
3023
  )({
2883
3024
  name: "prefer-server-actions",
@@ -2952,36 +3093,36 @@ var prefer_server_actions_default = import_utils19.ESLintUtils.RuleCreator(
2952
3093
  });
2953
3094
 
2954
3095
  // src/rules/require-assert-never.ts
2955
- var import_utils20 = require("@typescript-eslint/utils");
3096
+ var import_utils19 = require("@typescript-eslint/utils");
2956
3097
  var isAssertNeverCall = (expression) => {
2957
- if (expression.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
3098
+ if (expression.type !== import_utils19.AST_NODE_TYPES.CallExpression) return false;
2958
3099
  const callee = expression.callee;
2959
- if (callee.type === import_utils20.AST_NODE_TYPES.Identifier) {
3100
+ if (callee.type === import_utils19.AST_NODE_TYPES.Identifier) {
2960
3101
  return callee.name === "assertNever";
2961
3102
  }
2962
- if (callee.type === import_utils20.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils20.AST_NODE_TYPES.Identifier) {
3103
+ if (callee.type === import_utils19.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils19.AST_NODE_TYPES.Identifier) {
2963
3104
  return callee.property.name === "assertNever";
2964
3105
  }
2965
3106
  return false;
2966
3107
  };
2967
3108
  var statementContainsAssertNever = (statement) => {
2968
- if (statement.type === import_utils20.AST_NODE_TYPES.ExpressionStatement) {
3109
+ if (statement.type === import_utils19.AST_NODE_TYPES.ExpressionStatement) {
2969
3110
  return isAssertNeverCall(statement.expression);
2970
3111
  }
2971
- if (statement.type === import_utils20.AST_NODE_TYPES.ThrowStatement) {
3112
+ if (statement.type === import_utils19.AST_NODE_TYPES.ThrowStatement) {
2972
3113
  return isAssertNeverCall(statement.argument);
2973
3114
  }
2974
- if (statement.type === import_utils20.AST_NODE_TYPES.ReturnStatement) {
3115
+ if (statement.type === import_utils19.AST_NODE_TYPES.ReturnStatement) {
2975
3116
  return statement.argument !== null && isAssertNeverCall(statement.argument);
2976
3117
  }
2977
- if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
3118
+ if (statement.type === import_utils19.AST_NODE_TYPES.BlockStatement) {
2978
3119
  return statement.body.some(statementContainsAssertNever);
2979
3120
  }
2980
3121
  return false;
2981
3122
  };
2982
3123
  var isRuntimeHandlingStatement = (statement) => {
2983
- if (statement.type === import_utils20.AST_NODE_TYPES.EmptyStatement) return false;
2984
- if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
3124
+ if (statement.type === import_utils19.AST_NODE_TYPES.EmptyStatement) return false;
3125
+ if (statement.type === import_utils19.AST_NODE_TYPES.BlockStatement) {
2985
3126
  return statement.body.some(isRuntimeHandlingStatement);
2986
3127
  }
2987
3128
  return true;
@@ -2997,12 +3138,12 @@ var isCommentOnlyNoopDefault = (defaultCase, sourceCode) => {
2997
3138
  return colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0;
2998
3139
  }
2999
3140
  const only = defaultCase.consequent[0];
3000
- if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === import_utils20.AST_NODE_TYPES.BlockStatement && only.body.length === 0) {
3141
+ if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === import_utils19.AST_NODE_TYPES.BlockStatement && only.body.length === 0) {
3001
3142
  return sourceCode.getCommentsInside(only).length > 0;
3002
3143
  }
3003
3144
  return false;
3004
3145
  };
3005
- var require_assert_never_default = import_utils20.ESLintUtils.RuleCreator(
3146
+ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
3006
3147
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
3007
3148
  )({
3008
3149
  name: "require-assert-never",
@@ -3040,25 +3181,28 @@ var require_assert_never_default = import_utils20.ESLintUtils.RuleCreator(
3040
3181
  });
3041
3182
 
3042
3183
  // src/rules/require-zod-form-validation.ts
3043
- var import_utils21 = require("@typescript-eslint/utils");
3184
+ var import_utils20 = require("@typescript-eslint/utils");
3044
3185
 
3045
3186
  // src/rules/_zod.ts
3046
3187
  var ZOD_PREFIX_RE = /^Z[A-Z]/;
3047
3188
  var ZOD_SUFFIX_RE = /Schema$/;
3048
3189
  var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
3190
+ function isZodModule(source) {
3191
+ return /(^|[/@-])zod([/-]|$)/.test(source);
3192
+ }
3049
3193
 
3050
3194
  // src/rules/require-zod-form-validation.ts
3051
3195
  var looksLikeZodSchema = (node) => {
3052
3196
  let current = node;
3053
3197
  while (true) {
3054
- if (current.type === import_utils21.AST_NODE_TYPES.Identifier) {
3198
+ if (current.type === import_utils20.AST_NODE_TYPES.Identifier) {
3055
3199
  return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
3056
3200
  }
3057
- if (current.type === import_utils21.AST_NODE_TYPES.CallExpression) {
3201
+ if (current.type === import_utils20.AST_NODE_TYPES.CallExpression) {
3058
3202
  current = current.callee;
3059
3203
  continue;
3060
3204
  }
3061
- if (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
3205
+ if (current.type === import_utils20.AST_NODE_TYPES.MemberExpression) {
3062
3206
  current = current.object;
3063
3207
  continue;
3064
3208
  }
@@ -3066,25 +3210,25 @@ var looksLikeZodSchema = (node) => {
3066
3210
  }
3067
3211
  };
3068
3212
  var isZodParseCall = (node) => {
3069
- if (node.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
3213
+ if (node.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
3070
3214
  const callee = node.callee;
3071
- if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
3215
+ if (callee.type !== import_utils20.AST_NODE_TYPES.MemberExpression) return false;
3072
3216
  if (callee.computed) return false;
3073
- if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
3217
+ if (callee.property.type !== import_utils20.AST_NODE_TYPES.Identifier) return false;
3074
3218
  const method = callee.property.name;
3075
3219
  if (method !== "parse" && method !== "safeParse") return false;
3076
3220
  return looksLikeZodSchema(callee.object);
3077
3221
  };
3078
3222
  var isFormDataMethodCall = (node) => {
3079
3223
  let current = node;
3080
- if (current.type === import_utils21.AST_NODE_TYPES.AwaitExpression) {
3224
+ if (current.type === import_utils20.AST_NODE_TYPES.AwaitExpression) {
3081
3225
  current = current.argument;
3082
3226
  }
3083
- if (current.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
3227
+ if (current.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
3084
3228
  const callee = current.callee;
3085
- return callee.type === import_utils21.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils21.AST_NODE_TYPES.Identifier && callee.property.name === "formData";
3229
+ return callee.type === import_utils20.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils20.AST_NODE_TYPES.Identifier && callee.property.name === "formData";
3086
3230
  };
3087
- var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator(
3231
+ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator(
3088
3232
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
3089
3233
  )({
3090
3234
  name: "require-zod-form-validation",
@@ -3104,14 +3248,14 @@ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator
3104
3248
  return {};
3105
3249
  }
3106
3250
  const isFormSourceIdentifier = (node) => {
3107
- if (node.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
3251
+ if (node.type !== import_utils20.AST_NODE_TYPES.Identifier) return false;
3108
3252
  if (/formdata/i.test(node.name)) return true;
3109
3253
  let scope = context.sourceCode.getScope(node);
3110
3254
  while (scope !== null) {
3111
3255
  const variable = scope.set.get(node.name);
3112
3256
  if (variable !== void 0 && variable.defs.length === 1) {
3113
3257
  const def = variable.defs[0];
3114
- if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils21.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
3258
+ if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils20.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
3115
3259
  return isFormDataMethodCall(def.node.init);
3116
3260
  }
3117
3261
  return false;
@@ -3122,8 +3266,8 @@ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator
3122
3266
  };
3123
3267
  const isFormDataGetCall = (node) => {
3124
3268
  const callee = node.callee;
3125
- if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
3126
- if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
3269
+ if (callee.type !== import_utils20.AST_NODE_TYPES.MemberExpression) return false;
3270
+ if (callee.property.type !== import_utils20.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
3127
3271
  return false;
3128
3272
  }
3129
3273
  return isFormSourceIdentifier(callee.object);
@@ -3138,11 +3282,11 @@ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator
3138
3282
  };
3139
3283
  const isInstanceofNarrowing = (node) => {
3140
3284
  const parent = node.parent;
3141
- return parent !== null && parent !== void 0 && parent.type === import_utils21.AST_NODE_TYPES.BinaryExpression && parent.operator === "instanceof" && parent.left === node;
3285
+ return parent !== null && parent !== void 0 && parent.type === import_utils20.AST_NODE_TYPES.BinaryExpression && parent.operator === "instanceof" && parent.left === node;
3142
3286
  };
3143
3287
  const boundDeclarator = (node) => {
3144
3288
  const parent = node.parent;
3145
- if (parent.type === import_utils21.AST_NODE_TYPES.VariableDeclarator && parent.init === node && parent.id.type === import_utils21.AST_NODE_TYPES.Identifier) {
3289
+ if (parent.type === import_utils20.AST_NODE_TYPES.VariableDeclarator && parent.init === node && parent.id.type === import_utils20.AST_NODE_TYPES.Identifier) {
3146
3290
  return parent;
3147
3291
  }
3148
3292
  return null;
@@ -3170,13 +3314,14 @@ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator
3170
3314
  });
3171
3315
 
3172
3316
  // src/rules/zod-naming-convention.ts
3173
- var import_utils22 = require("@typescript-eslint/utils");
3317
+ var import_utils21 = require("@typescript-eslint/utils");
3174
3318
  var CONVENTIONS = {
3175
3319
  prefix: { test: ZOD_PREFIX_RE, messageId: "zPrefix" },
3176
3320
  suffix: { test: ZOD_SUFFIX_RE, messageId: "schemaSuffix" },
3177
3321
  either: { test: ZOD_SCHEMA_NAME_RE, messageId: "zodSchemaName" }
3178
3322
  };
3179
3323
  var CONTAINS_SCHEMA_RE = /schema/i;
3324
+ var BENCHMARK_PATH_RE = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
3180
3325
  var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
3181
3326
  "parse",
3182
3327
  "parseAsync",
@@ -3194,15 +3339,15 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
3194
3339
  "registry",
3195
3340
  "implement"
3196
3341
  ]);
3197
- var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils22.AST_NODE_TYPES.Identifier ? callee.property.name : null;
3342
+ var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils21.AST_NODE_TYPES.Identifier ? callee.property.name : null;
3198
3343
  var calleeChainStartsWithZ = (node) => {
3199
3344
  let current = node;
3200
- while (current.type === import_utils22.AST_NODE_TYPES.MemberExpression) {
3345
+ while (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
3201
3346
  const receiver = current.object;
3202
- if (receiver.type === import_utils22.AST_NODE_TYPES.Identifier && receiver.name === "z") {
3347
+ if (receiver.type === import_utils21.AST_NODE_TYPES.Identifier && receiver.name === "z") {
3203
3348
  return true;
3204
3349
  }
3205
- if (receiver.type === import_utils22.AST_NODE_TYPES.CallExpression) {
3350
+ if (receiver.type === import_utils21.AST_NODE_TYPES.CallExpression) {
3206
3351
  current = receiver.callee;
3207
3352
  continue;
3208
3353
  }
@@ -3210,7 +3355,7 @@ var calleeChainStartsWithZ = (node) => {
3210
3355
  }
3211
3356
  return false;
3212
3357
  };
3213
- var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
3358
+ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
3214
3359
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
3215
3360
  )({
3216
3361
  name: "zod-naming-convention",
@@ -3242,20 +3387,20 @@ var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
3242
3387
  const convention = optionsArg?.convention ?? "either";
3243
3388
  const { test, messageId } = CONVENTIONS[convention];
3244
3389
  const acceptsSchemaWord = convention !== "prefix";
3245
- if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
3390
+ if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
3246
3391
  return {};
3247
3392
  }
3248
3393
  return {
3249
3394
  VariableDeclarator(node) {
3250
3395
  const init = node.init;
3251
3396
  if (init === null || init === void 0) return;
3252
- if (init.type !== import_utils22.AST_NODE_TYPES.CallExpression) return;
3397
+ if (init.type !== import_utils21.AST_NODE_TYPES.CallExpression) return;
3253
3398
  const callee = init.callee;
3254
- if (callee.type !== import_utils22.AST_NODE_TYPES.MemberExpression) return;
3399
+ if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return;
3255
3400
  if (!calleeChainStartsWithZ(callee)) return;
3256
3401
  const terminal = terminalMethodName(callee);
3257
3402
  if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;
3258
- if (node.id.type !== import_utils22.AST_NODE_TYPES.Identifier) return;
3403
+ if (node.id.type !== import_utils21.AST_NODE_TYPES.Identifier) return;
3259
3404
  if (test.test(node.id.name)) return;
3260
3405
  if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;
3261
3406
  context.report({
@@ -3268,7 +3413,7 @@ var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
3268
3413
  });
3269
3414
 
3270
3415
  // src/rules/no-cors-wildcard-with-credentials.ts
3271
- var import_utils23 = require("@typescript-eslint/utils");
3416
+ var import_utils22 = require("@typescript-eslint/utils");
3272
3417
  var ACAO_HEADER = "access-control-allow-origin";
3273
3418
  var ACAC_HEADER = "access-control-allow-credentials";
3274
3419
  var HEADER_SET_METHODS = /* @__PURE__ */ new Set(["setheader", "set", "append"]);
@@ -3410,7 +3555,7 @@ function enclosingScope(node) {
3410
3555
  }
3411
3556
  return void 0;
3412
3557
  }
3413
- var no_cors_wildcard_with_credentials_default = import_utils23.ESLintUtils.RuleCreator(
3558
+ var no_cors_wildcard_with_credentials_default = import_utils22.ESLintUtils.RuleCreator(
3414
3559
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
3415
3560
  )({
3416
3561
  name: "no-cors-wildcard-with-credentials",
@@ -3478,9 +3623,9 @@ var no_cors_wildcard_with_credentials_default = import_utils23.ESLintUtils.RuleC
3478
3623
  });
3479
3624
 
3480
3625
  // src/rules/no-silent-promise-catch.ts
3481
- var import_utils24 = require("@typescript-eslint/utils");
3626
+ var import_utils23 = require("@typescript-eslint/utils");
3482
3627
  function isBodyParseCall(node) {
3483
- return node.type === import_utils24.AST_NODE_TYPES.CallExpression && node.arguments.length === 0 && node.callee.type === import_utils24.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils24.AST_NODE_TYPES.Identifier && (node.callee.property.name === "json" || node.callee.property.name === "text");
3628
+ return node.type === import_utils23.AST_NODE_TYPES.CallExpression && node.arguments.length === 0 && node.callee.type === import_utils23.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils23.AST_NODE_TYPES.Identifier && (node.callee.property.name === "json" || node.callee.property.name === "text");
3484
3629
  }
3485
3630
  var TEARDOWN_METHODS = /* @__PURE__ */ new Set([
3486
3631
  "cancel",
@@ -3495,21 +3640,21 @@ var TEARDOWN_METHODS = /* @__PURE__ */ new Set([
3495
3640
  var DIRECTIVE_COMMENT_RE = /^\s*(eslint-|@ts-|prettier-ignore|biome-ignore|c8 |v8 |istanbul )/;
3496
3641
  var isExplanatory = (comment) => !DIRECTIVE_COMMENT_RE.test(comment.value);
3497
3642
  function isTeardownCall(node) {
3498
- return node.type === import_utils24.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils24.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils24.AST_NODE_TYPES.Identifier && TEARDOWN_METHODS.has(node.callee.property.name);
3643
+ return node.type === import_utils23.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils23.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils23.AST_NODE_TYPES.Identifier && TEARDOWN_METHODS.has(node.callee.property.name);
3499
3644
  }
3500
3645
  function isSilentExpression(node) {
3501
3646
  switch (node.type) {
3502
- case import_utils24.AST_NODE_TYPES.Literal:
3647
+ case import_utils23.AST_NODE_TYPES.Literal:
3503
3648
  return !("regex" in node);
3504
- case import_utils24.AST_NODE_TYPES.Identifier:
3649
+ case import_utils23.AST_NODE_TYPES.Identifier:
3505
3650
  return node.name === "undefined";
3506
- case import_utils24.AST_NODE_TYPES.UnaryExpression:
3507
- return node.operator === "void" && node.argument.type === import_utils24.AST_NODE_TYPES.Literal;
3508
- case import_utils24.AST_NODE_TYPES.ObjectExpression:
3651
+ case import_utils23.AST_NODE_TYPES.UnaryExpression:
3652
+ return node.operator === "void" && node.argument.type === import_utils23.AST_NODE_TYPES.Literal;
3653
+ case import_utils23.AST_NODE_TYPES.ObjectExpression:
3509
3654
  return node.properties.length === 0;
3510
- case import_utils24.AST_NODE_TYPES.ArrayExpression:
3655
+ case import_utils23.AST_NODE_TYPES.ArrayExpression:
3511
3656
  return node.elements.length === 0;
3512
- case import_utils24.AST_NODE_TYPES.TSAsExpression:
3657
+ case import_utils23.AST_NODE_TYPES.TSAsExpression:
3513
3658
  return isSilentExpression(node.expression);
3514
3659
  default:
3515
3660
  return false;
@@ -3517,7 +3662,7 @@ function isSilentExpression(node) {
3517
3662
  }
3518
3663
  function isSilentHandler(handler) {
3519
3664
  const body = handler.body;
3520
- if (body.type !== import_utils24.AST_NODE_TYPES.BlockStatement) {
3665
+ if (body.type !== import_utils23.AST_NODE_TYPES.BlockStatement) {
3521
3666
  return isSilentExpression(body);
3522
3667
  }
3523
3668
  if (body.body.length === 0) {
@@ -3525,13 +3670,13 @@ function isSilentHandler(handler) {
3525
3670
  }
3526
3671
  if (body.body.length === 1) {
3527
3672
  const only = body.body[0];
3528
- if (only !== void 0 && only.type === import_utils24.AST_NODE_TYPES.ReturnStatement) {
3673
+ if (only !== void 0 && only.type === import_utils23.AST_NODE_TYPES.ReturnStatement) {
3529
3674
  return only.argument === null || isSilentExpression(only.argument);
3530
3675
  }
3531
3676
  }
3532
3677
  return false;
3533
3678
  }
3534
- var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
3679
+ var no_silent_promise_catch_default = import_utils23.ESLintUtils.RuleCreator(
3535
3680
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
3536
3681
  )({
3537
3682
  name: "no-silent-promise-catch",
@@ -3556,7 +3701,7 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
3556
3701
  return true;
3557
3702
  }
3558
3703
  let statement = call;
3559
- while (statement.parent !== void 0 && statement.parent !== null && !statement.type.endsWith("Statement") && statement.type !== import_utils24.AST_NODE_TYPES.VariableDeclaration) {
3704
+ while (statement.parent !== void 0 && statement.parent !== null && !statement.type.endsWith("Statement") && statement.type !== import_utils23.AST_NODE_TYPES.VariableDeclaration) {
3560
3705
  statement = statement.parent;
3561
3706
  }
3562
3707
  if (sourceCode.getCommentsBefore(statement).some(isExplanatory)) {
@@ -3568,7 +3713,7 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
3568
3713
  };
3569
3714
  return {
3570
3715
  CallExpression(node) {
3571
- if (node.callee.type !== import_utils24.AST_NODE_TYPES.MemberExpression || node.callee.computed || node.callee.property.type !== import_utils24.AST_NODE_TYPES.Identifier || node.callee.property.name !== "catch") {
3716
+ if (node.callee.type !== import_utils23.AST_NODE_TYPES.MemberExpression || node.callee.computed || node.callee.property.type !== import_utils23.AST_NODE_TYPES.Identifier || node.callee.property.name !== "catch") {
3572
3717
  return;
3573
3718
  }
3574
3719
  if (isBodyParseCall(node.callee.object)) {
@@ -3577,14 +3722,14 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
3577
3722
  if (isTeardownCall(node.callee.object)) {
3578
3723
  return;
3579
3724
  }
3580
- if (node.parent.type === import_utils24.AST_NODE_TYPES.MemberExpression && node.parent.object === node) {
3725
+ if (node.parent.type === import_utils23.AST_NODE_TYPES.MemberExpression && node.parent.object === node) {
3581
3726
  return;
3582
3727
  }
3583
3728
  if (node.arguments.length !== 1) {
3584
3729
  return;
3585
3730
  }
3586
3731
  const handler = node.arguments[0];
3587
- if (handler === void 0 || handler.type !== import_utils24.AST_NODE_TYPES.ArrowFunctionExpression && handler.type !== import_utils24.AST_NODE_TYPES.FunctionExpression) {
3732
+ if (handler === void 0 || handler.type !== import_utils23.AST_NODE_TYPES.ArrowFunctionExpression && handler.type !== import_utils23.AST_NODE_TYPES.FunctionExpression) {
3588
3733
  return;
3589
3734
  }
3590
3735
  if (hasExplanatoryComment(node, handler)) {
@@ -3599,7 +3744,7 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
3599
3744
  });
3600
3745
 
3601
3746
  // src/rules/require-fetch-timeout.ts
3602
- var import_utils25 = require("@typescript-eslint/utils");
3747
+ var import_utils24 = require("@typescript-eslint/utils");
3603
3748
  var CODEMOD_FIXTURE_RE = /[\\/]__testfixtures__[\\/]/;
3604
3749
  var GLOBAL_OBJECTS = /* @__PURE__ */ new Set([
3605
3750
  "globalThis",
@@ -3616,14 +3761,14 @@ function matchesAnyPattern2(filename, patterns) {
3616
3761
  return false;
3617
3762
  }
3618
3763
  function initProvablyLacksSignal(init) {
3619
- if (init.type !== import_utils25.AST_NODE_TYPES.ObjectExpression) {
3764
+ if (init.type !== import_utils24.AST_NODE_TYPES.ObjectExpression) {
3620
3765
  return false;
3621
3766
  }
3622
3767
  for (const prop of init.properties) {
3623
- if (prop.type === import_utils25.AST_NODE_TYPES.SpreadElement) {
3768
+ if (prop.type === import_utils24.AST_NODE_TYPES.SpreadElement) {
3624
3769
  return false;
3625
3770
  }
3626
- if (prop.key.type === import_utils25.AST_NODE_TYPES.Identifier && prop.key.name === "signal" || prop.key.type === import_utils25.AST_NODE_TYPES.Literal && prop.key.value === "signal") {
3771
+ if (prop.key.type === import_utils24.AST_NODE_TYPES.Identifier && prop.key.name === "signal" || prop.key.type === import_utils24.AST_NODE_TYPES.Literal && prop.key.value === "signal") {
3627
3772
  return false;
3628
3773
  }
3629
3774
  if (prop.computed) {
@@ -3633,9 +3778,9 @@ function initProvablyLacksSignal(init) {
3633
3778
  return true;
3634
3779
  }
3635
3780
  function isStringish(node) {
3636
- return node.type === import_utils25.AST_NODE_TYPES.Literal && typeof node.value === "string" || node.type === import_utils25.AST_NODE_TYPES.TemplateLiteral;
3781
+ return node.type === import_utils24.AST_NODE_TYPES.Literal && typeof node.value === "string" || node.type === import_utils24.AST_NODE_TYPES.TemplateLiteral;
3637
3782
  }
3638
- var require_fetch_timeout_default = import_utils25.ESLintUtils.RuleCreator(
3783
+ var require_fetch_timeout_default = import_utils24.ESLintUtils.RuleCreator(
3639
3784
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
3640
3785
  )({
3641
3786
  name: "require-fetch-timeout",
@@ -3672,14 +3817,14 @@ var require_fetch_timeout_default = import_utils25.ESLintUtils.RuleCreator(
3672
3817
  }
3673
3818
  function resolvesToGlobal(identifier) {
3674
3819
  const scope = context.sourceCode.getScope(identifier);
3675
- const variable = import_utils25.ASTUtils.findVariable(scope, identifier.name);
3820
+ const variable = import_utils24.ASTUtils.findVariable(scope, identifier.name);
3676
3821
  return variable === null || variable.defs.length === 0;
3677
3822
  }
3678
3823
  function isGlobalFetchCall2(callee) {
3679
- if (callee.type === import_utils25.AST_NODE_TYPES.Identifier) {
3824
+ if (callee.type === import_utils24.AST_NODE_TYPES.Identifier) {
3680
3825
  return callee.name === "fetch" && resolvesToGlobal(callee);
3681
3826
  }
3682
- return callee.type === import_utils25.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils25.AST_NODE_TYPES.Identifier && callee.property.name === "fetch" && callee.object.type === import_utils25.AST_NODE_TYPES.Identifier && GLOBAL_OBJECTS.has(callee.object.name) && resolvesToGlobal(callee.object);
3827
+ return callee.type === import_utils24.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils24.AST_NODE_TYPES.Identifier && callee.property.name === "fetch" && callee.object.type === import_utils24.AST_NODE_TYPES.Identifier && GLOBAL_OBJECTS.has(callee.object.name) && resolvesToGlobal(callee.object);
3683
3828
  }
3684
3829
  return {
3685
3830
  CallExpression(node) {
@@ -3699,12 +3844,12 @@ var require_fetch_timeout_default = import_utils25.ESLintUtils.RuleCreator(
3699
3844
  });
3700
3845
 
3701
3846
  // src/rules/no-fat-try-blocks.ts
3702
- var import_utils26 = require("@typescript-eslint/utils");
3847
+ var import_utils25 = require("@typescript-eslint/utils");
3703
3848
  var MAX_TRY_BODY_STATEMENTS = 3;
3704
3849
  var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set([
3705
- import_utils26.AST_NODE_TYPES.FunctionDeclaration,
3706
- import_utils26.AST_NODE_TYPES.FunctionExpression,
3707
- import_utils26.AST_NODE_TYPES.ArrowFunctionExpression
3850
+ import_utils25.AST_NODE_TYPES.FunctionDeclaration,
3851
+ import_utils25.AST_NODE_TYPES.FunctionExpression,
3852
+ import_utils25.AST_NODE_TYPES.ArrowFunctionExpression
3708
3853
  ]);
3709
3854
  var PURE_METHODS = /* @__PURE__ */ new Set([
3710
3855
  "map",
@@ -3802,22 +3947,22 @@ function isNode3(value) {
3802
3947
  }
3803
3948
  function isPureCall(node) {
3804
3949
  const callee = node.callee;
3805
- if (callee.type !== import_utils26.AST_NODE_TYPES.MemberExpression) {
3950
+ if (callee.type !== import_utils25.AST_NODE_TYPES.MemberExpression) {
3806
3951
  return false;
3807
3952
  }
3808
3953
  const property = callee.property;
3809
- if (property.type !== import_utils26.AST_NODE_TYPES.Identifier) {
3954
+ if (property.type !== import_utils25.AST_NODE_TYPES.Identifier) {
3810
3955
  return false;
3811
3956
  }
3812
- if (callee.object.type === import_utils26.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
3957
+ if (callee.object.type === import_utils25.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
3813
3958
  return true;
3814
3959
  }
3815
3960
  return PURE_METHODS.has(property.name);
3816
3961
  }
3817
3962
  function isPureNew(node) {
3818
- return node.callee.type === import_utils26.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
3963
+ return node.callee.type === import_utils25.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
3819
3964
  }
3820
- function subtreeMatches(stmt, predicate) {
3965
+ function subtreeMatches(stmt, predicate, descendIntoFunctions = false) {
3821
3966
  let found = false;
3822
3967
  const visit = (current) => {
3823
3968
  if (found) {
@@ -3831,7 +3976,7 @@ function subtreeMatches(stmt, predicate) {
3831
3976
  if (key === "parent") {
3832
3977
  continue;
3833
3978
  }
3834
- if (NESTED_FUNCTION_TYPES.has(current.type) && key === "body") {
3979
+ if (!descendIntoFunctions && NESTED_FUNCTION_TYPES.has(current.type) && key === "body") {
3835
3980
  continue;
3836
3981
  }
3837
3982
  const value = current[key];
@@ -3852,20 +3997,20 @@ function subtreeMatches(stmt, predicate) {
3852
3997
  visit(stmt);
3853
3998
  return found;
3854
3999
  }
3855
- var hasAwait = (node) => subtreeMatches(node, (n) => n.type === import_utils26.AST_NODE_TYPES.AwaitExpression);
4000
+ var hasAwait = (node) => subtreeMatches(node, (n) => n.type === import_utils25.AST_NODE_TYPES.AwaitExpression);
3856
4001
  var hasThrowingCallOrNew = (node) => subtreeMatches(
3857
4002
  node,
3858
- (n) => n.type === import_utils26.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils26.AST_NODE_TYPES.NewExpression && !isPureNew(n)
4003
+ (n) => n.type === import_utils25.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils25.AST_NODE_TYPES.NewExpression && !isPureNew(n)
3859
4004
  );
3860
4005
  function unwrap2(expr) {
3861
4006
  let current = expr;
3862
- while (current.type === import_utils26.AST_NODE_TYPES.ChainExpression || current.type === import_utils26.AST_NODE_TYPES.TSNonNullExpression) {
4007
+ while (current.type === import_utils25.AST_NODE_TYPES.ChainExpression || current.type === import_utils25.AST_NODE_TYPES.TSNonNullExpression) {
3863
4008
  current = current.expression;
3864
4009
  }
3865
4010
  return current;
3866
4011
  }
3867
4012
  function isBareCallStatement(stmt) {
3868
- return stmt.type === import_utils26.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils26.AST_NODE_TYPES.CallExpression;
4013
+ return stmt.type === import_utils25.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils25.AST_NODE_TYPES.CallExpression;
3869
4014
  }
3870
4015
  function canThrow(stmt) {
3871
4016
  if (hasAwait(stmt)) {
@@ -3874,10 +4019,10 @@ function canThrow(stmt) {
3874
4019
  if (isBareCallStatement(stmt)) {
3875
4020
  return false;
3876
4021
  }
3877
- if (stmt.type === import_utils26.AST_NODE_TYPES.BlockStatement) {
4022
+ if (stmt.type === import_utils25.AST_NODE_TYPES.BlockStatement) {
3878
4023
  return stmt.body.some(canThrow);
3879
4024
  }
3880
- if (stmt.type === import_utils26.AST_NODE_TYPES.IfStatement) {
4025
+ if (stmt.type === import_utils25.AST_NODE_TYPES.IfStatement) {
3881
4026
  return hasThrowingCallOrNew(stmt.test) || canThrow(stmt.consequent) || stmt.alternate !== null && canThrow(stmt.alternate);
3882
4027
  }
3883
4028
  return hasThrowingCallOrNew(stmt);
@@ -3888,9 +4033,137 @@ function handlerRethrows(handler) {
3888
4033
  }
3889
4034
  const body = handler.body.body;
3890
4035
  const last = body[body.length - 1];
3891
- return last !== void 0 && last.type === import_utils26.AST_NODE_TYPES.ThrowStatement;
4036
+ return last !== void 0 && last.type === import_utils25.AST_NODE_TYPES.ThrowStatement;
4037
+ }
4038
+ var PASS_THROUGH_PARENTS = /* @__PURE__ */ new Set([
4039
+ import_utils25.AST_NODE_TYPES.IfStatement,
4040
+ import_utils25.AST_NODE_TYPES.TryStatement,
4041
+ import_utils25.AST_NODE_TYPES.CatchClause,
4042
+ import_utils25.AST_NODE_TYPES.LabeledStatement
4043
+ ]);
4044
+ function isTerminalInFunction(node) {
4045
+ let current = node;
4046
+ let parent = current.parent;
4047
+ while (parent !== void 0) {
4048
+ if (parent.type === import_utils25.AST_NODE_TYPES.BlockStatement) {
4049
+ if (parent.body[parent.body.length - 1] !== current) {
4050
+ return false;
4051
+ }
4052
+ } else if (parent.type === import_utils25.AST_NODE_TYPES.Program) {
4053
+ return parent.body[parent.body.length - 1] === current;
4054
+ } else if (NESTED_FUNCTION_TYPES.has(parent.type)) {
4055
+ return true;
4056
+ } else if (!PASS_THROUGH_PARENTS.has(parent.type)) {
4057
+ return false;
4058
+ }
4059
+ current = parent;
4060
+ parent = current.parent;
4061
+ }
4062
+ return false;
4063
+ }
4064
+ function unwrapAwait(expr) {
4065
+ const inner = unwrap2(expr);
4066
+ return inner.type === import_utils25.AST_NODE_TYPES.AwaitExpression ? unwrap2(inner.argument) : inner;
4067
+ }
4068
+ function handlerEndsByHandingOff(handler) {
4069
+ const body = handler.body.body;
4070
+ const last = body[body.length - 1];
4071
+ if (last === void 0) {
4072
+ return false;
4073
+ }
4074
+ if (last.type === import_utils25.AST_NODE_TYPES.ReturnStatement || last.type === import_utils25.AST_NODE_TYPES.ThrowStatement) {
4075
+ return true;
4076
+ }
4077
+ return last.type === import_utils25.AST_NODE_TYPES.ExpressionStatement && unwrapAwait(last.expression).type === import_utils25.AST_NODE_TYPES.CallExpression;
4078
+ }
4079
+ function collectBindingNames(pattern, names) {
4080
+ if (pattern.type === import_utils25.AST_NODE_TYPES.Identifier) {
4081
+ names.add(pattern.name);
4082
+ return;
4083
+ }
4084
+ if (pattern.type === import_utils25.AST_NODE_TYPES.ObjectPattern) {
4085
+ for (const property of pattern.properties) {
4086
+ collectBindingNames(
4087
+ property.type === import_utils25.AST_NODE_TYPES.RestElement ? property.argument : property.value,
4088
+ names
4089
+ );
4090
+ }
4091
+ return;
4092
+ }
4093
+ if (pattern.type === import_utils25.AST_NODE_TYPES.ArrayPattern) {
4094
+ for (const element of pattern.elements) {
4095
+ if (element !== null) {
4096
+ collectBindingNames(element, names);
4097
+ }
4098
+ }
4099
+ return;
4100
+ }
4101
+ if (pattern.type === import_utils25.AST_NODE_TYPES.AssignmentPattern || pattern.type === import_utils25.AST_NODE_TYPES.RestElement) {
4102
+ collectBindingNames(
4103
+ pattern.type === import_utils25.AST_NODE_TYPES.AssignmentPattern ? pattern.left : pattern.argument,
4104
+ names
4105
+ );
4106
+ }
4107
+ }
4108
+ function caughtBindingNames(handler) {
4109
+ const names = /* @__PURE__ */ new Set();
4110
+ if (handler.param !== null) {
4111
+ collectBindingNames(handler.param, names);
4112
+ }
4113
+ return names;
4114
+ }
4115
+ function isPropertyName(node) {
4116
+ const parent = node.parent;
4117
+ if (parent.type === import_utils25.AST_NODE_TYPES.MemberExpression) {
4118
+ return parent.property === node && !parent.computed;
4119
+ }
4120
+ if (parent.type === import_utils25.AST_NODE_TYPES.Property) {
4121
+ return parent.key === node && !parent.computed;
4122
+ }
4123
+ return false;
4124
+ }
4125
+ function handlerMentionsCaughtBinding(handler) {
4126
+ const names = caughtBindingNames(handler);
4127
+ if (names.size === 0) {
4128
+ return false;
4129
+ }
4130
+ return subtreeMatches(
4131
+ handler.body,
4132
+ (n) => n.type === import_utils25.AST_NODE_TYPES.Identifier && names.has(n.name) && !isPropertyName(n),
4133
+ true
4134
+ );
4135
+ }
4136
+ function isSuccessShapedValue(expr) {
4137
+ let current = expr;
4138
+ while (current.type === import_utils25.AST_NODE_TYPES.TSAsExpression || current.type === import_utils25.AST_NODE_TYPES.TSNonNullExpression) {
4139
+ current = current.expression;
4140
+ }
4141
+ if (current.type === import_utils25.AST_NODE_TYPES.Literal) {
4142
+ return current.value === null || current.value === false;
4143
+ }
4144
+ if (current.type === import_utils25.AST_NODE_TYPES.Identifier) {
4145
+ return current.name === "undefined";
4146
+ }
4147
+ if (current.type === import_utils25.AST_NODE_TYPES.UnaryExpression) {
4148
+ return current.operator === "void";
4149
+ }
4150
+ if (current.type === import_utils25.AST_NODE_TYPES.ArrayExpression) {
4151
+ return current.elements.length === 0;
4152
+ }
4153
+ if (current.type === import_utils25.AST_NODE_TYPES.ObjectExpression) {
4154
+ return current.properties.length === 0;
4155
+ }
4156
+ return false;
4157
+ }
4158
+ var handlerReturnsSuccessShaped = (handler) => subtreeMatches(
4159
+ handler.body,
4160
+ (n) => n.type === import_utils25.AST_NODE_TYPES.ReturnStatement && n.argument !== null && isSuccessShapedValue(n.argument)
4161
+ );
4162
+ function isTerminalErrorBoundary(node) {
4163
+ const handler = node.handler;
4164
+ return handler !== null && isTerminalInFunction(node) && handlerEndsByHandingOff(handler) && handlerMentionsCaughtBinding(handler) && !handlerReturnsSuccessShaped(handler);
3892
4165
  }
3893
- var no_fat_try_blocks_default = import_utils26.ESLintUtils.RuleCreator(
4166
+ var no_fat_try_blocks_default = import_utils25.ESLintUtils.RuleCreator(
3894
4167
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
3895
4168
  )({
3896
4169
  name: "no-fat-try-blocks",
@@ -3918,6 +4191,9 @@ var no_fat_try_blocks_default = import_utils26.ESLintUtils.RuleCreator(
3918
4191
  if (handlerRethrows(node.handler)) {
3919
4192
  return;
3920
4193
  }
4194
+ if (isTerminalErrorBoundary(node)) {
4195
+ return;
4196
+ }
3921
4197
  const count = node.block.body.filter(canThrow).length;
3922
4198
  if (count <= MAX_TRY_BODY_STATEMENTS) {
3923
4199
  return;
@@ -3934,7 +4210,7 @@ var no_fat_try_blocks_default = import_utils26.ESLintUtils.RuleCreator(
3934
4210
  });
3935
4211
 
3936
4212
  // src/rules/no-secret-in-log.ts
3937
- var import_utils27 = require("@typescript-eslint/utils");
4213
+ var import_utils26 = require("@typescript-eslint/utils");
3938
4214
 
3939
4215
  // src/rules/_secret_names.ts
3940
4216
  var SECRET_WORDS = /* @__PURE__ */ new Set([
@@ -4196,7 +4472,7 @@ function propertyKeyName2(prop) {
4196
4472
  }
4197
4473
  return null;
4198
4474
  }
4199
- var no_secret_in_log_default = import_utils27.ESLintUtils.RuleCreator(
4475
+ var no_secret_in_log_default = import_utils26.ESLintUtils.RuleCreator(
4200
4476
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
4201
4477
  )({
4202
4478
  name: "no-secret-in-log",
@@ -4273,24 +4549,24 @@ var no_secret_in_log_default = import_utils27.ESLintUtils.RuleCreator(
4273
4549
  });
4274
4550
 
4275
4551
  // src/rules/no-unsafe-mock-casting.ts
4552
+ var import_utils27 = require("@typescript-eslint/utils");
4276
4553
  var import_utils28 = require("@typescript-eslint/utils");
4277
- var import_utils29 = require("@typescript-eslint/utils");
4278
4554
  function isMockTypeReference(node) {
4279
- if (node.type !== import_utils29.AST_NODE_TYPES.TSTypeReference) {
4555
+ if (node.type !== import_utils28.AST_NODE_TYPES.TSTypeReference) {
4280
4556
  return false;
4281
4557
  }
4282
4558
  const typeName = node.typeName;
4283
- if (typeName.type === import_utils29.AST_NODE_TYPES.Identifier) {
4559
+ if (typeName.type === import_utils28.AST_NODE_TYPES.Identifier) {
4284
4560
  const name = typeName.name;
4285
4561
  return name === "Mock" || name === "MockInstance" || name === "SpyInstance";
4286
4562
  }
4287
- if (typeName.type === import_utils29.AST_NODE_TYPES.TSQualifiedName) {
4563
+ if (typeName.type === import_utils28.AST_NODE_TYPES.TSQualifiedName) {
4288
4564
  const rightName = typeName.right.name;
4289
4565
  return rightName === "Mock" || rightName === "MockInstance" || rightName === "SpyInstance";
4290
4566
  }
4291
4567
  return false;
4292
4568
  }
4293
- var no_unsafe_mock_casting_default = import_utils28.ESLintUtils.RuleCreator(
4569
+ var no_unsafe_mock_casting_default = import_utils27.ESLintUtils.RuleCreator(
4294
4570
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
4295
4571
  )({
4296
4572
  name: "no-unsafe-mock-casting",
@@ -4322,7 +4598,7 @@ var no_unsafe_mock_casting_default = import_utils28.ESLintUtils.RuleCreator(
4322
4598
  });
4323
4599
 
4324
4600
  // src/rules/prefer-string-literal-union.ts
4325
- var import_utils30 = require("@typescript-eslint/utils");
4601
+ var import_utils29 = require("@typescript-eslint/utils");
4326
4602
  var ts = __toESM(require("typescript"), 1);
4327
4603
  var CHOICE_TOKENS = /* @__PURE__ */ new Set([
4328
4604
  "status",
@@ -4365,19 +4641,19 @@ function isChoiceLikeName(name) {
4365
4641
  return CHOICE_TOKENS.has(lastWord(name));
4366
4642
  }
4367
4643
  function keyName(key) {
4368
- if (key.type === import_utils30.AST_NODE_TYPES.Identifier) {
4644
+ if (key.type === import_utils29.AST_NODE_TYPES.Identifier) {
4369
4645
  return key.name;
4370
4646
  }
4371
- if (key.type === import_utils30.AST_NODE_TYPES.Literal && typeof key.value === "string") {
4647
+ if (key.type === import_utils29.AST_NODE_TYPES.Literal && typeof key.value === "string") {
4372
4648
  return key.value;
4373
4649
  }
4374
4650
  return null;
4375
4651
  }
4376
4652
  function isStringLiteralMember(t) {
4377
- return t.type === import_utils30.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils30.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
4653
+ return t.type === import_utils29.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils29.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
4378
4654
  }
4379
4655
  function isStringLiteralUnion(node) {
4380
- if (node?.type !== import_utils30.AST_NODE_TYPES.TSUnionType) {
4656
+ if (node?.type !== import_utils29.AST_NODE_TYPES.TSUnionType) {
4381
4657
  return false;
4382
4658
  }
4383
4659
  return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
@@ -4406,12 +4682,12 @@ function bindingSourceExpression(decl) {
4406
4682
  return ts.isForOfStatement(node) ? node.expression : node.initializer;
4407
4683
  }
4408
4684
  function refKey(node) {
4409
- if (node.type === import_utils30.AST_NODE_TYPES.Identifier) {
4685
+ if (node.type === import_utils29.AST_NODE_TYPES.Identifier) {
4410
4686
  return node.name;
4411
4687
  }
4412
- if (node.type === import_utils30.AST_NODE_TYPES.MemberExpression && !node.computed) {
4688
+ if (node.type === import_utils29.AST_NODE_TYPES.MemberExpression && !node.computed) {
4413
4689
  const inner = refKey(node.object);
4414
- if (inner === null || node.property.type !== import_utils30.AST_NODE_TYPES.Identifier) {
4690
+ if (inner === null || node.property.type !== import_utils29.AST_NODE_TYPES.Identifier) {
4415
4691
  return null;
4416
4692
  }
4417
4693
  return `${inner}.${node.property.name}`;
@@ -4419,12 +4695,12 @@ function refKey(node) {
4419
4695
  return null;
4420
4696
  }
4421
4697
  function strLiteral(node) {
4422
- if (node.type === import_utils30.AST_NODE_TYPES.Literal && typeof node.value === "string") {
4698
+ if (node.type === import_utils29.AST_NODE_TYPES.Literal && typeof node.value === "string") {
4423
4699
  return node.value;
4424
4700
  }
4425
4701
  return null;
4426
4702
  }
4427
- var prefer_string_literal_union_default = import_utils30.ESLintUtils.RuleCreator(
4703
+ var prefer_string_literal_union_default = import_utils29.ESLintUtils.RuleCreator(
4428
4704
  (name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
4429
4705
  )({
4430
4706
  name: "prefer-string-literal-union",
@@ -4462,7 +4738,7 @@ var prefer_string_literal_union_default = import_utils30.ESLintUtils.RuleCreator
4462
4738
  );
4463
4739
  let services;
4464
4740
  try {
4465
- services = import_utils30.ESLintUtils.getParserServices(context);
4741
+ services = import_utils29.ESLintUtils.getParserServices(context);
4466
4742
  } catch {
4467
4743
  services = null;
4468
4744
  }
@@ -4574,7 +4850,7 @@ var prefer_string_literal_union_default = import_utils30.ESLintUtils.RuleCreator
4574
4850
  containersWithUnion.add(container);
4575
4851
  return;
4576
4852
  }
4577
- if (typeNode?.type !== import_utils30.AST_NODE_TYPES.TSStringKeyword) {
4853
+ if (typeNode?.type !== import_utils29.AST_NODE_TYPES.TSStringKeyword) {
4578
4854
  return;
4579
4855
  }
4580
4856
  const name = keyName(key);
@@ -4662,10 +4938,10 @@ var prefer_string_literal_union_default = import_utils30.ESLintUtils.RuleCreator
4662
4938
  }
4663
4939
  };
4664
4940
  function refKeyText(node) {
4665
- if (node.type === import_utils30.AST_NODE_TYPES.BinaryExpression) {
4941
+ if (node.type === import_utils29.AST_NODE_TYPES.BinaryExpression) {
4666
4942
  return refKey(node.left) ?? refKey(node.right) ?? "value";
4667
4943
  }
4668
- if (node.type === import_utils30.AST_NODE_TYPES.SwitchStatement) {
4944
+ if (node.type === import_utils29.AST_NODE_TYPES.SwitchStatement) {
4669
4945
  return refKey(node.discriminant) ?? "value";
4670
4946
  }
4671
4947
  return "value";
@@ -4674,11 +4950,8 @@ var prefer_string_literal_union_default = import_utils30.ESLintUtils.RuleCreator
4674
4950
  });
4675
4951
 
4676
4952
  // src/rules/prefer-zod-enum.ts
4677
- var import_utils31 = require("@typescript-eslint/utils");
4678
- function isZodModule(source) {
4679
- return /(^|[/@-])zod([/-]|$)/.test(source);
4680
- }
4681
- var prefer_zod_enum_default = import_utils31.ESLintUtils.RuleCreator(
4953
+ var import_utils30 = require("@typescript-eslint/utils");
4954
+ var prefer_zod_enum_default = import_utils30.ESLintUtils.RuleCreator(
4682
4955
  (name) => `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`
4683
4956
  )({
4684
4957
  name: "prefer-zod-enum",
@@ -4699,20 +4972,20 @@ var prefer_zod_enum_default = import_utils31.ESLintUtils.RuleCreator(
4699
4972
  const zodNamespaces = /* @__PURE__ */ new Set();
4700
4973
  function enumValues(node) {
4701
4974
  const callee = node.callee;
4702
- if (callee.type !== import_utils31.AST_NODE_TYPES.MemberExpression || callee.computed || callee.object.type !== import_utils31.AST_NODE_TYPES.Identifier || !zodNamespaces.has(callee.object.name) || callee.property.type !== import_utils31.AST_NODE_TYPES.Identifier || callee.property.name !== "union" || node.arguments.length !== 1) {
4975
+ if (callee.type !== import_utils30.AST_NODE_TYPES.MemberExpression || callee.computed || callee.object.type !== import_utils30.AST_NODE_TYPES.Identifier || !zodNamespaces.has(callee.object.name) || callee.property.type !== import_utils30.AST_NODE_TYPES.Identifier || callee.property.name !== "union" || node.arguments.length !== 1) {
4703
4976
  return null;
4704
4977
  }
4705
4978
  const argument = node.arguments[0];
4706
- if (argument === void 0 || argument.type !== import_utils31.AST_NODE_TYPES.ArrayExpression || argument.elements.length === 0) {
4979
+ if (argument === void 0 || argument.type !== import_utils30.AST_NODE_TYPES.ArrayExpression || argument.elements.length === 0) {
4707
4980
  return null;
4708
4981
  }
4709
4982
  const values = [];
4710
4983
  for (const element of argument.elements) {
4711
- if (element === null || element.type !== import_utils31.AST_NODE_TYPES.CallExpression || element.arguments.length !== 1 || element.callee.type !== import_utils31.AST_NODE_TYPES.MemberExpression || element.callee.computed || element.callee.object.type !== import_utils31.AST_NODE_TYPES.Identifier || !zodNamespaces.has(element.callee.object.name) || element.callee.property.type !== import_utils31.AST_NODE_TYPES.Identifier || element.callee.property.name !== "literal") {
4984
+ if (element === null || element.type !== import_utils30.AST_NODE_TYPES.CallExpression || element.arguments.length !== 1 || element.callee.type !== import_utils30.AST_NODE_TYPES.MemberExpression || element.callee.computed || element.callee.object.type !== import_utils30.AST_NODE_TYPES.Identifier || !zodNamespaces.has(element.callee.object.name) || element.callee.property.type !== import_utils30.AST_NODE_TYPES.Identifier || element.callee.property.name !== "literal") {
4712
4985
  return null;
4713
4986
  }
4714
4987
  const value = element.arguments[0];
4715
- if (value === void 0 || value.type !== import_utils31.AST_NODE_TYPES.Literal || typeof value.value !== "string") {
4988
+ if (value === void 0 || value.type !== import_utils30.AST_NODE_TYPES.Literal || typeof value.value !== "string") {
4716
4989
  return null;
4717
4990
  }
4718
4991
  values.push(value);
@@ -4721,11 +4994,11 @@ var prefer_zod_enum_default = import_utils31.ESLintUtils.RuleCreator(
4721
4994
  }
4722
4995
  function buildFix(node, values) {
4723
4996
  const argument = node.arguments[0];
4724
- if (argument === void 0 || argument.type !== import_utils31.AST_NODE_TYPES.ArrayExpression || sourceCode.getCommentsInside(argument).length > 0) {
4997
+ if (argument === void 0 || argument.type !== import_utils30.AST_NODE_TYPES.ArrayExpression || sourceCode.getCommentsInside(argument).length > 0) {
4725
4998
  return void 0;
4726
4999
  }
4727
5000
  const callee = node.callee;
4728
- if (callee.type !== import_utils31.AST_NODE_TYPES.MemberExpression || callee.property.type !== import_utils31.AST_NODE_TYPES.Identifier) {
5001
+ if (callee.type !== import_utils30.AST_NODE_TYPES.MemberExpression || callee.property.type !== import_utils30.AST_NODE_TYPES.Identifier) {
4729
5002
  return void 0;
4730
5003
  }
4731
5004
  return (fixer) => [
@@ -4742,7 +5015,7 @@ var prefer_zod_enum_default = import_utils31.ESLintUtils.RuleCreator(
4742
5015
  return;
4743
5016
  }
4744
5017
  for (const specifier of node.specifiers) {
4745
- if (specifier.type === import_utils31.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils31.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils31.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === import_utils31.AST_NODE_TYPES.Identifier && specifier.imported.name === "z") {
5018
+ if (specifier.type === import_utils30.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils30.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils30.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === import_utils30.AST_NODE_TYPES.Identifier && specifier.imported.name === "z") {
4746
5019
  zodNamespaces.add(specifier.local.name);
4747
5020
  }
4748
5021
  }
@@ -4763,51 +5036,454 @@ var prefer_zod_enum_default = import_utils31.ESLintUtils.RuleCreator(
4763
5036
  }
4764
5037
  });
4765
5038
 
4766
- // src/rules/no-offset-pagination.ts
4767
- var import_utils33 = require("@typescript-eslint/utils");
4768
-
4769
- // src/rules/_sql.ts
4770
- var import_utils32 = require("@typescript-eslint/utils");
4771
- function stripSqlNoise(text) {
4772
- const out = [...text];
4773
- const n = text.length;
4774
- let i = 0;
4775
- while (i < n) {
4776
- const ch = text[i];
4777
- if (ch === "'" || ch === '"') {
4778
- out[i] = " ";
4779
- i += 1;
4780
- while (i < n) {
4781
- const c = text[i];
4782
- if (c === ch) {
4783
- if (i + 1 < n && text[i + 1] === ch) {
4784
- out[i] = " ";
4785
- out[i + 1] = " ";
4786
- i += 2;
4787
- continue;
4788
- }
4789
- out[i] = " ";
4790
- i += 1;
4791
- break;
4792
- }
4793
- if (c !== "\n") {
4794
- out[i] = " ";
4795
- }
4796
- i += 1;
4797
- }
4798
- continue;
4799
- }
4800
- if (ch === "-" && text[i + 1] === "-") {
4801
- while (i < n && text[i] !== "\n") {
4802
- out[i] = " ";
4803
- i += 1;
4804
- }
4805
- continue;
4806
- }
4807
- if (ch === "/" && text[i + 1] === "*") {
4808
- out[i] = " ";
4809
- out[i + 1] = " ";
4810
- i += 2;
5039
+ // src/rules/prefer-zod-infer.ts
5040
+ var import_utils31 = require("@typescript-eslint/utils");
5041
+ var SHAPE_PRESERVING_METHODS = /* @__PURE__ */ new Set([
5042
+ "describe",
5043
+ "refine",
5044
+ "superRefine",
5045
+ "check",
5046
+ "meta",
5047
+ "register"
5048
+ ]);
5049
+ var OPTIONAL_MODIFIERS = /* @__PURE__ */ new Set([
5050
+ "optional",
5051
+ "nullish",
5052
+ "default",
5053
+ "prefault",
5054
+ "catch"
5055
+ ]);
5056
+ var NULLABLE_MODIFIERS = /* @__PURE__ */ new Set(["nullable", "nullish"]);
5057
+ var RESHAPING_MODIFIERS = /* @__PURE__ */ new Set([
5058
+ "transform",
5059
+ "pipe",
5060
+ "preprocess",
5061
+ "brand",
5062
+ "overwrite"
5063
+ ]);
5064
+ var MODULE_LEVEL_RESHAPERS = /* @__PURE__ */ new Set([
5065
+ "transform",
5066
+ "pipe",
5067
+ "preprocess"
5068
+ ]);
5069
+ var ZOD_TYPE_CONSTRAINTS = /* @__PURE__ */ new Set([
5070
+ "ZodType",
5071
+ "ZodSchema",
5072
+ "ZodTypeAny",
5073
+ "ZodMiniType",
5074
+ "Schema"
5075
+ ]);
5076
+ var LEAF_NODE_TYPES = {
5077
+ string: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5078
+ email: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5079
+ url: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5080
+ uuid: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5081
+ ulid: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5082
+ cuid: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5083
+ cuid2: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5084
+ nanoid: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5085
+ iso: [import_utils31.AST_NODE_TYPES.TSStringKeyword],
5086
+ number: [import_utils31.AST_NODE_TYPES.TSNumberKeyword],
5087
+ int: [import_utils31.AST_NODE_TYPES.TSNumberKeyword],
5088
+ float32: [import_utils31.AST_NODE_TYPES.TSNumberKeyword],
5089
+ float64: [import_utils31.AST_NODE_TYPES.TSNumberKeyword],
5090
+ boolean: [import_utils31.AST_NODE_TYPES.TSBooleanKeyword],
5091
+ bigint: [import_utils31.AST_NODE_TYPES.TSBigIntKeyword],
5092
+ symbol: [import_utils31.AST_NODE_TYPES.TSSymbolKeyword],
5093
+ any: [import_utils31.AST_NODE_TYPES.TSAnyKeyword],
5094
+ unknown: [import_utils31.AST_NODE_TYPES.TSUnknownKeyword],
5095
+ never: [import_utils31.AST_NODE_TYPES.TSNeverKeyword],
5096
+ void: [import_utils31.AST_NODE_TYPES.TSVoidKeyword],
5097
+ null: [import_utils31.AST_NODE_TYPES.TSNullKeyword],
5098
+ undefined: [import_utils31.AST_NODE_TYPES.TSUndefinedKeyword],
5099
+ literal: [import_utils31.AST_NODE_TYPES.TSLiteralType],
5100
+ date: [import_utils31.AST_NODE_TYPES.TSTypeReference],
5101
+ array: [import_utils31.AST_NODE_TYPES.TSArrayType, import_utils31.AST_NODE_TYPES.TSTypeReference],
5102
+ tuple: [import_utils31.AST_NODE_TYPES.TSTupleType],
5103
+ object: [import_utils31.AST_NODE_TYPES.TSTypeLiteral, import_utils31.AST_NODE_TYPES.TSTypeReference],
5104
+ strictObject: [import_utils31.AST_NODE_TYPES.TSTypeLiteral, import_utils31.AST_NODE_TYPES.TSTypeReference],
5105
+ looseObject: [import_utils31.AST_NODE_TYPES.TSTypeLiteral, import_utils31.AST_NODE_TYPES.TSTypeReference],
5106
+ record: [import_utils31.AST_NODE_TYPES.TSTypeReference, import_utils31.AST_NODE_TYPES.TSTypeLiteral],
5107
+ map: [import_utils31.AST_NODE_TYPES.TSTypeReference],
5108
+ set: [import_utils31.AST_NODE_TYPES.TSTypeReference],
5109
+ promise: [import_utils31.AST_NODE_TYPES.TSTypeReference],
5110
+ enum: [import_utils31.AST_NODE_TYPES.TSUnionType, import_utils31.AST_NODE_TYPES.TSTypeReference, import_utils31.AST_NODE_TYPES.TSLiteralType],
5111
+ nativeEnum: [import_utils31.AST_NODE_TYPES.TSUnionType, import_utils31.AST_NODE_TYPES.TSTypeReference, import_utils31.AST_NODE_TYPES.TSLiteralType],
5112
+ union: [import_utils31.AST_NODE_TYPES.TSUnionType, import_utils31.AST_NODE_TYPES.TSTypeReference],
5113
+ discriminatedUnion: [import_utils31.AST_NODE_TYPES.TSUnionType, import_utils31.AST_NODE_TYPES.TSTypeReference],
5114
+ intersection: [import_utils31.AST_NODE_TYPES.TSIntersectionType, import_utils31.AST_NODE_TYPES.TSTypeReference]
5115
+ };
5116
+ function normalizeSchemaName(name) {
5117
+ return name.replace(/Schema$/i, "").replace(/^Z(?=[A-Z])/, "").toLowerCase();
5118
+ }
5119
+ function normalizeTypeName(name) {
5120
+ return name.replace(/Type$/, "").toLowerCase();
5121
+ }
5122
+ function unwrapNullish(annotation) {
5123
+ if (annotation.type !== import_utils31.AST_NODE_TYPES.TSUnionType) {
5124
+ return {
5125
+ core: annotation,
5126
+ nullable: annotation.type === import_utils31.AST_NODE_TYPES.TSNullKeyword
5127
+ };
5128
+ }
5129
+ const rest = [];
5130
+ let nullable = false;
5131
+ for (const member of annotation.types) {
5132
+ if (member.type === import_utils31.AST_NODE_TYPES.TSNullKeyword) {
5133
+ nullable = true;
5134
+ continue;
5135
+ }
5136
+ if (member.type === import_utils31.AST_NODE_TYPES.TSUndefinedKeyword) {
5137
+ continue;
5138
+ }
5139
+ rest.push(member);
5140
+ }
5141
+ if (rest.length === 1) {
5142
+ return { core: rest[0] ?? null, nullable };
5143
+ }
5144
+ return { core: rest.length === 0 ? null : annotation, nullable };
5145
+ }
5146
+ function leafAgrees(leaf, annotation) {
5147
+ if (leaf === null || annotation === null) {
5148
+ return null;
5149
+ }
5150
+ const expected = LEAF_NODE_TYPES[leaf];
5151
+ if (expected === void 0) {
5152
+ return null;
5153
+ }
5154
+ const { core } = unwrapNullish(annotation);
5155
+ if (core === null) {
5156
+ return null;
5157
+ }
5158
+ return expected.includes(core.type);
5159
+ }
5160
+ var prefer_zod_infer_default = import_utils31.ESLintUtils.RuleCreator(
5161
+ (name) => `https://github.com/sarj-ai/standards/tree/main/packages/typescript#${name}`
5162
+ )({
5163
+ name: "prefer-zod-infer",
5164
+ meta: {
5165
+ type: "problem",
5166
+ docs: {
5167
+ description: "Derive a type from its Zod schema with `z.infer` instead of hand-writing a twin declaration beside it."
5168
+ },
5169
+ schema: [
5170
+ {
5171
+ type: "object",
5172
+ additionalProperties: false,
5173
+ properties: {
5174
+ ignoreTypeNames: {
5175
+ type: "array",
5176
+ items: { type: "string" }
5177
+ },
5178
+ requireIdenticalShape: { type: "boolean" }
5179
+ }
5180
+ }
5181
+ ],
5182
+ messages: {
5183
+ handWrittenTwin: "`{{typeName}}` restates the shape of the Zod schema `{{schemaName}}` declared in this module. Derive it instead \u2014 `type {{typeName}} = z.infer<typeof {{schemaName}}>` \u2014 so a field added to the schema cannot silently leave the type behind."
5184
+ }
5185
+ },
5186
+ defaultOptions: [{}],
5187
+ create(context, [optionsArg]) {
5188
+ const ignorePatterns = (optionsArg?.ignoreTypeNames ?? []).map(
5189
+ (source) => new RegExp(source, "u")
5190
+ );
5191
+ const requireIdenticalShape = optionsArg?.requireIdenticalShape ?? true;
5192
+ if (isTestFile(context.filename) || isStoryFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
5193
+ return {};
5194
+ }
5195
+ const zodNamespaces = /* @__PURE__ */ new Set();
5196
+ const schemas = [];
5197
+ const typeDeclarations = [];
5198
+ const constrainedTypeNames = /* @__PURE__ */ new Set();
5199
+ const reshapedSchemaNames = /* @__PURE__ */ new Set();
5200
+ function zodCallChain(node) {
5201
+ const chain = [];
5202
+ let current = node;
5203
+ while (current.type === import_utils31.AST_NODE_TYPES.CallExpression) {
5204
+ const callee = current.callee;
5205
+ if (callee.type !== import_utils31.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils31.AST_NODE_TYPES.Identifier) {
5206
+ return null;
5207
+ }
5208
+ chain.push(current);
5209
+ const receiver = callee.object;
5210
+ if (receiver.type === import_utils31.AST_NODE_TYPES.Identifier) {
5211
+ return zodNamespaces.has(receiver.name) ? chain.reverse() : null;
5212
+ }
5213
+ current = receiver;
5214
+ }
5215
+ return null;
5216
+ }
5217
+ function methodName(call) {
5218
+ const callee = call.callee;
5219
+ return callee.type === import_utils31.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils31.AST_NODE_TYPES.Identifier ? callee.property.name : "";
5220
+ }
5221
+ function schemaField(node) {
5222
+ const modifiers = [];
5223
+ let current = node;
5224
+ let leaf = null;
5225
+ while (current.type === import_utils31.AST_NODE_TYPES.CallExpression) {
5226
+ const callee = current.callee;
5227
+ if (callee.type !== import_utils31.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils31.AST_NODE_TYPES.Identifier) {
5228
+ break;
5229
+ }
5230
+ const receiver = callee.object;
5231
+ if (receiver.type === import_utils31.AST_NODE_TYPES.Identifier && zodNamespaces.has(receiver.name)) {
5232
+ leaf = callee.property.name;
5233
+ break;
5234
+ }
5235
+ modifiers.push(callee.property.name);
5236
+ current = receiver;
5237
+ }
5238
+ return {
5239
+ leaf,
5240
+ optional: modifiers.some((name) => OPTIONAL_MODIFIERS.has(name)),
5241
+ nullable: modifiers.some((name) => NULLABLE_MODIFIERS.has(name)),
5242
+ reshaped: modifiers.some((name) => RESHAPING_MODIFIERS.has(name))
5243
+ };
5244
+ }
5245
+ function schemaFields(init) {
5246
+ const chain = zodCallChain(init);
5247
+ if (chain === null || chain.length === 0) {
5248
+ return null;
5249
+ }
5250
+ const [base, ...rest] = chain;
5251
+ if (base === void 0) {
5252
+ return null;
5253
+ }
5254
+ const baseMethod = methodName(base);
5255
+ if (baseMethod !== "object" && baseMethod !== "strictObject") {
5256
+ return null;
5257
+ }
5258
+ if (rest.some((call) => !SHAPE_PRESERVING_METHODS.has(methodName(call)))) {
5259
+ return null;
5260
+ }
5261
+ const shape = base.arguments[0];
5262
+ if (shape === void 0 || shape.type !== import_utils31.AST_NODE_TYPES.ObjectExpression) {
5263
+ return null;
5264
+ }
5265
+ const fields = /* @__PURE__ */ new Map();
5266
+ for (const property of shape.properties) {
5267
+ if (property.type !== import_utils31.AST_NODE_TYPES.Property || property.computed) {
5268
+ return null;
5269
+ }
5270
+ const { key } = property;
5271
+ const name = key.type === import_utils31.AST_NODE_TYPES.Identifier ? key.name : key.type === import_utils31.AST_NODE_TYPES.Literal && typeof key.value === "string" ? key.value : null;
5272
+ if (name === null) {
5273
+ return null;
5274
+ }
5275
+ fields.set(name, schemaField(property.value));
5276
+ }
5277
+ return fields.size === 0 ? null : fields;
5278
+ }
5279
+ function typeMembers(members) {
5280
+ const result = /* @__PURE__ */ new Map();
5281
+ for (const member of members) {
5282
+ if (member.type !== import_utils31.AST_NODE_TYPES.TSPropertySignature || member.computed) {
5283
+ return null;
5284
+ }
5285
+ const { key } = member;
5286
+ const name = key.type === import_utils31.AST_NODE_TYPES.Identifier ? key.name : key.type === import_utils31.AST_NODE_TYPES.Literal && typeof key.value === "string" ? key.value : null;
5287
+ if (name === null) {
5288
+ return null;
5289
+ }
5290
+ const annotation = member.typeAnnotation?.typeAnnotation ?? null;
5291
+ result.set(name, {
5292
+ optional: member.optional === true,
5293
+ nullable: annotation !== null && unwrapNullish(annotation).nullable,
5294
+ annotation
5295
+ });
5296
+ }
5297
+ return result.size === 0 ? null : result;
5298
+ }
5299
+ function collectConstrainedNames(node) {
5300
+ if (node.type === import_utils31.AST_NODE_TYPES.TSTypeReference) {
5301
+ if (node.typeName.type === import_utils31.AST_NODE_TYPES.Identifier) {
5302
+ constrainedTypeNames.add(node.typeName.name);
5303
+ }
5304
+ for (const argument of node.typeArguments?.params ?? []) {
5305
+ collectConstrainedNames(argument);
5306
+ }
5307
+ return;
5308
+ }
5309
+ if (node.type === import_utils31.AST_NODE_TYPES.TSArrayType) {
5310
+ collectConstrainedNames(node.elementType);
5311
+ return;
5312
+ }
5313
+ if (node.type === import_utils31.AST_NODE_TYPES.TSUnionType || node.type === import_utils31.AST_NODE_TYPES.TSIntersectionType) {
5314
+ for (const member of node.types) {
5315
+ collectConstrainedNames(member);
5316
+ }
5317
+ }
5318
+ }
5319
+ function isTwin(fields, members) {
5320
+ if (!requireIdenticalShape) {
5321
+ return true;
5322
+ }
5323
+ if (fields.size !== members.size) {
5324
+ return false;
5325
+ }
5326
+ let agreements = 0;
5327
+ for (const [name, field] of fields) {
5328
+ const member = members.get(name);
5329
+ if (member === void 0) {
5330
+ return false;
5331
+ }
5332
+ if (field.reshaped) {
5333
+ return false;
5334
+ }
5335
+ if (field.optional !== member.optional) {
5336
+ return false;
5337
+ }
5338
+ if (field.nullable !== member.nullable) {
5339
+ return false;
5340
+ }
5341
+ const agrees = leafAgrees(field.leaf, member.annotation);
5342
+ if (agrees === false) {
5343
+ return false;
5344
+ }
5345
+ if (agrees === true) {
5346
+ agreements += 1;
5347
+ }
5348
+ }
5349
+ return agreements > 0;
5350
+ }
5351
+ return {
5352
+ ImportDeclaration(node) {
5353
+ if (!isZodModule(node.source.value)) {
5354
+ return;
5355
+ }
5356
+ for (const specifier of node.specifiers) {
5357
+ if (specifier.type === import_utils31.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils31.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils31.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === import_utils31.AST_NODE_TYPES.Identifier && specifier.imported.name === "z") {
5358
+ zodNamespaces.add(specifier.local.name);
5359
+ }
5360
+ }
5361
+ },
5362
+ VariableDeclarator(node) {
5363
+ if (node.id.type !== import_utils31.AST_NODE_TYPES.Identifier || node.init == null) {
5364
+ return;
5365
+ }
5366
+ const fields = schemaFields(node.init);
5367
+ if (fields !== null) {
5368
+ schemas.push({ name: node.id.name, fields });
5369
+ }
5370
+ },
5371
+ /** Guard (f): `XSchema.transform(...)` anywhere in the module. */
5372
+ "MemberExpression[computed=false]"(node) {
5373
+ if (node.object.type === import_utils31.AST_NODE_TYPES.Identifier && node.property.type === import_utils31.AST_NODE_TYPES.Identifier && MODULE_LEVEL_RESHAPERS.has(node.property.name)) {
5374
+ reshapedSchemaNames.add(node.object.name);
5375
+ }
5376
+ },
5377
+ /** Guard (c): `z.ZodType<T>` and every other type argument it carries. */
5378
+ TSTypeReference(node) {
5379
+ const { typeName } = node;
5380
+ const referenced = typeName.type === import_utils31.AST_NODE_TYPES.Identifier ? typeName.name : typeName.type === import_utils31.AST_NODE_TYPES.TSQualifiedName && typeName.right.type === import_utils31.AST_NODE_TYPES.Identifier ? typeName.right.name : null;
5381
+ if (referenced === null || !ZOD_TYPE_CONSTRAINTS.has(referenced)) {
5382
+ return;
5383
+ }
5384
+ for (const argument of node.typeArguments?.params ?? []) {
5385
+ collectConstrainedNames(argument);
5386
+ }
5387
+ },
5388
+ TSInterfaceDeclaration(node) {
5389
+ if (node.typeParameters !== void 0 || (node.extends?.length ?? 0) > 0) {
5390
+ return;
5391
+ }
5392
+ const members = typeMembers(node.body.body);
5393
+ if (members !== null) {
5394
+ typeDeclarations.push({ name: node.id.name, node: node.id, members });
5395
+ }
5396
+ },
5397
+ TSTypeAliasDeclaration(node) {
5398
+ if (node.typeParameters !== void 0 || node.typeAnnotation.type !== import_utils31.AST_NODE_TYPES.TSTypeLiteral) {
5399
+ return;
5400
+ }
5401
+ const members = typeMembers(node.typeAnnotation.members);
5402
+ if (members !== null) {
5403
+ typeDeclarations.push({ name: node.id.name, node: node.id, members });
5404
+ }
5405
+ },
5406
+ "Program:exit"() {
5407
+ if (schemas.length === 0 || typeDeclarations.length === 0) {
5408
+ return;
5409
+ }
5410
+ const byName = /* @__PURE__ */ new Map();
5411
+ for (const schema of schemas) {
5412
+ const key = normalizeSchemaName(schema.name);
5413
+ if (key !== "" && !byName.has(key)) {
5414
+ byName.set(key, schema);
5415
+ }
5416
+ }
5417
+ for (const declaration of typeDeclarations) {
5418
+ if (constrainedTypeNames.has(declaration.name)) {
5419
+ continue;
5420
+ }
5421
+ if (ignorePatterns.some((pattern) => pattern.test(declaration.name))) {
5422
+ continue;
5423
+ }
5424
+ const schema = byName.get(normalizeTypeName(declaration.name));
5425
+ if (schema === void 0 || reshapedSchemaNames.has(schema.name)) {
5426
+ continue;
5427
+ }
5428
+ if (!isTwin(schema.fields, declaration.members)) {
5429
+ continue;
5430
+ }
5431
+ context.report({
5432
+ node: declaration.node,
5433
+ messageId: "handWrittenTwin",
5434
+ data: { typeName: declaration.name, schemaName: schema.name }
5435
+ });
5436
+ }
5437
+ }
5438
+ };
5439
+ }
5440
+ });
5441
+
5442
+ // src/rules/no-offset-pagination.ts
5443
+ var import_utils33 = require("@typescript-eslint/utils");
5444
+
5445
+ // src/rules/_sql.ts
5446
+ var import_utils32 = require("@typescript-eslint/utils");
5447
+ function stripSqlNoise(text) {
5448
+ const out = [...text];
5449
+ const n = text.length;
5450
+ let i = 0;
5451
+ while (i < n) {
5452
+ const ch = text[i];
5453
+ if (ch === "'" || ch === '"') {
5454
+ out[i] = " ";
5455
+ i += 1;
5456
+ while (i < n) {
5457
+ const c = text[i];
5458
+ if (c === ch) {
5459
+ if (i + 1 < n && text[i + 1] === ch) {
5460
+ out[i] = " ";
5461
+ out[i + 1] = " ";
5462
+ i += 2;
5463
+ continue;
5464
+ }
5465
+ out[i] = " ";
5466
+ i += 1;
5467
+ break;
5468
+ }
5469
+ if (c !== "\n") {
5470
+ out[i] = " ";
5471
+ }
5472
+ i += 1;
5473
+ }
5474
+ continue;
5475
+ }
5476
+ if (ch === "-" && text[i + 1] === "-") {
5477
+ while (i < n && text[i] !== "\n") {
5478
+ out[i] = " ";
5479
+ i += 1;
5480
+ }
5481
+ continue;
5482
+ }
5483
+ if (ch === "/" && text[i + 1] === "*") {
5484
+ out[i] = " ";
5485
+ out[i + 1] = " ";
5486
+ i += 2;
4811
5487
  while (i < n && !(text[i] === "*" && text[i + 1] === "/")) {
4812
5488
  if (text[i] !== "\n") {
4813
5489
  out[i] = " ";
@@ -5106,7 +5782,7 @@ var MIN_DISTINCT_SCOPES = 2;
5106
5782
  var PREVIEW_LENGTH = 40;
5107
5783
  var SQL_KEYWORD_RE = /\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|VALUES|ON CONFLICT|RETURNING|GROUP BY|ORDER BY)\b/;
5108
5784
  var IDENTIFIER_RE = /^[a-z_][a-z0-9_.]*$/;
5109
- var FUNCTION_TYPES = /* @__PURE__ */ new Set([
5785
+ var FUNCTION_TYPES2 = /* @__PURE__ */ new Set([
5110
5786
  import_utils35.AST_NODE_TYPES.FunctionDeclaration,
5111
5787
  import_utils35.AST_NODE_TYPES.FunctionExpression,
5112
5788
  import_utils35.AST_NODE_TYPES.ArrowFunctionExpression
@@ -5120,7 +5796,7 @@ function preview(value) {
5120
5796
  }
5121
5797
  function enclosingFunction(node) {
5122
5798
  for (let current = node.parent; current != null; current = current.parent) {
5123
- if (FUNCTION_TYPES.has(current.type)) {
5799
+ if (FUNCTION_TYPES2.has(current.type)) {
5124
5800
  return current;
5125
5801
  }
5126
5802
  }
@@ -5284,7 +5960,7 @@ var TEST_CALLERS = /* @__PURE__ */ new Set([
5284
5960
  "beforeEach",
5285
5961
  "afterEach"
5286
5962
  ]);
5287
- var FUNCTION_TYPES2 = /* @__PURE__ */ new Set([
5963
+ var FUNCTION_TYPES3 = /* @__PURE__ */ new Set([
5288
5964
  import_utils37.AST_NODE_TYPES.FunctionDeclaration,
5289
5965
  import_utils37.AST_NODE_TYPES.FunctionExpression,
5290
5966
  import_utils37.AST_NODE_TYPES.ArrowFunctionExpression
@@ -5316,7 +5992,7 @@ function isHelperSleep(node) {
5316
5992
  }
5317
5993
  function nearestEnclosingFunction(node) {
5318
5994
  for (let current = node.parent; current != null; current = current.parent) {
5319
- if (!FUNCTION_TYPES2.has(current.type)) {
5995
+ if (!FUNCTION_TYPES3.has(current.type)) {
5320
5996
  continue;
5321
5997
  }
5322
5998
  const grandparent = current.parent;
@@ -5394,14 +6070,24 @@ var no_sleep_in_test_body_default = import_utils37.ESLintUtils.RuleCreator(
5394
6070
  // src/rules/no-conditional-in-test.ts
5395
6071
  var import_utils38 = require("@typescript-eslint/utils");
5396
6072
  var TEST_CALLERS2 = /* @__PURE__ */ new Set(["it", "test"]);
5397
- var FUNCTION_TYPES3 = /* @__PURE__ */ new Set([
6073
+ var FUNCTION_TYPES4 = /* @__PURE__ */ new Set([
5398
6074
  import_utils38.AST_NODE_TYPES.FunctionDeclaration,
5399
6075
  import_utils38.AST_NODE_TYPES.FunctionExpression,
5400
6076
  import_utils38.AST_NODE_TYPES.ArrowFunctionExpression
5401
6077
  ]);
6078
+ var ASSERTION_ROOTS = /* @__PURE__ */ new Set([
6079
+ "expect",
6080
+ "expectTypeOf",
6081
+ "assert",
6082
+ "assertType"
6083
+ ]);
6084
+ var TYPE_ASSERTION_ROOTS = /* @__PURE__ */ new Set([
6085
+ "expectTypeOf",
6086
+ "assertType"
6087
+ ]);
5402
6088
  function nearestEnclosingFunction2(node) {
5403
6089
  for (let current = node.parent; current != null; current = current.parent) {
5404
- if (FUNCTION_TYPES3.has(current.type)) {
6090
+ if (FUNCTION_TYPES4.has(current.type)) {
5405
6091
  return current;
5406
6092
  }
5407
6093
  }
@@ -5430,6 +6116,166 @@ function isTestBody2(fn) {
5430
6116
  const name = testCallerName2(call.callee);
5431
6117
  return name !== null && TEST_CALLERS2.has(name);
5432
6118
  }
6119
+ function subtreeMatches2(node, predicate, descendIntoFunctions = true) {
6120
+ let found = false;
6121
+ const visit = (current) => {
6122
+ if (found) {
6123
+ return;
6124
+ }
6125
+ if (predicate(current)) {
6126
+ found = true;
6127
+ return;
6128
+ }
6129
+ for (const key of Object.keys(current)) {
6130
+ if (key === "parent") {
6131
+ continue;
6132
+ }
6133
+ if (!descendIntoFunctions && FUNCTION_TYPES4.has(current.type) && key === "body") {
6134
+ continue;
6135
+ }
6136
+ const value = current[key];
6137
+ if (Array.isArray(value)) {
6138
+ for (const child of value) {
6139
+ if (typeof child === "object" && child !== null && typeof child.type === "string") {
6140
+ visit(child);
6141
+ }
6142
+ }
6143
+ } else if (typeof value === "object" && value !== null && typeof value.type === "string") {
6144
+ visit(value);
6145
+ }
6146
+ if (found) {
6147
+ return;
6148
+ }
6149
+ }
6150
+ };
6151
+ visit(node);
6152
+ return found;
6153
+ }
6154
+ function rootIdentifier(node) {
6155
+ switch (node.type) {
6156
+ case import_utils38.AST_NODE_TYPES.Identifier:
6157
+ return node.name;
6158
+ case import_utils38.AST_NODE_TYPES.MemberExpression:
6159
+ return rootIdentifier(node.object);
6160
+ case import_utils38.AST_NODE_TYPES.UnaryExpression:
6161
+ return rootIdentifier(node.argument);
6162
+ case import_utils38.AST_NODE_TYPES.AwaitExpression:
6163
+ return rootIdentifier(node.argument);
6164
+ case import_utils38.AST_NODE_TYPES.ChainExpression:
6165
+ case import_utils38.AST_NODE_TYPES.TSNonNullExpression:
6166
+ case import_utils38.AST_NODE_TYPES.TSAsExpression:
6167
+ return rootIdentifier(node.expression);
6168
+ case import_utils38.AST_NODE_TYPES.BinaryExpression:
6169
+ case import_utils38.AST_NODE_TYPES.LogicalExpression:
6170
+ return rootIdentifier(node.left);
6171
+ case import_utils38.AST_NODE_TYPES.CallExpression:
6172
+ return rootIdentifier(node.callee);
6173
+ default:
6174
+ return null;
6175
+ }
6176
+ }
6177
+ function calleeRootName(call) {
6178
+ let current = call.callee;
6179
+ for (; ; ) {
6180
+ if (current.type === import_utils38.AST_NODE_TYPES.Identifier) {
6181
+ return current.name;
6182
+ }
6183
+ if (current.type === import_utils38.AST_NODE_TYPES.MemberExpression) {
6184
+ current = current.object;
6185
+ continue;
6186
+ }
6187
+ if (current.type === import_utils38.AST_NODE_TYPES.CallExpression) {
6188
+ current = current.callee;
6189
+ continue;
6190
+ }
6191
+ if (current.type === import_utils38.AST_NODE_TYPES.ChainExpression || current.type === import_utils38.AST_NODE_TYPES.TSNonNullExpression) {
6192
+ current = current.expression;
6193
+ continue;
6194
+ }
6195
+ return null;
6196
+ }
6197
+ }
6198
+ var isAssertionCall = (node) => node.type === import_utils38.AST_NODE_TYPES.CallExpression && ASSERTION_ROOTS.has(calleeRootName(node) ?? "");
6199
+ var isTypeAssertionCall = (node) => node.type === import_utils38.AST_NODE_TYPES.CallExpression && TYPE_ASSERTION_ROOTS.has(calleeRootName(node) ?? "");
6200
+ var containsAssertion = (node) => subtreeMatches2(node, isAssertionCall);
6201
+ var containsSkipCall = (node) => subtreeMatches2(
6202
+ node,
6203
+ (current) => current.type === import_utils38.AST_NODE_TYPES.CallExpression && current.callee.type === import_utils38.AST_NODE_TYPES.MemberExpression && !current.callee.computed && current.callee.property.type === import_utils38.AST_NODE_TYPES.Identifier && current.callee.property.name === "skip"
6204
+ );
6205
+ var containsEscape = (node) => subtreeMatches2(
6206
+ node,
6207
+ (current) => current.type === import_utils38.AST_NODE_TYPES.ReturnStatement || current.type === import_utils38.AST_NODE_TYPES.ContinueStatement || current.type === import_utils38.AST_NODE_TYPES.BreakStatement,
6208
+ false
6209
+ );
6210
+ function branchStatements(branch) {
6211
+ return branch.type === import_utils38.AST_NODE_TYPES.BlockStatement ? branch.body : [branch];
6212
+ }
6213
+ function previousSibling(node) {
6214
+ const parent = node.parent;
6215
+ let siblings = null;
6216
+ if (parent.type === import_utils38.AST_NODE_TYPES.BlockStatement || parent.type === import_utils38.AST_NODE_TYPES.Program || parent.type === import_utils38.AST_NODE_TYPES.StaticBlock) {
6217
+ siblings = parent.body;
6218
+ } else if (parent.type === import_utils38.AST_NODE_TYPES.SwitchCase) {
6219
+ siblings = parent.consequent;
6220
+ }
6221
+ if (siblings === null) {
6222
+ return null;
6223
+ }
6224
+ const index = siblings.indexOf(node);
6225
+ return index > 0 ? siblings[index - 1] ?? null : null;
6226
+ }
6227
+ function isPinnedNarrowingGuard(node) {
6228
+ const testRoot = rootIdentifier(node.test);
6229
+ if (testRoot === null) {
6230
+ return false;
6231
+ }
6232
+ const previous = previousSibling(node);
6233
+ if (previous === null || previous.type !== import_utils38.AST_NODE_TYPES.ExpressionStatement) {
6234
+ return false;
6235
+ }
6236
+ let matched = false;
6237
+ subtreeMatches2(previous, (current) => {
6238
+ if (current.type !== import_utils38.AST_NODE_TYPES.CallExpression || current.callee.type !== import_utils38.AST_NODE_TYPES.Identifier || current.callee.name !== "expect") {
6239
+ return false;
6240
+ }
6241
+ const subject = current.arguments[0];
6242
+ if (subject === void 0) {
6243
+ return false;
6244
+ }
6245
+ if (rootIdentifier(subject) === testRoot) {
6246
+ matched = true;
6247
+ return true;
6248
+ }
6249
+ return false;
6250
+ });
6251
+ return matched;
6252
+ }
6253
+ function isThrowingGuard(node) {
6254
+ if (node.alternate !== null) {
6255
+ return false;
6256
+ }
6257
+ const statements = branchStatements(node.consequent);
6258
+ return statements.length === 1 && statements[0]?.type === import_utils38.AST_NODE_TYPES.ThrowStatement;
6259
+ }
6260
+ function isTypeAssertionBranch(branch) {
6261
+ const statements = branchStatements(branch);
6262
+ return statements.length > 0 && statements.every(
6263
+ (statement) => statement.type === import_utils38.AST_NODE_TYPES.ExpressionStatement && isTypeAssertionCall(statement.expression)
6264
+ );
6265
+ }
6266
+ function isTypeLevelNarrowing(node) {
6267
+ return isTypeAssertionBranch(node.consequent) && (node.alternate === null || isTypeAssertionBranch(node.alternate));
6268
+ }
6269
+ var isInertBranch = (branch) => !containsAssertion(branch) && !containsEscape(branch) && !containsSkipCall(branch);
6270
+ function isInertNormalization(node) {
6271
+ return isInertBranch(node.consequent) && (node.alternate === null || isInertBranch(node.alternate));
6272
+ }
6273
+ function isExemptIfStatement(node) {
6274
+ return isPinnedNarrowingGuard(node) || isThrowingGuard(node) || isTypeLevelNarrowing(node) || isInertNormalization(node);
6275
+ }
6276
+ function isShortCircuitedAssertion(node) {
6277
+ return node.operator !== "??" && node.parent.type === import_utils38.AST_NODE_TYPES.ExpressionStatement && containsAssertion(node.right);
6278
+ }
5433
6279
  var no_conditional_in_test_default = import_utils38.ESLintUtils.RuleCreator(
5434
6280
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
5435
6281
  )({
@@ -5458,6 +6304,9 @@ var no_conditional_in_test_default = import_utils38.ESLintUtils.RuleCreator(
5458
6304
  };
5459
6305
  return {
5460
6306
  IfStatement(node) {
6307
+ if (isExemptIfStatement(node)) {
6308
+ return;
6309
+ }
5461
6310
  report(node);
5462
6311
  },
5463
6312
  SwitchStatement(node) {
@@ -5467,6 +6316,9 @@ var no_conditional_in_test_default = import_utils38.ESLintUtils.RuleCreator(
5467
6316
  report(node);
5468
6317
  },
5469
6318
  LogicalExpression(node) {
6319
+ if (!isShortCircuitedAssertion(node)) {
6320
+ return;
6321
+ }
5470
6322
  report(node);
5471
6323
  }
5472
6324
  };
@@ -5732,17 +6584,15 @@ var DEFAULT_ALLOW = [
5732
6584
  "[\\\\/]api[\\\\/]",
5733
6585
  "[\\\\/]api\\.[cm]?[jt]sx?$",
5734
6586
  "[-.]api\\.[cm]?[jt]sx?$",
5735
- "\\.test\\.",
5736
- "\\.spec\\.",
5737
- // `*.test-d.ts` type tests, and react-router's `single-fetch-test.ts` spelling.
5738
- "\\.(test|spec)-d\\.[cm]?[jt]sx?$",
5739
- "-(test|spec)\\.[cm]?[jt]sx?$",
5740
- "[\\\\/]__tests__[\\\\/]",
5741
- "[\\\\/]__mocks__[\\\\/]",
5742
- "[\\\\/]tests?[\\\\/]",
5743
- // jscodeshift input/output fixtures — text a codemod transforms, not code.
5744
- "[\\\\/]__testfixtures__[\\\\/]"
6587
+ // Vendor wrapper conventions: a module that exists to own one third-party
6588
+ // origin's HTTP. See the SECOND SWEEP note in the file header.
6589
+ "[\\\\/]services?[\\\\/]",
6590
+ "[\\\\/](connectors|providers|integrations|adapters|fetchers)[\\\\/]",
6591
+ "[\\\\/]notifications[\\\\/]",
6592
+ "[Ss]ervice\\.[cm]?[jt]sx?$",
6593
+ "-(service|connector|adapter|sdk|fetcher)\\.[cm]?[jt]sx?$"
5745
6594
  ];
6595
+ var NON_PRODUCTION_TREE_RE = /[\\/](playwright|cypress|__testfixtures__)[\\/]/;
5746
6596
  var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
5747
6597
  "globalThis",
5748
6598
  "window",
@@ -5768,6 +6618,10 @@ function identifierLikeName(node) {
5768
6618
  }
5769
6619
  return null;
5770
6620
  }
6621
+ function isConstructedArgumentHandoff(node) {
6622
+ const [first] = node.arguments;
6623
+ return node.arguments.length === 1 && first !== void 0 && first.type === import_utils42.AST_NODE_TYPES.NewExpression;
6624
+ }
5771
6625
  function isPresignedUrlTransfer(node) {
5772
6626
  const first = node.arguments[0];
5773
6627
  if (first === void 0) {
@@ -5814,15 +6668,18 @@ var no_raw_fetch_outside_clients_default = import_utils42.ESLintUtils.RuleCreato
5814
6668
  },
5815
6669
  defaultOptions: [{}],
5816
6670
  create(context, [options]) {
6671
+ const filename = context.filename;
6672
+ if (isTestFile(filename) || NON_PRODUCTION_TREE_RE.test(filename)) {
6673
+ return {};
6674
+ }
5817
6675
  const patterns = options?.allow ?? DEFAULT_ALLOW;
5818
6676
  const allowed = compile(patterns);
5819
- const filename = context.filename;
5820
6677
  if (allowed.some((re) => re.test(filename))) {
5821
6678
  return {};
5822
6679
  }
5823
6680
  return {
5824
6681
  CallExpression(node) {
5825
- if (isPresignedUrlTransfer(node)) {
6682
+ if (isPresignedUrlTransfer(node) || isConstructedArgumentHandoff(node)) {
5826
6683
  return;
5827
6684
  }
5828
6685
  if (isGlobalFetchCall(node)) {
@@ -6109,13 +6966,6 @@ var IGNORE_PATTERNS3 = [
6109
6966
  /\.generated\.tsx?$/,
6110
6967
  /\.d\.ts$/
6111
6968
  ];
6112
- var TEST_FILE_PATTERNS = [
6113
- /\.(?:test|spec)\.[cm]?[jt]sx?$/,
6114
- /[\\/]__tests__[\\/]/,
6115
- /[\\/]__mocks__[\\/]/,
6116
- /[\\/]tests?[\\/]/,
6117
- /\.stories\.[cm]?[jt]sx?$/
6118
- ];
6119
6969
  var MUTATING_METHODS = /* @__PURE__ */ new Set([
6120
6970
  // Array
6121
6971
  "push",
@@ -6135,7 +6985,7 @@ var MUTATING_METHODS = /* @__PURE__ */ new Set([
6135
6985
  // Object-ish escape hatches
6136
6986
  "assign"
6137
6987
  ]);
6138
- var FUNCTION_TYPES4 = /* @__PURE__ */ new Set([
6988
+ var FUNCTION_TYPES5 = /* @__PURE__ */ new Set([
6139
6989
  import_utils45.AST_NODE_TYPES.FunctionDeclaration,
6140
6990
  import_utils45.AST_NODE_TYPES.FunctionExpression,
6141
6991
  import_utils45.AST_NODE_TYPES.ArrowFunctionExpression
@@ -6147,8 +6997,8 @@ function isIgnoredFile3(filename, sourceText) {
6147
6997
  }
6148
6998
  return /@generated\b/.test(sourceText.slice(0, 1024));
6149
6999
  }
6150
- function isTestFile2(filename) {
6151
- return TEST_FILE_PATTERNS.some((re) => re.test(filename));
7000
+ function isLocalFixtureFile(filename) {
7001
+ return isTestFile(filename) || isStoryFile(filename);
6152
7002
  }
6153
7003
  function unwrap4(node) {
6154
7004
  if (node.type === import_utils45.AST_NODE_TYPES.TSAsExpression || node.type === import_utils45.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils45.AST_NODE_TYPES.TSNonNullExpression) {
@@ -6156,6 +7006,7 @@ function unwrap4(node) {
6156
7006
  }
6157
7007
  return node;
6158
7008
  }
7009
+ var HAS_STATEFUL_FLAG_RE = /[gy]/;
6159
7010
  function isRegexLiteral(node) {
6160
7011
  return node.type === import_utils45.AST_NODE_TYPES.Literal && "regex" in node && node.regex !== void 0;
6161
7012
  }
@@ -6166,7 +7017,7 @@ function isLiteralOnly(node, depth) {
6166
7017
  const inner = unwrap4(node);
6167
7018
  switch (inner.type) {
6168
7019
  case import_utils45.AST_NODE_TYPES.Literal: {
6169
- return true;
7020
+ return !(isRegexLiteral(inner) && HAS_STATEFUL_FLAG_RE.test(inner.regex.flags));
6170
7021
  }
6171
7022
  case import_utils45.AST_NODE_TYPES.TemplateLiteral: {
6172
7023
  return inner.expressions.length === 0;
@@ -6211,7 +7062,7 @@ function classify(init, checkRegex) {
6211
7062
  if (!checkRegex) {
6212
7063
  return null;
6213
7064
  }
6214
- if (/[gy]/.test(node.regex.flags)) {
7065
+ if (HAS_STATEFUL_FLAG_RE.test(node.regex.flags)) {
6215
7066
  return null;
6216
7067
  }
6217
7068
  return { kind: "regex", size: 1 };
@@ -6238,7 +7089,7 @@ function classify(init, checkRegex) {
6238
7089
  function enclosingFunction2(node) {
6239
7090
  let current = node.parent;
6240
7091
  while (current !== void 0 && current !== null) {
6241
- if (FUNCTION_TYPES4.has(current.type)) {
7092
+ if (FUNCTION_TYPES5.has(current.type)) {
6242
7093
  return current;
6243
7094
  }
6244
7095
  current = current.parent;
@@ -6346,7 +7197,7 @@ var prefer_module_level_constant_default = import_utils45.ESLintUtils.RuleCreato
6346
7197
  if (isIgnoredFile3(filename, sourceCode.getText())) {
6347
7198
  return {};
6348
7199
  }
6349
- if (ignoreTestFiles && isTestFile2(filename)) {
7200
+ if (ignoreTestFiles && isLocalFixtureFile(filename)) {
6350
7201
  return {};
6351
7202
  }
6352
7203
  function allReferencesAreSafeReads(declarator) {
@@ -7177,174 +8028,217 @@ var prefer_non_nullable_collection_default = import_utils51.ESLintUtils.RuleCrea
7177
8028
  }
7178
8029
  });
7179
8030
 
7180
- // src/rules/no-implicit-attribute-access.ts
8031
+ // src/rules/strict-test-assertions.ts
7181
8032
  var import_utils52 = require("@typescript-eslint/utils");
7182
- var EXCLUDED_BASES = /* @__PURE__ */ new Set([
7183
- "environ",
7184
- "headers",
7185
- "cookies",
7186
- "session",
7187
- "redis",
7188
- "cache",
7189
- "state",
7190
- "config",
7191
- "env",
7192
- "process",
7193
- "localStorage",
7194
- "sessionStorage"
7195
- ]);
7196
- function getBaseName(node) {
7197
- if (node.type === import_utils52.AST_NODE_TYPES.Identifier) {
7198
- return node.name;
8033
+ var MERGEABLE_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
8034
+ var ARRAY_MATCHERS = /* @__PURE__ */ new Set(["toEqual", "toStrictEqual"]);
8035
+ var COLLECTION_PROPERTIES = /* @__PURE__ */ new Set(["length", "size"]);
8036
+ var NUMERIC_SIGNS2 = /* @__PURE__ */ new Set(["-", "+"]);
8037
+ var MIN_RUN_LENGTH = 2;
8038
+ function literalText(node, getText) {
8039
+ switch (node.type) {
8040
+ case import_utils52.AST_NODE_TYPES.Literal:
8041
+ return "regex" in node ? null : getText(node);
8042
+ case import_utils52.AST_NODE_TYPES.TemplateLiteral:
8043
+ return node.expressions.length === 0 ? getText(node) : null;
8044
+ case import_utils52.AST_NODE_TYPES.UnaryExpression:
8045
+ return NUMERIC_SIGNS2.has(node.operator) && literalText(node.argument, getText) !== null ? getText(node) : null;
8046
+ default:
8047
+ return null;
7199
8048
  }
7200
- if (node.type === import_utils52.AST_NODE_TYPES.MemberExpression && node.property.type === import_utils52.AST_NODE_TYPES.Identifier) {
7201
- return node.property.name;
8049
+ }
8050
+ function isPureReceiver(node) {
8051
+ switch (node.type) {
8052
+ case import_utils52.AST_NODE_TYPES.Identifier:
8053
+ case import_utils52.AST_NODE_TYPES.ThisExpression:
8054
+ return true;
8055
+ case import_utils52.AST_NODE_TYPES.MemberExpression:
8056
+ if (node.optional) {
8057
+ return false;
8058
+ }
8059
+ if (node.computed) {
8060
+ return node.property.type === import_utils52.AST_NODE_TYPES.Literal && isPureReceiver(node.object);
8061
+ }
8062
+ return isPureReceiver(node.object);
8063
+ default:
8064
+ return false;
7202
8065
  }
7203
- return null;
7204
8066
  }
7205
- var no_implicit_attribute_access_default = import_utils52.ESLintUtils.RuleCreator(
8067
+ function literalIndex(node) {
8068
+ if (node.type !== import_utils52.AST_NODE_TYPES.Literal || typeof node.value !== "number") {
8069
+ return null;
8070
+ }
8071
+ return Number.isInteger(node.value) && node.value >= 0 ? node.value : null;
8072
+ }
8073
+ var strict_test_assertions_default = import_utils52.ESLintUtils.RuleCreator(
7206
8074
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7207
8075
  )({
7208
- name: "no-implicit-attribute-access",
7209
- meta: {
7210
- type: "problem",
7211
- docs: {
7212
- description: "Disallow imperative dictionary access with string literals; use declarative Zod schemas."
7213
- },
7214
- schema: [],
7215
- messages: {
7216
- noImplicitAttributeAccess: "Imperative lookup for '{{key}}' \u2014 if the key is known, parse the object declaratively with a Zod schema instead."
7217
- }
7218
- },
7219
- defaultOptions: [],
7220
- create(context) {
7221
- return {
7222
- MemberExpression(node) {
7223
- if (!node.computed) {
7224
- return;
7225
- }
7226
- if (node.property.type === import_utils52.AST_NODE_TYPES.Literal && typeof node.property.value === "string") {
7227
- const baseName = getBaseName(node.object);
7228
- if (!baseName || !EXCLUDED_BASES.has(baseName)) {
7229
- context.report({
7230
- node,
7231
- messageId: "noImplicitAttributeAccess",
7232
- data: { key: node.property.value }
7233
- });
7234
- }
7235
- }
7236
- },
7237
- CallExpression(node) {
7238
- const callee = node.callee;
7239
- if (callee.type === import_utils52.AST_NODE_TYPES.MemberExpression) {
7240
- const method = callee.property.type === import_utils52.AST_NODE_TYPES.Identifier ? callee.property.name : null;
7241
- if (method === "get") {
7242
- const arg = node.arguments[0];
7243
- if (arg && arg.type === import_utils52.AST_NODE_TYPES.Literal && typeof arg.value === "string") {
7244
- const baseName = getBaseName(callee.object);
7245
- if (!baseName || !EXCLUDED_BASES.has(baseName)) {
7246
- context.report({
7247
- node,
7248
- messageId: "noImplicitAttributeAccess",
7249
- data: { key: arg.value }
7250
- });
7251
- }
7252
- }
7253
- }
7254
- }
7255
- }
7256
- };
7257
- }
7258
- });
7259
-
7260
- // src/rules/strict-test-assertions.ts
7261
- var import_utils53 = require("@typescript-eslint/utils");
7262
- var strict_test_assertions_default = import_utils53.ESLintUtils.RuleCreator.withoutDocs({
8076
+ name: "strict-test-assertions",
7263
8077
  meta: {
7264
8078
  type: "suggestion",
7265
8079
  docs: {
7266
- description: "Replace multiple sequential assertions with a single toMatchObject or toStrictEqual"
8080
+ description: "Collapse a run of consecutive assertions on the same object into one assertion about the whole object, so every mismatch is reported and nothing outside the asserted keys goes unchecked."
7267
8081
  },
7268
8082
  fixable: "code",
7269
8083
  messages: {
7270
- combineAssertions: "Combine multiple sequential assertions on the same object into a single toMatchObject or toStrictEqual"
8084
+ combineAssertions: "These {{count}} assertions each check one property of `{{receiver}}` against a literal, so the first mismatch hides the rest. Assert the object once: `expect({{receiver}}).toMatchObject({ \u2026 })`.",
8085
+ assertArrayOnce: "These {{count}} assertions check `{{receiver}}[0]`\u2026`{{receiver}}[{{last}}]` one at a time, which never checks how long `{{receiver}}` is \u2014 extra elements pass unnoticed. Assert the array once: `expect({{receiver}}).{{matcher}}([ \u2026 ])`."
7271
8086
  },
7272
8087
  schema: []
7273
8088
  },
7274
8089
  defaultOptions: [],
7275
8090
  create(context) {
7276
- function checkBody(body) {
7277
- let currentObject = null;
7278
- let sequence = [];
7279
- function getExpectObject(expr) {
7280
- if (expr.type === import_utils53.AST_NODE_TYPES.CallExpression && expr.callee.type === import_utils53.AST_NODE_TYPES.MemberExpression && expr.callee.object.type === import_utils53.AST_NODE_TYPES.CallExpression && expr.callee.object.callee.type === import_utils53.AST_NODE_TYPES.Identifier && expr.callee.object.callee.name === "expect" && expr.callee.object.arguments.length === 1) {
7281
- const arg = expr.callee.object.arguments.at(0);
7282
- if (arg?.type === import_utils53.AST_NODE_TYPES.MemberExpression) {
7283
- return arg.object;
7284
- }
8091
+ if (!isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
8092
+ return {};
8093
+ }
8094
+ const { sourceCode } = context;
8095
+ function parseAssertion(statement) {
8096
+ if (statement.type !== import_utils52.AST_NODE_TYPES.ExpressionStatement) {
8097
+ return null;
8098
+ }
8099
+ const call = statement.expression;
8100
+ if (call.type !== import_utils52.AST_NODE_TYPES.CallExpression) {
8101
+ return null;
8102
+ }
8103
+ const callee = call.callee;
8104
+ if (callee.type !== import_utils52.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils52.AST_NODE_TYPES.Identifier) {
8105
+ return null;
8106
+ }
8107
+ const matcher = callee.property.name;
8108
+ const expectCall = callee.object;
8109
+ if (expectCall.type !== import_utils52.AST_NODE_TYPES.CallExpression || expectCall.callee.type !== import_utils52.AST_NODE_TYPES.Identifier || expectCall.callee.name !== "expect" || expectCall.arguments.length !== 1) {
8110
+ return null;
8111
+ }
8112
+ const actual = expectCall.arguments[0];
8113
+ if (actual === void 0 || actual.type !== import_utils52.AST_NODE_TYPES.MemberExpression || actual.optional) {
8114
+ return null;
8115
+ }
8116
+ if (!isPureReceiver(actual.object)) {
8117
+ return null;
8118
+ }
8119
+ let key;
8120
+ if (actual.computed) {
8121
+ const index = literalIndex(actual.property);
8122
+ if (index === null) {
8123
+ return null;
8124
+ }
8125
+ key = { kind: "index", index };
8126
+ } else {
8127
+ if (actual.property.type !== import_utils52.AST_NODE_TYPES.Identifier || COLLECTION_PROPERTIES.has(actual.property.name)) {
8128
+ return null;
7285
8129
  }
8130
+ key = { kind: "property", name: actual.property.name };
8131
+ }
8132
+ if (matcher === "toBeNull" && call.arguments.length === 0) {
8133
+ return { statement, receiver: actual.object, key, matcher, expectedText: "null", expectedIsLiteral: true };
8134
+ }
8135
+ if (!MERGEABLE_MATCHERS.has(matcher)) {
7286
8136
  return null;
7287
8137
  }
7288
- function reportSequence() {
7289
- const first = sequence.at(0);
7290
- if (sequence.length > 1 && first) {
7291
- context.report({
7292
- node: first,
7293
- messageId: "combineAssertions",
7294
- fix(fixer) {
7295
- if (!currentObject) return null;
7296
- const objectText = context.sourceCode.getText(currentObject);
7297
- const props = sequence.map((stmt) => {
7298
- if (stmt.expression.type !== import_utils53.AST_NODE_TYPES.CallExpression || stmt.expression.callee.type !== import_utils53.AST_NODE_TYPES.MemberExpression || stmt.expression.callee.object.type !== import_utils53.AST_NODE_TYPES.CallExpression) {
7299
- return null;
7300
- }
7301
- const actual = stmt.expression.callee.object.arguments.at(0);
7302
- const expected = stmt.expression.arguments.at(0);
7303
- if (actual?.type === import_utils53.AST_NODE_TYPES.MemberExpression && actual.property.type === import_utils53.AST_NODE_TYPES.Identifier && expected) {
7304
- const propName2 = actual.property.name;
7305
- const valText = context.sourceCode.getText(expected);
7306
- return `${propName2}: ${valText}`;
7307
- }
7308
- return null;
7309
- });
7310
- if (props.some((p) => p === null)) return null;
7311
- const replacement = `expect(${objectText}).toMatchObject({ ${props.join(", ")} });`;
7312
- return [
7313
- fixer.replaceText(first, replacement),
7314
- ...sequence.slice(1).map((stmt) => fixer.remove(stmt))
7315
- ];
7316
- }
7317
- });
8138
+ const expected = call.arguments[0];
8139
+ if (call.arguments.length !== 1 || expected === void 0 || expected.type === import_utils52.AST_NODE_TYPES.SpreadElement) {
8140
+ return null;
8141
+ }
8142
+ const literal = literalText(expected, (node) => sourceCode.getText(node));
8143
+ return {
8144
+ statement,
8145
+ receiver: actual.object,
8146
+ key,
8147
+ matcher,
8148
+ expectedText: literal ?? sourceCode.getText(expected),
8149
+ expectedIsLiteral: literal !== null
8150
+ };
8151
+ }
8152
+ function reportPropertyRun(run) {
8153
+ const names = /* @__PURE__ */ new Set();
8154
+ for (const assertion of run) {
8155
+ if (assertion.key.kind !== "property" || !assertion.expectedIsLiteral) {
8156
+ return;
7318
8157
  }
8158
+ if (!MERGEABLE_MATCHERS.has(assertion.matcher) && assertion.matcher !== "toBeNull") {
8159
+ return;
8160
+ }
8161
+ if (names.has(assertion.key.name)) {
8162
+ return;
8163
+ }
8164
+ names.add(assertion.key.name);
7319
8165
  }
7320
- for (const statement of body) {
7321
- if (statement.type === import_utils53.AST_NODE_TYPES.ExpressionStatement) {
7322
- const obj = getExpectObject(statement.expression);
7323
- if (obj && currentObject && context.sourceCode.getText(obj) === context.sourceCode.getText(currentObject)) {
7324
- sequence.push(statement);
8166
+ const first = run[0];
8167
+ if (first === void 0) {
8168
+ return;
8169
+ }
8170
+ const receiverText = sourceCode.getText(first.receiver);
8171
+ const properties = run.map(
8172
+ (assertion) => assertion.key.kind === "property" ? `${assertion.key.name}: ${assertion.expectedText}` : ""
8173
+ ).join(", ");
8174
+ context.report({
8175
+ node: first.statement,
8176
+ messageId: "combineAssertions",
8177
+ data: { count: String(run.length), receiver: receiverText },
8178
+ fix: (fixer) => [
8179
+ fixer.replaceText(first.statement, `expect(${receiverText}).toMatchObject({ ${properties} });`),
8180
+ ...run.slice(1).map((assertion) => fixer.remove(assertion.statement))
8181
+ ]
8182
+ });
8183
+ }
8184
+ function reportIndexRun(run) {
8185
+ const first = run[0];
8186
+ if (first === void 0 || !ARRAY_MATCHERS.has(first.matcher)) {
8187
+ return;
8188
+ }
8189
+ const indices = /* @__PURE__ */ new Set();
8190
+ for (const assertion of run) {
8191
+ if (assertion.key.kind !== "index" || assertion.matcher !== first.matcher) {
8192
+ return;
8193
+ }
8194
+ indices.add(assertion.key.index);
8195
+ }
8196
+ if (indices.size !== run.length || Math.max(...indices) !== run.length - 1) {
8197
+ return;
8198
+ }
8199
+ context.report({
8200
+ node: first.statement,
8201
+ messageId: "assertArrayOnce",
8202
+ data: {
8203
+ count: String(run.length),
8204
+ receiver: sourceCode.getText(first.receiver),
8205
+ last: String(run.length - 1),
8206
+ matcher: first.matcher
8207
+ }
8208
+ });
8209
+ }
8210
+ function checkBody(body) {
8211
+ let run = [];
8212
+ const flush = () => {
8213
+ const first = run[0];
8214
+ if (first !== void 0 && run.length >= MIN_RUN_LENGTH) {
8215
+ if (first.key.kind === "property") {
8216
+ reportPropertyRun(run);
7325
8217
  } else {
7326
- reportSequence();
7327
- if (obj) {
7328
- currentObject = obj;
7329
- sequence = [statement];
7330
- } else {
7331
- currentObject = null;
7332
- sequence = [];
7333
- }
8218
+ reportIndexRun(run);
7334
8219
  }
7335
- } else {
7336
- reportSequence();
7337
- currentObject = null;
7338
- sequence = [];
8220
+ }
8221
+ run = [];
8222
+ };
8223
+ for (const statement of body) {
8224
+ const assertion = parseAssertion(statement);
8225
+ const previous = run.at(-1);
8226
+ if (assertion !== null && previous !== void 0 && previous.key.kind === assertion.key.kind && sourceCode.getText(previous.receiver) === sourceCode.getText(assertion.receiver)) {
8227
+ run.push(assertion);
8228
+ continue;
8229
+ }
8230
+ flush();
8231
+ if (assertion !== null) {
8232
+ run = [assertion];
7339
8233
  }
7340
8234
  }
7341
- reportSequence();
8235
+ flush();
7342
8236
  }
7343
8237
  return {
7344
- BlockStatement(node) {
8238
+ BlockStatement: (node) => {
7345
8239
  checkBody(node.body);
7346
8240
  },
7347
- Program(node) {
8241
+ Program: (node) => {
7348
8242
  checkBody(node.body);
7349
8243
  }
7350
8244
  };
@@ -7352,8 +8246,8 @@ var strict_test_assertions_default = import_utils53.ESLintUtils.RuleCreator.with
7352
8246
  });
7353
8247
 
7354
8248
  // src/rules/no-async-callback-in-waitfor.ts
7355
- var import_utils54 = require("@typescript-eslint/utils");
7356
- var no_async_callback_in_waitfor_default = import_utils54.ESLintUtils.RuleCreator(
8249
+ var import_utils53 = require("@typescript-eslint/utils");
8250
+ var no_async_callback_in_waitfor_default = import_utils53.ESLintUtils.RuleCreator(
7357
8251
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7358
8252
  )({
7359
8253
  name: "no-async-callback-in-waitfor",
@@ -7374,9 +8268,9 @@ var no_async_callback_in_waitfor_default = import_utils54.ESLintUtils.RuleCreato
7374
8268
  }
7375
8269
  return {
7376
8270
  CallExpression(node) {
7377
- if (node.callee.type === import_utils54.AST_NODE_TYPES.Identifier && node.callee.name === "waitFor") {
8271
+ if (node.callee.type === import_utils53.AST_NODE_TYPES.Identifier && node.callee.name === "waitFor") {
7378
8272
  const callback = node.arguments[0];
7379
- if (callback && (callback.type === import_utils54.AST_NODE_TYPES.ArrowFunctionExpression || callback.type === import_utils54.AST_NODE_TYPES.FunctionExpression) && callback.async) {
8273
+ if (callback && (callback.type === import_utils53.AST_NODE_TYPES.ArrowFunctionExpression || callback.type === import_utils53.AST_NODE_TYPES.FunctionExpression) && callback.async) {
7380
8274
  context.report({
7381
8275
  node: callback,
7382
8276
  messageId: "noAsyncCallbackInWaitFor"
@@ -7389,7 +8283,7 @@ var no_async_callback_in_waitfor_default = import_utils54.ESLintUtils.RuleCreato
7389
8283
  });
7390
8284
 
7391
8285
  // src/rules/no-hand-rolled-sleep.ts
7392
- var import_utils55 = require("@typescript-eslint/utils");
8286
+ var import_utils54 = require("@typescript-eslint/utils");
7393
8287
  var GLOBAL_OBJECTS2 = /* @__PURE__ */ new Set([
7394
8288
  "globalThis",
7395
8289
  "window",
@@ -7408,66 +8302,66 @@ function matchesAnyPattern3(filename, patterns) {
7408
8302
  return false;
7409
8303
  }
7410
8304
  function isSetTimeoutCallee(callee) {
7411
- if (callee.type === import_utils55.AST_NODE_TYPES.Identifier) {
8305
+ if (callee.type === import_utils54.AST_NODE_TYPES.Identifier) {
7412
8306
  return callee.name === "setTimeout";
7413
8307
  }
7414
- return callee.type === import_utils55.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils55.AST_NODE_TYPES.Identifier && callee.property.name === "setTimeout" && callee.object.type === import_utils55.AST_NODE_TYPES.Identifier && GLOBAL_OBJECTS2.has(callee.object.name);
8308
+ return callee.type === import_utils54.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils54.AST_NODE_TYPES.Identifier && callee.property.name === "setTimeout" && callee.object.type === import_utils54.AST_NODE_TYPES.Identifier && GLOBAL_OBJECTS2.has(callee.object.name);
7415
8309
  }
7416
8310
  function soleCall(fn) {
7417
- if (fn.body.type !== import_utils55.AST_NODE_TYPES.BlockStatement) {
7418
- return fn.body.type === import_utils55.AST_NODE_TYPES.CallExpression ? fn.body : null;
8311
+ if (fn.body.type !== import_utils54.AST_NODE_TYPES.BlockStatement) {
8312
+ return fn.body.type === import_utils54.AST_NODE_TYPES.CallExpression ? fn.body : null;
7419
8313
  }
7420
8314
  if (fn.body.body.length !== 1) {
7421
8315
  return null;
7422
8316
  }
7423
8317
  const [only] = fn.body.body;
7424
- if (only?.type !== import_utils55.AST_NODE_TYPES.ExpressionStatement) {
8318
+ if (only?.type !== import_utils54.AST_NODE_TYPES.ExpressionStatement) {
7425
8319
  return null;
7426
8320
  }
7427
- return only.expression.type === import_utils55.AST_NODE_TYPES.CallExpression ? only.expression : null;
8321
+ return only.expression.type === import_utils54.AST_NODE_TYPES.CallExpression ? only.expression : null;
7428
8322
  }
7429
8323
  function isTimedDelay(delay) {
7430
8324
  if (delay === void 0) {
7431
8325
  return false;
7432
8326
  }
7433
- if (delay.type === import_utils55.AST_NODE_TYPES.Literal && typeof delay.value === "number") {
8327
+ if (delay.type === import_utils54.AST_NODE_TYPES.Literal && typeof delay.value === "number") {
7434
8328
  return delay.value !== 0;
7435
8329
  }
7436
8330
  return true;
7437
8331
  }
7438
8332
  function settlesWithoutValue(callback, name) {
7439
- if (callback.type === import_utils55.AST_NODE_TYPES.Identifier) {
8333
+ if (callback.type === import_utils54.AST_NODE_TYPES.Identifier) {
7440
8334
  return callback.name === name;
7441
8335
  }
7442
- if (callback.type !== import_utils55.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils55.AST_NODE_TYPES.FunctionExpression) {
8336
+ if (callback.type !== import_utils54.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils54.AST_NODE_TYPES.FunctionExpression) {
7443
8337
  return false;
7444
8338
  }
7445
8339
  const call = soleCall(callback);
7446
- return call !== null && call.arguments.length === 0 && call.callee.type === import_utils55.AST_NODE_TYPES.Identifier && call.callee.name === name;
8340
+ return call !== null && call.arguments.length === 0 && call.callee.type === import_utils54.AST_NODE_TYPES.Identifier && call.callee.name === name;
7447
8341
  }
7448
8342
  function rejectsInCallback(callback, name) {
7449
- if (callback.type === import_utils55.AST_NODE_TYPES.Identifier) {
8343
+ if (callback.type === import_utils54.AST_NODE_TYPES.Identifier) {
7450
8344
  return callback.name === name;
7451
8345
  }
7452
- if (callback.type !== import_utils55.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils55.AST_NODE_TYPES.FunctionExpression) {
8346
+ if (callback.type !== import_utils54.AST_NODE_TYPES.ArrowFunctionExpression && callback.type !== import_utils54.AST_NODE_TYPES.FunctionExpression) {
7453
8347
  return false;
7454
8348
  }
7455
8349
  const call = soleCall(callback);
7456
- return call !== null && call.callee.type === import_utils55.AST_NODE_TYPES.Identifier && call.callee.name === name;
8350
+ return call !== null && call.callee.type === import_utils54.AST_NODE_TYPES.Identifier && call.callee.name === name;
7457
8351
  }
7458
8352
  function parameterName(fn, index) {
7459
8353
  const parameter = fn.params[index];
7460
- return parameter?.type === import_utils55.AST_NODE_TYPES.Identifier ? parameter.name : null;
8354
+ return parameter?.type === import_utils54.AST_NODE_TYPES.Identifier ? parameter.name : null;
7461
8355
  }
7462
8356
  function isRaceArm(node) {
7463
8357
  const array = node.parent;
7464
- if (array?.type !== import_utils55.AST_NODE_TYPES.ArrayExpression) {
8358
+ if (array?.type !== import_utils54.AST_NODE_TYPES.ArrayExpression) {
7465
8359
  return false;
7466
8360
  }
7467
8361
  const call = array.parent;
7468
- return call?.type === import_utils55.AST_NODE_TYPES.CallExpression && call.arguments[0] === array && call.callee.type === import_utils55.AST_NODE_TYPES.MemberExpression && !call.callee.computed && call.callee.object.type === import_utils55.AST_NODE_TYPES.Identifier && call.callee.object.name === "Promise" && call.callee.property.type === import_utils55.AST_NODE_TYPES.Identifier && RACE_METHODS.has(call.callee.property.name);
8362
+ return call?.type === import_utils54.AST_NODE_TYPES.CallExpression && call.arguments[0] === array && call.callee.type === import_utils54.AST_NODE_TYPES.MemberExpression && !call.callee.computed && call.callee.object.type === import_utils54.AST_NODE_TYPES.Identifier && call.callee.object.name === "Promise" && call.callee.property.type === import_utils54.AST_NODE_TYPES.Identifier && RACE_METHODS.has(call.callee.property.name);
7469
8363
  }
7470
- var no_hand_rolled_sleep_default = import_utils55.ESLintUtils.RuleCreator(
8364
+ var no_hand_rolled_sleep_default = import_utils54.ESLintUtils.RuleCreator(
7471
8365
  (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7472
8366
  )({
7473
8367
  name: "no-hand-rolled-sleep",
@@ -7515,10 +8409,10 @@ var no_hand_rolled_sleep_default = import_utils55.ESLintUtils.RuleCreator(
7515
8409
  }
7516
8410
  const program = sourceCode.ast;
7517
8411
  for (const statement of program.body) {
7518
- if (statement.type === import_utils55.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils55.AST_NODE_TYPES.Literal && statement.expression.value === "use client") {
8412
+ if (statement.type === import_utils54.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils54.AST_NODE_TYPES.Literal && statement.expression.value === "use client") {
7519
8413
  return true;
7520
8414
  }
7521
- if (statement.type === import_utils55.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && CLIENT_ONLY_MODULES.test(statement.source.value)) {
8415
+ if (statement.type === import_utils54.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && CLIENT_ONLY_MODULES.test(statement.source.value)) {
7522
8416
  return true;
7523
8417
  }
7524
8418
  }
@@ -7534,11 +8428,11 @@ var no_hand_rolled_sleep_default = import_utils55.ESLintUtils.RuleCreator(
7534
8428
  };
7535
8429
  return {
7536
8430
  NewExpression(node) {
7537
- if (node.callee.type !== import_utils55.AST_NODE_TYPES.Identifier || node.callee.name !== "Promise") {
8431
+ if (node.callee.type !== import_utils54.AST_NODE_TYPES.Identifier || node.callee.name !== "Promise") {
7538
8432
  return;
7539
8433
  }
7540
8434
  const executor = node.arguments[0];
7541
- if (executor?.type !== import_utils55.AST_NODE_TYPES.ArrowFunctionExpression && executor?.type !== import_utils55.AST_NODE_TYPES.FunctionExpression) {
8435
+ if (executor?.type !== import_utils54.AST_NODE_TYPES.ArrowFunctionExpression && executor?.type !== import_utils54.AST_NODE_TYPES.FunctionExpression) {
7542
8436
  return;
7543
8437
  }
7544
8438
  const call = soleCall(executor);
@@ -7565,43 +8459,8 @@ var no_hand_rolled_sleep_default = import_utils55.ESLintUtils.RuleCreator(
7565
8459
  }
7566
8460
  });
7567
8461
 
7568
- // src/rules/prefer-setup-file-mocks.ts
7569
- var import_utils56 = require("@typescript-eslint/utils");
7570
- var prefer_setup_file_mocks_default = import_utils56.ESLintUtils.RuleCreator(
7571
- (name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
7572
- )({
7573
- name: "prefer-setup-file-mocks",
7574
- meta: {
7575
- type: "suggestion",
7576
- docs: {
7577
- description: "Prefer defining module mocks in setup files rather than using vi.mock or jest.mock inline in test files."
7578
- },
7579
- schema: [],
7580
- messages: {
7581
- preferSetupFileMocks: "Define module mocks in a setup file (e.g. vitest.setup.ts) instead of using vi.mock or jest.mock directly in test files."
7582
- }
7583
- },
7584
- defaultOptions: [],
7585
- create(context) {
7586
- if (!isTestFile(context.filename)) {
7587
- return {};
7588
- }
7589
- return {
7590
- CallExpression(node) {
7591
- if (node.callee.type === import_utils56.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils56.AST_NODE_TYPES.Identifier && (node.callee.object.name === "vi" || node.callee.object.name === "jest") && node.callee.property.type === import_utils56.AST_NODE_TYPES.Identifier && node.callee.property.name === "mock") {
7592
- context.report({
7593
- node,
7594
- messageId: "preferSetupFileMocks"
7595
- });
7596
- }
7597
- }
7598
- };
7599
- }
7600
- });
7601
-
7602
8462
  // src/index.ts
7603
8463
  var rules = {
7604
- "ban-loose-type-guards-in-tests": ban_loose_type_guards_in_tests_default,
7605
8464
  "enforce-file-structure": enforce_file_structure_default,
7606
8465
  "no-client-side-data-fetching": no_client_side_data_fetching_default,
7607
8466
  "no-comment-cruft": no_comment_cruft_default,
@@ -7626,6 +8485,7 @@ var rules = {
7626
8485
  "no-unsafe-mock-casting": no_unsafe_mock_casting_default,
7627
8486
  "prefer-string-literal-union": prefer_string_literal_union_default,
7628
8487
  "prefer-zod-enum": prefer_zod_enum_default,
8488
+ "prefer-zod-infer": prefer_zod_infer_default,
7629
8489
  "no-silent-promise-catch": no_silent_promise_catch_default,
7630
8490
  "require-fetch-timeout": require_fetch_timeout_default,
7631
8491
  "no-offset-pagination": no_offset_pagination_default,
@@ -7649,14 +8509,12 @@ var rules = {
7649
8509
  "require-interface-for-injected-service": require_interface_for_injected_service_default,
7650
8510
  "strict-test-assertions": strict_test_assertions_default,
7651
8511
  "prefer-non-nullable-collection": prefer_non_nullable_collection_default,
7652
- "no-implicit-attribute-access": no_implicit_attribute_access_default,
7653
- "no-async-callback-in-waitfor": no_async_callback_in_waitfor_default,
7654
- "prefer-setup-file-mocks": prefer_setup_file_mocks_default
8512
+ "no-async-callback-in-waitfor": no_async_callback_in_waitfor_default
7655
8513
  };
7656
8514
  var plugin = {
7657
8515
  meta: {
7658
8516
  name: "@sarj/eslint-plugin",
7659
- version: "4.1.0"
8517
+ version: "5.0.0"
7660
8518
  },
7661
8519
  rules,
7662
8520
  configs: {
@@ -7666,7 +8524,6 @@ var plugin = {
7666
8524
  "@sarj/zod-naming-convention": "warn",
7667
8525
  "@sarj/require-assert-never": "error",
7668
8526
  "@sarj/require-zod-form-validation": "error",
7669
- "@sarj/ban-loose-type-guards-in-tests": "error",
7670
8527
  "@sarj/enforce-file-structure": "warn",
7671
8528
  "@sarj/no-client-side-data-fetching": "warn",
7672
8529
  "@sarj/prefer-server-actions": "warn",
@@ -7692,6 +8549,11 @@ var plugin = {
7692
8549
  "@sarj/no-unsafe-mock-casting": "warn",
7693
8550
  "@sarj/prefer-string-literal-union": "warn",
7694
8551
  "@sarj/prefer-zod-enum": "warn",
8552
+ // A type hand-written beside the Zod schema it restates drifts the
8553
+ // moment the schema gains a field. 30,759-file, 17-repo sweep
8554
+ // (2026-07): 5 reports, 5 true positives, all in public repos.
8555
+ // `requireIdenticalShape: false` widens it to 8 reports, 1 of them noise.
8556
+ "@sarj/prefer-zod-infer": "warn",
7695
8557
  // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
7696
8558
  "@sarj/require-fetch-timeout": "warn",
7697
8559
  "@sarj/no-silent-promise-catch": "warn",
@@ -7746,9 +8608,7 @@ var plugin = {
7746
8608
  // port, 29 fire, 28 of them true positives.
7747
8609
  "@sarj/require-interface-for-injected-service": "warn",
7748
8610
  "@sarj/prefer-non-nullable-collection": "warn",
7749
- "@sarj/no-implicit-attribute-access": "warn",
7750
- "@sarj/no-async-callback-in-waitfor": "warn",
7751
- "@sarj/prefer-setup-file-mocks": "warn"
8611
+ "@sarj/no-async-callback-in-waitfor": "warn"
7752
8612
  }
7753
8613
  },
7754
8614
  strict: {
@@ -7757,7 +8617,6 @@ var plugin = {
7757
8617
  "@sarj/zod-naming-convention": "error",
7758
8618
  "@sarj/require-assert-never": "error",
7759
8619
  "@sarj/require-zod-form-validation": "error",
7760
- "@sarj/ban-loose-type-guards-in-tests": "error",
7761
8620
  "@sarj/enforce-file-structure": "error",
7762
8621
  "@sarj/no-raw-env": "error",
7763
8622
  "@sarj/no-enum": "error",
@@ -7787,6 +8646,8 @@ var plugin = {
7787
8646
  // Promoted to error 2026-07-25 — strict means strict (user directive).
7788
8647
  "@sarj/prefer-string-literal-union": "error",
7789
8648
  "@sarj/prefer-zod-enum": "error",
8649
+ // See the `recommended` block for the measured counts.
8650
+ "@sarj/prefer-zod-infer": "error",
7790
8651
  // Mined from 2y of PR review feedback + 5-repo code-smell audit (2026-07).
7791
8652
  "@sarj/require-fetch-timeout": "error",
7792
8653
  "@sarj/no-silent-promise-catch": "error",
@@ -7830,9 +8691,7 @@ var plugin = {
7830
8691
  // corpus (175 `implements` clauses vs 29 hits), so strict enforces it.
7831
8692
  "@sarj/require-interface-for-injected-service": "error",
7832
8693
  "@sarj/prefer-non-nullable-collection": "error",
7833
- "@sarj/no-implicit-attribute-access": "error",
7834
- "@sarj/no-async-callback-in-waitfor": "error",
7835
- "@sarj/prefer-setup-file-mocks": "error"
8694
+ "@sarj/no-async-callback-in-waitfor": "error"
7836
8695
  }
7837
8696
  }
7838
8697
  }