@sarj/eslint-plugin 15.4.0 → 15.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -564,6 +564,9 @@ function isAssertionStatement(statement) {
564
564
  const expression = statement.expression;
565
565
  return expression.type === AST_NODE_TYPES2.CallExpression && isAssertionCall(expression);
566
566
  }
567
+ function isTypeOnlyContractStatement(statement) {
568
+ return statement.type === AST_NODE_TYPES2.TSTypeAliasDeclaration || statement.type === AST_NODE_TYPES2.TSInterfaceDeclaration;
569
+ }
567
570
  function normalizedLiteral(node) {
568
571
  if ("regex" in node) {
569
572
  return ["Literal", "regex", node.regex.pattern, node.regex.flags];
@@ -621,7 +624,7 @@ var duplicate_test_body_default = createRule({
621
624
  return;
622
625
  }
623
626
  const body2 = candidate.body;
624
- if (body2.body.type !== AST_NODE_TYPES2.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement)) {
627
+ if (body2.body.type !== AST_NODE_TYPES2.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement) || body2.body.body.some(isTypeOnlyContractStatement)) {
625
628
  return;
626
629
  }
627
630
  const comments = context.sourceCode.getCommentsInside(body2.body).map((comment) => [comment.type, comment.value]);
@@ -3521,7 +3524,11 @@ var NUMBER_METHODS = /* @__PURE__ */ new Set([
3521
3524
  "lt",
3522
3525
  "lte",
3523
3526
  "max",
3524
- "min"
3527
+ "min",
3528
+ "negative",
3529
+ "nonnegative",
3530
+ "nonpositive",
3531
+ "positive"
3525
3532
  ]);
3526
3533
  var LENGTH_METHODS = /* @__PURE__ */ new Set(["length", "max", "min"]);
3527
3534
  var RESHAPING_METHODS = /* @__PURE__ */ new Set([
@@ -3658,13 +3665,29 @@ var no_impossible_zod_literal_bounds_default = createRule({
3658
3665
  let upper = null;
3659
3666
  for (const { method, node } of chain.calls) {
3660
3667
  if (!allowed.has(method)) return null;
3661
- const value = finiteNumber(node.arguments[0]);
3668
+ const semantic = method === "positive" ? { exclusive: true, lower: true, value: 0 } : method === "nonnegative" ? { exclusive: false, lower: true, value: 0 } : method === "negative" ? { exclusive: true, lower: false, value: 0 } : method === "nonpositive" ? { exclusive: false, lower: false, value: 0 } : null;
3669
+ const value = semantic?.value ?? finiteNumber(node.arguments[0]);
3662
3670
  if (value === null) return null;
3663
3671
  if (chain.kind !== "number" && (!Number.isInteger(value) || value < 0)) {
3664
3672
  return null;
3665
3673
  }
3666
3674
  const label = `${method}(${String(value)})`;
3667
- if (method === "length") {
3675
+ if (semantic !== null) {
3676
+ if (node.arguments.length !== 0) return null;
3677
+ if (semantic.lower) {
3678
+ lower = strongerLower(lower, {
3679
+ exclusive: semantic.exclusive,
3680
+ label: `${method}()`,
3681
+ value
3682
+ });
3683
+ } else {
3684
+ upper = strongerUpper(upper, {
3685
+ exclusive: semantic.exclusive,
3686
+ label: `${method}()`,
3687
+ value
3688
+ });
3689
+ }
3690
+ } else if (method === "length") {
3668
3691
  lower = strongerLower(lower, { exclusive: false, label, value });
3669
3692
  upper = strongerUpper(upper, { exclusive: false, label, value });
3670
3693
  } else if (method === "gt" || method === "gte" || method === "min") {
@@ -4170,7 +4193,7 @@ import { AST_NODE_TYPES as AST_NODE_TYPES16, ASTUtils as ASTUtils4 } from "@type
4170
4193
  var noGenericSingleExportModuleDocumentation = {
4171
4194
  summary: "Disallow generic module stems when one runtime export already names the responsibility.",
4172
4195
  rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
4173
- remediation: "Rename the module after its single runtime export.",
4196
+ remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
4174
4197
  category: "maintainability",
4175
4198
  limitations: ["Only configured generic stems with exactly one public runtime export are reported."],
4176
4199
  examples: [
@@ -4179,36 +4202,21 @@ var noGenericSingleExportModuleDocumentation = {
4179
4202
  ]
4180
4203
  };
4181
4204
  var GENERIC_STEMS = /* @__PURE__ */ new Set([
4182
- "base",
4183
4205
  "common",
4184
- "constant",
4185
- "constants",
4186
- "core",
4187
- "enum",
4188
- "enums",
4189
4206
  "helper",
4190
4207
  "helpers",
4191
4208
  "misc",
4192
- "model",
4193
- "models",
4194
4209
  "shared",
4195
4210
  "stuff",
4196
- "type",
4197
- "types",
4198
4211
  "util",
4199
4212
  "utils"
4200
4213
  ]);
4201
- var CONVENTIONAL_STEMS = /* @__PURE__ */ new Set(["base", "models"]);
4202
4214
  var CJS_OBJECT_EXPORT_METHODS = /* @__PURE__ */ new Set(["assign", "defineProperties", "defineProperty"]);
4203
4215
  function fileParts(filename) {
4204
4216
  const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
4205
4217
  const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
4206
- const segments = base.slice(0, -extension.length).split(".");
4207
- return {
4208
- extension,
4209
- stem: segments[0]?.toLowerCase() ?? "",
4210
- suffixes: segments.slice(1)
4211
- };
4218
+ const [stem3 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
4219
+ return { stem: stem3, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
4212
4220
  }
4213
4221
  function declaredNames(declaration) {
4214
4222
  if (declaration.declare === true) return [];
@@ -4224,50 +4232,6 @@ function declaredNames(declaration) {
4224
4232
  (item) => item.id.type === AST_NODE_TYPES16.Identifier ? [item.id.name] : []
4225
4233
  );
4226
4234
  }
4227
- function typeOnlyBindings(program) {
4228
- const names = /* @__PURE__ */ new Set();
4229
- const runtimeNames = /* @__PURE__ */ new Set();
4230
- for (const statement of program.body) {
4231
- const declaration = statement.type === AST_NODE_TYPES16.ExportNamedDeclaration ? statement.declaration : statement;
4232
- if (declaration?.type === AST_NODE_TYPES16.TSInterfaceDeclaration || declaration?.type === AST_NODE_TYPES16.TSTypeAliasDeclaration) {
4233
- names.add(declaration.id.name);
4234
- continue;
4235
- }
4236
- if (declaration?.type === AST_NODE_TYPES16.TSEnumDeclaration && declaration.const) {
4237
- names.add(declaration.id.name);
4238
- continue;
4239
- }
4240
- if (statement.type === AST_NODE_TYPES16.ImportDeclaration) {
4241
- for (const specifier of statement.specifiers) {
4242
- if (statement.importKind === "type" || specifier.type === AST_NODE_TYPES16.ImportSpecifier && specifier.importKind === "type") {
4243
- names.add(specifier.local.name);
4244
- } else {
4245
- runtimeNames.add(specifier.local.name);
4246
- }
4247
- }
4248
- continue;
4249
- }
4250
- if (declaration?.type === AST_NODE_TYPES16.TSDeclareFunction && declaration.id !== null) {
4251
- names.add(declaration.id.name);
4252
- continue;
4253
- }
4254
- if (declaration !== null && declaration.declare === true) {
4255
- if ((declaration.type === AST_NODE_TYPES16.ClassDeclaration || declaration.type === AST_NODE_TYPES16.FunctionDeclaration || declaration.type === AST_NODE_TYPES16.TSEnumDeclaration || declaration.type === AST_NODE_TYPES16.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === AST_NODE_TYPES16.Identifier) names.add(declaration.id.name);
4256
- if (declaration.type === AST_NODE_TYPES16.VariableDeclaration) {
4257
- for (const item of declaration.declarations) {
4258
- if (item.id.type === AST_NODE_TYPES16.Identifier) names.add(item.id.name);
4259
- }
4260
- }
4261
- continue;
4262
- }
4263
- if (declaration !== null && "type" in declaration) {
4264
- for (const name of declaredNames(declaration)) {
4265
- runtimeNames.add(name);
4266
- }
4267
- }
4268
- }
4269
- return new Set([...names].filter((name) => !runtimeNames.has(name)));
4270
- }
4271
4235
  function runtimeExports(program) {
4272
4236
  const exports = [];
4273
4237
  const typeBindings = typeOnlyBindings(program);
@@ -4306,25 +4270,49 @@ function runtimeExports(program) {
4306
4270
  const unique = new Map(exports.map((entry) => [entry.key, entry]));
4307
4271
  return { exports: [...unique.values()], ambiguous };
4308
4272
  }
4309
- function publicTypeExportCount(program) {
4273
+ function typeOnlyBindings(program) {
4310
4274
  const names = /* @__PURE__ */ new Set();
4311
- const typeBindings = typeOnlyBindings(program);
4275
+ const runtimeNames = /* @__PURE__ */ new Set();
4312
4276
  for (const statement of program.body) {
4313
- if (statement.type !== AST_NODE_TYPES16.ExportNamedDeclaration) continue;
4314
- const declaration = statement.declaration;
4315
- if (declaration?.type === AST_NODE_TYPES16.TSInterfaceDeclaration || declaration?.type === AST_NODE_TYPES16.TSTypeAliasDeclaration) names.add(declaration.id.name);
4316
- for (const specifier of statement.specifiers) {
4317
- const local = specifier.local.type === AST_NODE_TYPES16.Identifier ? specifier.local.name : specifier.local.value;
4318
- if (statement.exportKind !== "type" && specifier.exportKind !== "type" && !typeBindings.has(local)) {
4319
- continue;
4277
+ const declaration = statement.type === AST_NODE_TYPES16.ExportNamedDeclaration ? statement.declaration : statement;
4278
+ if (declaration?.type === AST_NODE_TYPES16.TSInterfaceDeclaration || declaration?.type === AST_NODE_TYPES16.TSTypeAliasDeclaration) {
4279
+ names.add(declaration.id.name);
4280
+ continue;
4281
+ }
4282
+ if (declaration?.type === AST_NODE_TYPES16.TSEnumDeclaration && declaration.const) {
4283
+ names.add(declaration.id.name);
4284
+ continue;
4285
+ }
4286
+ if (statement.type === AST_NODE_TYPES16.ImportDeclaration) {
4287
+ for (const specifier of statement.specifiers) {
4288
+ if (statement.importKind === "type" || specifier.type === AST_NODE_TYPES16.ImportSpecifier && specifier.importKind === "type") {
4289
+ names.add(specifier.local.name);
4290
+ } else {
4291
+ runtimeNames.add(specifier.local.name);
4292
+ }
4293
+ }
4294
+ continue;
4295
+ }
4296
+ if (declaration?.type === AST_NODE_TYPES16.TSDeclareFunction && declaration.id !== null) {
4297
+ names.add(declaration.id.name);
4298
+ continue;
4299
+ }
4300
+ if (declaration !== null && declaration.declare === true) {
4301
+ if ((declaration.type === AST_NODE_TYPES16.ClassDeclaration || declaration.type === AST_NODE_TYPES16.FunctionDeclaration || declaration.type === AST_NODE_TYPES16.TSEnumDeclaration || declaration.type === AST_NODE_TYPES16.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === AST_NODE_TYPES16.Identifier) names.add(declaration.id.name);
4302
+ if (declaration.type === AST_NODE_TYPES16.VariableDeclaration) {
4303
+ for (const item of declaration.declarations) {
4304
+ if (item.id.type === AST_NODE_TYPES16.Identifier) names.add(item.id.name);
4305
+ }
4306
+ }
4307
+ continue;
4308
+ }
4309
+ if (declaration !== null && "type" in declaration) {
4310
+ for (const name of declaredNames(declaration)) {
4311
+ runtimeNames.add(name);
4320
4312
  }
4321
- names.add(specifier.exported.type === AST_NODE_TYPES16.Identifier ? specifier.exported.name : specifier.exported.value);
4322
4313
  }
4323
4314
  }
4324
- return names.size;
4325
- }
4326
- function kebabCase(name) {
4327
- return name.replaceAll(/oauth/giu, "Oauth").replaceAll(/graphql/giu, "Graphql").replaceAll(/grpc/giu, "Grpc").replaceAll(/([a-z\d])([A-Z])/gu, "$1-$2").replaceAll(/([A-Z]+)([A-Z][a-z])/gu, "$1-$2").replaceAll(/[_\s]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "").toLowerCase();
4315
+ return new Set([...names].filter((name) => !runtimeNames.has(name)));
4328
4316
  }
4329
4317
  function isGlobalIdentifier(context, node) {
4330
4318
  const variable = ASTUtils4.findVariable(context.sourceCode.getScope(node), node.name);
@@ -4346,14 +4334,14 @@ var no_generic_single_export_module_default = createRule({
4346
4334
  docs: { description: "Disallow generic module stems when one runtime export already names the responsibility." },
4347
4335
  schema: [],
4348
4336
  messages: {
4349
- genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`; rename the file to `{{expected}}`."
4337
+ genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`; choose a responsibility-bearing module name or colocate the export with its domain."
4350
4338
  }
4351
4339
  },
4352
4340
  defaultOptions: [],
4353
4341
  create(context) {
4354
4342
  const file = fileParts(context.filename);
4355
4343
  const { stem: stem3 } = file;
4356
- if (!GENERIC_STEMS.has(stem3) || CONVENTIONAL_STEMS.has(stem3) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
4344
+ if (!GENERIC_STEMS.has(stem3) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
4357
4345
  let hasCommonJsExport = false;
4358
4346
  return {
4359
4347
  CallExpression(node) {
@@ -4366,25 +4354,19 @@ var no_generic_single_export_module_default = createRule({
4366
4354
  },
4367
4355
  "Program:exit"(program) {
4368
4356
  if (hasCommonJsExport) return;
4369
- if ((stem3 === "type" || stem3 === "types") && publicTypeExportCount(program) >= 2) return;
4370
4357
  const exports = runtimeExports(program);
4371
4358
  if (exports.ambiguous || exports.exports.length !== 1) return;
4372
4359
  const onlyExport = exports.exports[0];
4373
4360
  if (onlyExport === void 0) return;
4374
4361
  const { name: exported, node } = onlyExport;
4375
4362
  if (isConventionalFrameworkUtility(context.filename, exported)) return;
4376
- const expectedStem = kebabCase(exported);
4377
- const suffixStem = file.suffixes.map(kebabCase).join("-");
4378
- const currentResponsibility = [stem3, suffixStem].filter(Boolean).join("-");
4379
- if (expectedStem === currentResponsibility || GENERIC_STEMS.has(expectedStem) || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(expectedStem)) return;
4380
- const suffixTail = suffixStem === "" ? "" : `-${suffixStem}`;
4381
- const renameStem = suffixTail !== "" && expectedStem.endsWith(suffixTail) ? expectedStem.slice(0, -suffixTail.length) : expectedStem;
4382
- if (renameStem === "" || GENERIC_STEMS.has(renameStem)) return;
4383
- const suffix = file.suffixes.map((part) => `.${kebabCase(part)}`).join("");
4363
+ const exportedRole = exported.replaceAll(/[^a-z0-9]/giu, "").toLowerCase();
4364
+ const pathRole = [stem3, ...file.suffixes].join("").replaceAll(/[^a-z0-9]/giu, "");
4365
+ if (exportedRole === pathRole && file.suffixes.length > 0) return;
4384
4366
  context.report({
4385
4367
  node,
4386
4368
  messageId: "genericSingleExport",
4387
- data: { stem: stem3, exported, expected: `${renameStem}${suffix}${file.extension}` }
4369
+ data: { stem: stem3, exported }
4388
4370
  });
4389
4371
  }
4390
4372
  };
@@ -6777,6 +6759,13 @@ function nearestEnclosingFunction(node) {
6777
6759
  }
6778
6760
  return null;
6779
6761
  }
6762
+ function isImmediatelyConsumedSleep(node) {
6763
+ const parent = node.parent;
6764
+ if (parent?.type === AST_NODE_TYPES25.AwaitExpression || parent?.type === AST_NODE_TYPES25.ReturnStatement || parent?.type === AST_NODE_TYPES25.ExpressionStatement) {
6765
+ return true;
6766
+ }
6767
+ return parent?.type === AST_NODE_TYPES25.ArrowFunctionExpression && parent.body === node;
6768
+ }
6780
6769
  function isTestBody(fn) {
6781
6770
  const call = fn.parent;
6782
6771
  if (call?.type !== AST_NODE_TYPES25.CallExpression || !call.arguments.some((argument) => argument === fn)) {
@@ -6819,6 +6808,9 @@ var no_sleep_in_test_body_default = createRule({
6819
6808
  return {};
6820
6809
  }
6821
6810
  const report = (node) => {
6811
+ if (!isImmediatelyConsumedSleep(node)) {
6812
+ return;
6813
+ }
6822
6814
  const enclosing = nearestEnclosingFunction(node);
6823
6815
  if (enclosing === null || !isTestBody(enclosing)) {
6824
6816
  return;
@@ -7116,6 +7108,27 @@ function isSmallStaticForLoop(node) {
7116
7108
  const iterations = node.test.right.value - declaration.init.value + (node.test.operator === "<=" ? 1 : 0);
7117
7109
  return iterations >= 0 && iterations <= 8;
7118
7110
  }
7111
+ function isStringSeededReduce(node) {
7112
+ if (node.callee.type !== "MemberExpression" || node.callee.computed || node.callee.property.type !== "Identifier" || node.callee.property.name !== "reduce" || node.arguments.length !== 2) {
7113
+ return false;
7114
+ }
7115
+ const [callback, initial] = node.arguments;
7116
+ if (node.callee.object.type === "ArrayExpression" && node.callee.object.elements.length <= 8) {
7117
+ return false;
7118
+ }
7119
+ if (callback === void 0 || initial === void 0 || callback.type === "SpreadElement" || initial.type === "SpreadElement" || callback.type !== "ArrowFunctionExpression" && callback.type !== "FunctionExpression" || callback.params[0]?.type !== "Identifier" || !isStringLiteralInit(initial)) {
7120
+ return false;
7121
+ }
7122
+ const accumulator = callback.params[0].name;
7123
+ if (callback.body.type !== "BlockStatement") {
7124
+ return isConcatOntoTarget(callback.body, accumulator);
7125
+ }
7126
+ if (callback.body.body.length !== 1 || callback.body.body[0]?.type !== "ReturnStatement") {
7127
+ return false;
7128
+ }
7129
+ const returned = callback.body.body[0].argument;
7130
+ return returned !== null && isConcatOntoTarget(returned, accumulator);
7131
+ }
7119
7132
  var no_string_concat_in_loop_default = createRule({
7120
7133
  name: "no-string-concat-in-loop",
7121
7134
  documentation: noStringConcatInLoopDocumentation,
@@ -7126,7 +7139,8 @@ var no_string_concat_in_loop_default = createRule({
7126
7139
  },
7127
7140
  schema: [],
7128
7141
  messages: {
7129
- noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.'
7142
+ noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.',
7143
+ noStringReduce: "Avoid concatenating a growing string in `reduce` \u2014 this is O(n^2). Map the fragments and join them once instead."
7130
7144
  }
7131
7145
  },
7132
7146
  defaultOptions: [],
@@ -7136,6 +7150,11 @@ var no_string_concat_in_loop_default = createRule({
7136
7150
  }
7137
7151
  const reported = /* @__PURE__ */ new WeakMap();
7138
7152
  return {
7153
+ CallExpression(node) {
7154
+ if (isStringSeededReduce(node)) {
7155
+ context.report({ node, messageId: "noStringReduce" });
7156
+ }
7157
+ },
7139
7158
  AssignmentExpression(node) {
7140
7159
  if (node.left.type !== "Identifier") {
7141
7160
  return;
@@ -7240,6 +7259,9 @@ function isLiteral(node) {
7240
7259
  return false;
7241
7260
  }
7242
7261
  }
7262
+ function isStructuralLiteral(node) {
7263
+ return node.type === AST_NODE_TYPES27.ArrayExpression || node.type === AST_NODE_TYPES27.ObjectExpression;
7264
+ }
7243
7265
  function expectOperand(callee) {
7244
7266
  const receiver = callee.object;
7245
7267
  if (receiver.type !== AST_NODE_TYPES27.CallExpression || receiver.callee.type !== AST_NODE_TYPES27.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
@@ -7296,6 +7318,9 @@ var no_tautological_expect_default = createRule({
7296
7318
  if (!EQUALITY_MATCHERS.has(matcher) || node.arguments.length !== 1 || expected === void 0 || !isLiteral(expected)) {
7297
7319
  return;
7298
7320
  }
7321
+ if (matcher === "toBe" && (isStructuralLiteral(operand) || isStructuralLiteral(expected))) {
7322
+ return;
7323
+ }
7299
7324
  if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {
7300
7325
  return;
7301
7326
  }
@@ -7609,7 +7634,7 @@ var no_trailing_value_narration_default = createRule({
7609
7634
  });
7610
7635
 
7611
7636
  // src/rules/no-declaration-comment-wall.ts
7612
- import { AST_NODE_TYPES as AST_NODE_TYPES29 } from "@typescript-eslint/utils";
7637
+ import { AST_NODE_TYPES as AST_NODE_TYPES29, AST_TOKEN_TYPES } from "@typescript-eslint/utils";
7613
7638
 
7614
7639
  // src/rules/_comment-wall.ts
7615
7640
  import { AST_NODE_TYPES as AST_NODE_TYPES28 } from "@typescript-eslint/utils";
@@ -7809,7 +7834,13 @@ var no_declaration_comment_wall_default = createRule({
7809
7834
  const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
7810
7835
  if (lead !== void 0) {
7811
7836
  const before = sourceCode.getTokenBefore(lead, { includeComments: false });
7812
- if (before === null || before.loc.end.line < lead.loc.start.line) return lead;
7837
+ if (before === null || before.loc.end.line < lead.loc.start.line) {
7838
+ const previousLine = endingOn.get(lead.loc.start.line - 1);
7839
+ if (lead.type === AST_TOKEN_TYPES.Line && previousLine?.type === AST_TOKEN_TYPES.Line && previousLine.loc.start.column === lead.loc.start.column) {
7840
+ return void 0;
7841
+ }
7842
+ return lead;
7843
+ }
7813
7844
  }
7814
7845
  const trail = startingOn.get(member.loc.end.line);
7815
7846
  return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
@@ -8048,7 +8079,7 @@ var no_union_in_comment_default = createRule({
8048
8079
  });
8049
8080
 
8050
8081
  // src/rules/no-type-member-comment-wall.ts
8051
- import { AST_NODE_TYPES as AST_NODE_TYPES31 } from "@typescript-eslint/utils";
8082
+ import { AST_NODE_TYPES as AST_NODE_TYPES31, AST_TOKEN_TYPES as AST_TOKEN_TYPES2 } from "@typescript-eslint/utils";
8052
8083
  var noTypeMemberCommentWallDocumentation = {
8053
8084
  summary: "Flag an object type whose member comments mostly re-spell the members' own names and types.",
8054
8085
  rationale: "Repetitive member comments add scanning cost while hiding the comments that describe facts absent from the type.",
@@ -8111,7 +8142,13 @@ var no_type_member_comment_wall_default = createRule({
8111
8142
  const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
8112
8143
  if (lead !== void 0) {
8113
8144
  const before = sourceCode.getTokenBefore(lead, { includeComments: false });
8114
- if (before === null || before.loc.end.line < lead.loc.start.line) return lead;
8145
+ if (before === null || before.loc.end.line < lead.loc.start.line) {
8146
+ const previousLine = endingOn.get(lead.loc.start.line - 1);
8147
+ if (lead.type === AST_TOKEN_TYPES2.Line && previousLine?.type === AST_TOKEN_TYPES2.Line && previousLine.loc.start.column === lead.loc.start.column) {
8148
+ return void 0;
8149
+ }
8150
+ return lead;
8151
+ }
8115
8152
  }
8116
8153
  const trail = startingOn.get(member.loc.end.line);
8117
8154
  return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
@@ -8411,6 +8448,10 @@ import {
8411
8448
  } from "@typescript-eslint/utils";
8412
8449
  var MOCK_TYPE_NAMES = /* @__PURE__ */ new Set([
8413
8450
  "Mock",
8451
+ "Mocked",
8452
+ "MockedClass",
8453
+ "MockedFunction",
8454
+ "MockedObject",
8414
8455
  "MockInstance",
8415
8456
  "SpyInstance"
8416
8457
  ]);
@@ -8658,9 +8699,9 @@ var no_zod_native_enum_default = createRule({
8658
8699
  }
8659
8700
  function isZodMemberCall(node, api) {
8660
8701
  const callee = node.callee;
8661
- if (callee.type === AST_NODE_TYPES34.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES34.Identifier && callee.property.type === AST_NODE_TYPES34.Identifier) {
8702
+ if (callee.type === AST_NODE_TYPES34.MemberExpression && callee.object.type === AST_NODE_TYPES34.Identifier && (callee.property.type === AST_NODE_TYPES34.Identifier && !callee.computed && callee.property.name === api || callee.computed && callee.property.type === AST_NODE_TYPES34.Literal && callee.property.value === api)) {
8662
8703
  const binding = resolvedBinding(callee.object);
8663
- return binding !== null && zodNamespaceBindings.has(binding) && callee.property.name === api;
8704
+ return binding !== null && zodNamespaceBindings.has(binding);
8664
8705
  }
8665
8706
  if (callee.type === AST_NODE_TYPES34.Identifier) {
8666
8707
  const binding = resolvedBinding(callee);
@@ -9504,6 +9545,27 @@ var prefer_immutable_module_constant_default = createRule({
9504
9545
  }
9505
9546
  const exportedNames2 = /* @__PURE__ */ new Set();
9506
9547
  const typeAliases2 = /* @__PURE__ */ new Map();
9548
+ const mutatesThroughConstAlias = (root) => {
9549
+ const pending = [root];
9550
+ const seen = /* @__PURE__ */ new Set();
9551
+ while (pending.length > 0) {
9552
+ const variable = pending.pop();
9553
+ if (variable === void 0 || seen.has(variable)) continue;
9554
+ seen.add(variable);
9555
+ for (const reference of variable.references) {
9556
+ const identifier = reference.identifier;
9557
+ if (identifier.type !== AST_NODE_TYPES39.Identifier) continue;
9558
+ if (referenceMutates(identifier, isUnshadowedGlobal)) return true;
9559
+ const declarator = identifier.parent;
9560
+ if (declarator.type !== AST_NODE_TYPES39.VariableDeclarator || declarator.init !== identifier || declarator.id.type !== AST_NODE_TYPES39.Identifier || declarator.parent.type !== AST_NODE_TYPES39.VariableDeclaration || declarator.parent.kind !== "const") {
9561
+ continue;
9562
+ }
9563
+ const alias = sourceCode.getDeclaredVariables(declarator)[0];
9564
+ if (alias !== void 0) pending.push(alias);
9565
+ }
9566
+ }
9567
+ return false;
9568
+ };
9507
9569
  return {
9508
9570
  Program(node) {
9509
9571
  for (const statement of node.body) {
@@ -9544,9 +9606,7 @@ var prefer_immutable_module_constant_default = createRule({
9544
9606
  return;
9545
9607
  }
9546
9608
  const variable = sourceCode.getDeclaredVariables(node)[0];
9547
- if (!directlyExported && !exportedNames2.has(node.id.name) && variable?.references.some(
9548
- (reference) => reference.identifier.type === AST_NODE_TYPES39.Identifier && referenceMutates(reference.identifier, isUnshadowedGlobal)
9549
- ) === true) {
9609
+ if (!directlyExported && !exportedNames2.has(node.id.name) && variable !== void 0 && mutatesThroughConstAlias(variable)) {
9550
9610
  return;
9551
9611
  }
9552
9612
  context.report({
@@ -10325,6 +10385,10 @@ var prefer_module_level_schema_default = createRule({
10325
10385
  }
10326
10386
  for (const definition of resolved.defs) {
10327
10387
  if (definition.type === "ImportBinding") {
10388
+ const parent = reference.identifier.parent;
10389
+ if (parent?.type === AST_NODE_TYPES42.CallExpression && parent.callee === reference.identifier && !zodNamespaces.has(reference.identifier.name)) {
10390
+ return false;
10391
+ }
10328
10392
  continue;
10329
10393
  }
10330
10394
  if (definition.node.type === AST_NODE_TYPES42.VariableDeclarator && definition.node.parent.type === AST_NODE_TYPES42.VariableDeclaration && definition.node.parent.kind !== "const") {
@@ -12156,10 +12220,7 @@ var SHAPE_PRESERVING_METHODS = /* @__PURE__ */ new Set([
12156
12220
  ]);
12157
12221
  var OPTIONAL_MODIFIERS = /* @__PURE__ */ new Set([
12158
12222
  "optional",
12159
- "nullish",
12160
- "default",
12161
- "prefault",
12162
- "catch"
12223
+ "nullish"
12163
12224
  ]);
12164
12225
  var NULLABLE_MODIFIERS = /* @__PURE__ */ new Set(["nullable", "nullish"]);
12165
12226
  var RESHAPING_MODIFIERS = /* @__PURE__ */ new Set([
@@ -13434,6 +13495,7 @@ var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
13434
13495
  "parseAsync",
13435
13496
  "safeParseAsync"
13436
13497
  ]);
13498
+ var FORM_VALUE_METHODS = /* @__PURE__ */ new Set(["get", "getAll"]);
13437
13499
  var zodReceiverRoot = (node) => {
13438
13500
  let current = node;
13439
13501
  while (true) {
@@ -13508,7 +13570,7 @@ var require_zod_form_validation_default = createRule({
13508
13570
  };
13509
13571
  const isFormSourceIdentifier = (node) => {
13510
13572
  if (node.type !== AST_NODE_TYPES53.Identifier) return false;
13511
- if (/formdata/i.test(node.name)) return true;
13573
+ const conventionalName = /formdata/i.test(node.name);
13512
13574
  let scope = context.sourceCode.getScope(node);
13513
13575
  while (scope !== null) {
13514
13576
  const variable = scope.set.get(node.name);
@@ -13517,16 +13579,16 @@ var require_zod_form_validation_default = createRule({
13517
13579
  if (def !== void 0 && def.type === "Variable" && def.node.type === AST_NODE_TYPES53.VariableDeclarator && def.node.init !== null) {
13518
13580
  return isFormDataMethodCall(def.node.init);
13519
13581
  }
13520
- return false;
13582
+ return def?.type === "Parameter" && conventionalName;
13521
13583
  }
13522
13584
  scope = scope.upper;
13523
13585
  }
13524
- return false;
13586
+ return conventionalName;
13525
13587
  };
13526
13588
  const isFormDataGetCall = (node) => {
13527
13589
  const callee = node.callee;
13528
13590
  if (callee.type !== AST_NODE_TYPES53.MemberExpression) return false;
13529
- if (callee.property.type !== AST_NODE_TYPES53.Identifier || callee.property.name !== "get") {
13591
+ if (callee.property.type !== AST_NODE_TYPES53.Identifier || !FORM_VALUE_METHODS.has(callee.property.name)) {
13530
13592
  return false;
13531
13593
  }
13532
13594
  return isFormSourceIdentifier(callee.object);
@@ -13620,6 +13682,40 @@ var require_zod_form_validation_default = createRule({
13620
13682
  }
13621
13683
  return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === AST_NODE_TYPES53.Literal && parent.right.value === null || parent.right.type === AST_NODE_TYPES53.Identifier && parent.right.name === "undefined");
13622
13684
  };
13685
+ const isDescendantOf = (node, ancestor) => {
13686
+ let current = node;
13687
+ while (current !== void 0 && current !== null) {
13688
+ if (current === ancestor) return true;
13689
+ current = current.parent;
13690
+ }
13691
+ return false;
13692
+ };
13693
+ const blockTerminates = (node) => {
13694
+ if (node.type === AST_NODE_TYPES53.ReturnStatement || node.type === AST_NODE_TYPES53.ThrowStatement) {
13695
+ return true;
13696
+ }
13697
+ if (node.type !== AST_NODE_TYPES53.BlockStatement || node.body.length === 0) return false;
13698
+ const last = node.body.at(-1);
13699
+ return last !== void 0 && blockTerminates(last);
13700
+ };
13701
+ const narrowingIf = (identifier) => {
13702
+ const comparison = identifier.parent;
13703
+ if (comparison?.type !== AST_NODE_TYPES53.BinaryExpression || comparison.operator !== "instanceof" || comparison.left !== identifier || comparison.right.type !== AST_NODE_TYPES53.Identifier || comparison.right.name !== "File" && comparison.right.name !== "Blob") {
13704
+ return null;
13705
+ }
13706
+ const maybeNegation = comparison.parent;
13707
+ const negated = maybeNegation?.type === AST_NODE_TYPES53.UnaryExpression && maybeNegation.operator === "!";
13708
+ const test = negated ? maybeNegation : comparison;
13709
+ const branch = test.parent;
13710
+ return branch?.type === AST_NODE_TYPES53.IfStatement && branch.test === test ? { branch, positive: !negated } : null;
13711
+ };
13712
+ const useDominatedByNarrowing = (use, narrowings) => narrowings.some(({ branch, positive }) => {
13713
+ if (positive) return isDescendantOf(use, branch.consequent);
13714
+ if (!blockTerminates(branch.consequent)) return false;
13715
+ const branchStatement = containingStatement(branch);
13716
+ const useStatement = containingStatement(use);
13717
+ return branchStatement !== null && useStatement !== null && branchStatement.parent === useStatement.parent && branchStatement.range[1] < useStatement.range[0];
13718
+ });
13623
13719
  const statementWithinBlock = (node, block) => {
13624
13720
  let current = node;
13625
13721
  while (current.parent !== void 0 && current.parent !== block) {
@@ -13634,14 +13730,16 @@ var require_zod_form_validation_default = createRule({
13634
13730
  (identifier) => identifier.type === AST_NODE_TYPES53.Identifier
13635
13731
  );
13636
13732
  if (references.length === 0) return false;
13637
- if (references.some(isInstanceofNarrowing)) return true;
13733
+ const narrowings = references.map(narrowingIf).filter(
13734
+ (value) => value !== null
13735
+ );
13638
13736
  const validationStatements = references.map((reference) => guaranteedValidationStatement(declarator, reference)).filter(
13639
13737
  (statement) => statement !== null
13640
13738
  );
13641
13739
  const declarationStatement = containingStatement(declarator);
13642
13740
  const declarationBlock = declarationStatement?.parent;
13643
13741
  return references.every((reference) => {
13644
- if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference)) {
13742
+ if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference) || useDominatedByNarrowing(reference, narrowings)) {
13645
13743
  return true;
13646
13744
  }
13647
13745
  if (declarationBlock === void 0) return false;
@@ -13744,8 +13842,13 @@ import { AST_NODE_TYPES as AST_NODE_TYPES54, ASTUtils as ASTUtils15 } from "@typ
13744
13842
  var stepdownDocumentation = {
13745
13843
  summary: "Place a private helper below its sole direct same-scope caller.",
13746
13844
  rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
13747
- remediation: "Move the private helper immediately below its sole caller.",
13845
+ remediation: "Move the private helper below its sole caller.",
13748
13846
  category: "maintainability",
13847
+ limitations: [
13848
+ "Generated and test files, cycles, dynamic references, overload targets, and helpers with multiple callers are excluded.",
13849
+ "Class methods are reported only when both helper and caller are private so accessibility ordering remains authoritative.",
13850
+ "Runtime class-field, static-block, computed-member, and decorator barriers are never crossed."
13851
+ ],
13749
13852
  examples: [
13750
13853
  { id: "caller-before-helper", title: "Place the caller first", outcome: "no-match", files: [{ path: "src/run.ts", source: "function run() { return load(); }\nfunction load() { return 1; }" }], focusPath: "src/run.ts", expectedCount: 0, public: true },
13751
13854
  { id: "helper-before-caller", title: "Do not lead with a sole-caller helper", outcome: "match", files: [{ path: "src/run.ts", source: "function load() { return 1; }\nfunction run() { return load(); }" }], focusPath: "src/run.ts", expectedCount: 1, public: true }
@@ -13754,7 +13857,7 @@ var stepdownDocumentation = {
13754
13857
  function isFunction(node) {
13755
13858
  return node.type === AST_NODE_TYPES54.ArrowFunctionExpression || node.type === AST_NODE_TYPES54.FunctionDeclaration || node.type === AST_NODE_TYPES54.FunctionExpression;
13756
13859
  }
13757
- function reportMisordered(context, candidates, scopeDefinitions, calls, pinned) {
13860
+ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
13758
13861
  const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
13759
13862
  const cycles = cycleComponents(calls);
13760
13863
  const callers = /* @__PURE__ */ new Map();
@@ -13772,7 +13875,7 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned)
13772
13875
  const callerName2 = helperCallers[0];
13773
13876
  if (callerName2 === void 0 || cycles.has(helper.name) && cycles.get(helper.name) === cycles.get(callerName2)) continue;
13774
13877
  const caller = byName.get(callerName2);
13775
- if (caller === void 0 || helper.node.range[0] >= caller.node.range[0]) continue;
13878
+ if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
13776
13879
  context.report({
13777
13880
  node: helper.node,
13778
13881
  messageId: "helperAboveOnlyCaller",
@@ -13852,8 +13955,10 @@ function moduleScope(context, program) {
13852
13955
  })
13853
13956
  );
13854
13957
  const exported = exportedNames(program);
13855
- const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1 && !overloadNames.has(node.name));
13856
- const definitions = scopeDefinitions.filter((definition) => !exported.has(definition.name));
13958
+ const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1);
13959
+ const definitions = scopeDefinitions.filter(
13960
+ (definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
13961
+ );
13857
13962
  const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
13858
13963
  const calls = /* @__PURE__ */ new Map();
13859
13964
  const pinned = /* @__PURE__ */ new Set();
@@ -14097,11 +14202,36 @@ function classScope(context, node, computedReferenceNames) {
14097
14202
  return [definition.name, accessibility2];
14098
14203
  })
14099
14204
  );
14205
+ const methodByName = new Map(scopeDefinitions.map((definition) => [definition.name, definition.node]));
14100
14206
  for (const [caller, callees] of calls) {
14101
- if (accessibility.get(caller) === "private") continue;
14207
+ const callerMethod = methodByName.get(caller);
14208
+ if (accessibility.get(caller) === "private" && callerMethod?.type === AST_NODE_TYPES54.MethodDefinition && callerMethod.decorators.length === 0) continue;
14102
14209
  for (const callee of callees) pinned.add(callee);
14103
14210
  }
14104
- reportMisordered(context, definitions, scopeDefinitions, calls, pinned);
14211
+ const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
14212
+ const runtimeBarrierPrefix = [0];
14213
+ for (const member of node.body.body) {
14214
+ runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
14215
+ }
14216
+ reportMisordered(context, definitions, scopeDefinitions, calls, pinned, (helper, caller) => {
14217
+ const helperIndex = memberIndexes.get(helper.node);
14218
+ const callerIndex = memberIndexes.get(caller.node);
14219
+ if (helperIndex === void 0 || callerIndex === void 0) return false;
14220
+ return runtimeBarrierPrefix[callerIndex + 1] === runtimeBarrierPrefix[helperIndex + 1];
14221
+ });
14222
+ }
14223
+ function isClassRuntimeBarrier(member) {
14224
+ switch (member.type) {
14225
+ case AST_NODE_TYPES54.StaticBlock:
14226
+ return true;
14227
+ case AST_NODE_TYPES54.PropertyDefinition:
14228
+ case AST_NODE_TYPES54.AccessorProperty:
14229
+ return member.static || member.computed || member.decorators.length > 0 || member.value !== null;
14230
+ case AST_NODE_TYPES54.MethodDefinition:
14231
+ return member.computed || member.decorators.length > 0;
14232
+ default:
14233
+ return false;
14234
+ }
14105
14235
  }
14106
14236
  var stepdown_default = createRule({
14107
14237
  name: "stepdown",
@@ -14174,7 +14304,13 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
14174
14304
  "safeDecodeAsync",
14175
14305
  "toJSONSchema",
14176
14306
  "registry",
14177
- "implement"
14307
+ "implement",
14308
+ "flattenError",
14309
+ "formatError",
14310
+ "isNullable",
14311
+ "isOptional",
14312
+ "prettifyError",
14313
+ "treeifyError"
14178
14314
  ]);
14179
14315
  var terminalMethodName = (callee) => !callee.computed && callee.property.type === AST_NODE_TYPES55.Identifier ? callee.property.name : null;
14180
14316
  var calleeChainRoot = (node) => {
@@ -14423,7 +14559,7 @@ var rules = {
14423
14559
  };
14424
14560
  var meta = {
14425
14561
  name: "@sarj/eslint-plugin",
14426
- version: "15.4.0"
14562
+ version: "15.6.0"
14427
14563
  };
14428
14564
  var applicationOnlyRules = [
14429
14565
  "no-restricted-library-load",