@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.cjs CHANGED
@@ -607,6 +607,9 @@ function isAssertionStatement(statement) {
607
607
  const expression = statement.expression;
608
608
  return expression.type === import_utils3.AST_NODE_TYPES.CallExpression && isAssertionCall(expression);
609
609
  }
610
+ function isTypeOnlyContractStatement(statement) {
611
+ return statement.type === import_utils3.AST_NODE_TYPES.TSTypeAliasDeclaration || statement.type === import_utils3.AST_NODE_TYPES.TSInterfaceDeclaration;
612
+ }
610
613
  function normalizedLiteral(node) {
611
614
  if ("regex" in node) {
612
615
  return ["Literal", "regex", node.regex.pattern, node.regex.flags];
@@ -664,7 +667,7 @@ var duplicate_test_body_default = createRule({
664
667
  return;
665
668
  }
666
669
  const body2 = candidate.body;
667
- if (body2.body.type !== import_utils3.AST_NODE_TYPES.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement)) {
670
+ if (body2.body.type !== import_utils3.AST_NODE_TYPES.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement) || body2.body.body.some(isTypeOnlyContractStatement)) {
668
671
  return;
669
672
  }
670
673
  const comments = context.sourceCode.getCommentsInside(body2.body).map((comment) => [comment.type, comment.value]);
@@ -3559,7 +3562,11 @@ var NUMBER_METHODS = /* @__PURE__ */ new Set([
3559
3562
  "lt",
3560
3563
  "lte",
3561
3564
  "max",
3562
- "min"
3565
+ "min",
3566
+ "negative",
3567
+ "nonnegative",
3568
+ "nonpositive",
3569
+ "positive"
3563
3570
  ]);
3564
3571
  var LENGTH_METHODS = /* @__PURE__ */ new Set(["length", "max", "min"]);
3565
3572
  var RESHAPING_METHODS = /* @__PURE__ */ new Set([
@@ -3696,13 +3703,29 @@ var no_impossible_zod_literal_bounds_default = createRule({
3696
3703
  let upper = null;
3697
3704
  for (const { method, node } of chain.calls) {
3698
3705
  if (!allowed.has(method)) return null;
3699
- const value = finiteNumber(node.arguments[0]);
3706
+ 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;
3707
+ const value = semantic?.value ?? finiteNumber(node.arguments[0]);
3700
3708
  if (value === null) return null;
3701
3709
  if (chain.kind !== "number" && (!Number.isInteger(value) || value < 0)) {
3702
3710
  return null;
3703
3711
  }
3704
3712
  const label = `${method}(${String(value)})`;
3705
- if (method === "length") {
3713
+ if (semantic !== null) {
3714
+ if (node.arguments.length !== 0) return null;
3715
+ if (semantic.lower) {
3716
+ lower = strongerLower(lower, {
3717
+ exclusive: semantic.exclusive,
3718
+ label: `${method}()`,
3719
+ value
3720
+ });
3721
+ } else {
3722
+ upper = strongerUpper(upper, {
3723
+ exclusive: semantic.exclusive,
3724
+ label: `${method}()`,
3725
+ value
3726
+ });
3727
+ }
3728
+ } else if (method === "length") {
3706
3729
  lower = strongerLower(lower, { exclusive: false, label, value });
3707
3730
  upper = strongerUpper(upper, { exclusive: false, label, value });
3708
3731
  } else if (method === "gt" || method === "gte" || method === "min") {
@@ -4208,7 +4231,7 @@ var import_utils21 = require("@typescript-eslint/utils");
4208
4231
  var noGenericSingleExportModuleDocumentation = {
4209
4232
  summary: "Disallow generic module stems when one runtime export already names the responsibility.",
4210
4233
  rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
4211
- remediation: "Rename the module after its single runtime export.",
4234
+ remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
4212
4235
  category: "maintainability",
4213
4236
  limitations: ["Only configured generic stems with exactly one public runtime export are reported."],
4214
4237
  examples: [
@@ -4217,36 +4240,21 @@ var noGenericSingleExportModuleDocumentation = {
4217
4240
  ]
4218
4241
  };
4219
4242
  var GENERIC_STEMS = /* @__PURE__ */ new Set([
4220
- "base",
4221
4243
  "common",
4222
- "constant",
4223
- "constants",
4224
- "core",
4225
- "enum",
4226
- "enums",
4227
4244
  "helper",
4228
4245
  "helpers",
4229
4246
  "misc",
4230
- "model",
4231
- "models",
4232
4247
  "shared",
4233
4248
  "stuff",
4234
- "type",
4235
- "types",
4236
4249
  "util",
4237
4250
  "utils"
4238
4251
  ]);
4239
- var CONVENTIONAL_STEMS = /* @__PURE__ */ new Set(["base", "models"]);
4240
4252
  var CJS_OBJECT_EXPORT_METHODS = /* @__PURE__ */ new Set(["assign", "defineProperties", "defineProperty"]);
4241
4253
  function fileParts(filename) {
4242
4254
  const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
4243
4255
  const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
4244
- const segments = base.slice(0, -extension.length).split(".");
4245
- return {
4246
- extension,
4247
- stem: segments[0]?.toLowerCase() ?? "",
4248
- suffixes: segments.slice(1)
4249
- };
4256
+ const [stem3 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
4257
+ return { stem: stem3, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
4250
4258
  }
4251
4259
  function declaredNames(declaration) {
4252
4260
  if (declaration.declare === true) return [];
@@ -4262,50 +4270,6 @@ function declaredNames(declaration) {
4262
4270
  (item) => item.id.type === import_utils21.AST_NODE_TYPES.Identifier ? [item.id.name] : []
4263
4271
  );
4264
4272
  }
4265
- function typeOnlyBindings(program) {
4266
- const names = /* @__PURE__ */ new Set();
4267
- const runtimeNames = /* @__PURE__ */ new Set();
4268
- for (const statement of program.body) {
4269
- const declaration = statement.type === import_utils21.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
4270
- if (declaration?.type === import_utils21.AST_NODE_TYPES.TSInterfaceDeclaration || declaration?.type === import_utils21.AST_NODE_TYPES.TSTypeAliasDeclaration) {
4271
- names.add(declaration.id.name);
4272
- continue;
4273
- }
4274
- if (declaration?.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration && declaration.const) {
4275
- names.add(declaration.id.name);
4276
- continue;
4277
- }
4278
- if (statement.type === import_utils21.AST_NODE_TYPES.ImportDeclaration) {
4279
- for (const specifier of statement.specifiers) {
4280
- if (statement.importKind === "type" || specifier.type === import_utils21.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type") {
4281
- names.add(specifier.local.name);
4282
- } else {
4283
- runtimeNames.add(specifier.local.name);
4284
- }
4285
- }
4286
- continue;
4287
- }
4288
- if (declaration?.type === import_utils21.AST_NODE_TYPES.TSDeclareFunction && declaration.id !== null) {
4289
- names.add(declaration.id.name);
4290
- continue;
4291
- }
4292
- if (declaration !== null && declaration.declare === true) {
4293
- if ((declaration.type === import_utils21.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(declaration.id.name);
4294
- if (declaration.type === import_utils21.AST_NODE_TYPES.VariableDeclaration) {
4295
- for (const item of declaration.declarations) {
4296
- if (item.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(item.id.name);
4297
- }
4298
- }
4299
- continue;
4300
- }
4301
- if (declaration !== null && "type" in declaration) {
4302
- for (const name of declaredNames(declaration)) {
4303
- runtimeNames.add(name);
4304
- }
4305
- }
4306
- }
4307
- return new Set([...names].filter((name) => !runtimeNames.has(name)));
4308
- }
4309
4273
  function runtimeExports(program) {
4310
4274
  const exports2 = [];
4311
4275
  const typeBindings = typeOnlyBindings(program);
@@ -4344,25 +4308,49 @@ function runtimeExports(program) {
4344
4308
  const unique = new Map(exports2.map((entry) => [entry.key, entry]));
4345
4309
  return { exports: [...unique.values()], ambiguous };
4346
4310
  }
4347
- function publicTypeExportCount(program) {
4311
+ function typeOnlyBindings(program) {
4348
4312
  const names = /* @__PURE__ */ new Set();
4349
- const typeBindings = typeOnlyBindings(program);
4313
+ const runtimeNames = /* @__PURE__ */ new Set();
4350
4314
  for (const statement of program.body) {
4351
- if (statement.type !== import_utils21.AST_NODE_TYPES.ExportNamedDeclaration) continue;
4352
- const declaration = statement.declaration;
4353
- if (declaration?.type === import_utils21.AST_NODE_TYPES.TSInterfaceDeclaration || declaration?.type === import_utils21.AST_NODE_TYPES.TSTypeAliasDeclaration) names.add(declaration.id.name);
4354
- for (const specifier of statement.specifiers) {
4355
- const local = specifier.local.type === import_utils21.AST_NODE_TYPES.Identifier ? specifier.local.name : specifier.local.value;
4356
- if (statement.exportKind !== "type" && specifier.exportKind !== "type" && !typeBindings.has(local)) {
4357
- continue;
4315
+ const declaration = statement.type === import_utils21.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
4316
+ if (declaration?.type === import_utils21.AST_NODE_TYPES.TSInterfaceDeclaration || declaration?.type === import_utils21.AST_NODE_TYPES.TSTypeAliasDeclaration) {
4317
+ names.add(declaration.id.name);
4318
+ continue;
4319
+ }
4320
+ if (declaration?.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration && declaration.const) {
4321
+ names.add(declaration.id.name);
4322
+ continue;
4323
+ }
4324
+ if (statement.type === import_utils21.AST_NODE_TYPES.ImportDeclaration) {
4325
+ for (const specifier of statement.specifiers) {
4326
+ if (statement.importKind === "type" || specifier.type === import_utils21.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type") {
4327
+ names.add(specifier.local.name);
4328
+ } else {
4329
+ runtimeNames.add(specifier.local.name);
4330
+ }
4331
+ }
4332
+ continue;
4333
+ }
4334
+ if (declaration?.type === import_utils21.AST_NODE_TYPES.TSDeclareFunction && declaration.id !== null) {
4335
+ names.add(declaration.id.name);
4336
+ continue;
4337
+ }
4338
+ if (declaration !== null && declaration.declare === true) {
4339
+ if ((declaration.type === import_utils21.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(declaration.id.name);
4340
+ if (declaration.type === import_utils21.AST_NODE_TYPES.VariableDeclaration) {
4341
+ for (const item of declaration.declarations) {
4342
+ if (item.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(item.id.name);
4343
+ }
4344
+ }
4345
+ continue;
4346
+ }
4347
+ if (declaration !== null && "type" in declaration) {
4348
+ for (const name of declaredNames(declaration)) {
4349
+ runtimeNames.add(name);
4358
4350
  }
4359
- names.add(specifier.exported.type === import_utils21.AST_NODE_TYPES.Identifier ? specifier.exported.name : specifier.exported.value);
4360
4351
  }
4361
4352
  }
4362
- return names.size;
4363
- }
4364
- function kebabCase(name) {
4365
- 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();
4353
+ return new Set([...names].filter((name) => !runtimeNames.has(name)));
4366
4354
  }
4367
4355
  function isGlobalIdentifier(context, node) {
4368
4356
  const variable = import_utils21.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
@@ -4384,14 +4372,14 @@ var no_generic_single_export_module_default = createRule({
4384
4372
  docs: { description: "Disallow generic module stems when one runtime export already names the responsibility." },
4385
4373
  schema: [],
4386
4374
  messages: {
4387
- genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`; rename the file to `{{expected}}`."
4375
+ 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."
4388
4376
  }
4389
4377
  },
4390
4378
  defaultOptions: [],
4391
4379
  create(context) {
4392
4380
  const file = fileParts(context.filename);
4393
4381
  const { stem: stem3 } = file;
4394
- if (!GENERIC_STEMS.has(stem3) || CONVENTIONAL_STEMS.has(stem3) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
4382
+ if (!GENERIC_STEMS.has(stem3) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
4395
4383
  let hasCommonJsExport = false;
4396
4384
  return {
4397
4385
  CallExpression(node) {
@@ -4404,25 +4392,19 @@ var no_generic_single_export_module_default = createRule({
4404
4392
  },
4405
4393
  "Program:exit"(program) {
4406
4394
  if (hasCommonJsExport) return;
4407
- if ((stem3 === "type" || stem3 === "types") && publicTypeExportCount(program) >= 2) return;
4408
4395
  const exports2 = runtimeExports(program);
4409
4396
  if (exports2.ambiguous || exports2.exports.length !== 1) return;
4410
4397
  const onlyExport = exports2.exports[0];
4411
4398
  if (onlyExport === void 0) return;
4412
4399
  const { name: exported, node } = onlyExport;
4413
4400
  if (isConventionalFrameworkUtility(context.filename, exported)) return;
4414
- const expectedStem = kebabCase(exported);
4415
- const suffixStem = file.suffixes.map(kebabCase).join("-");
4416
- const currentResponsibility = [stem3, suffixStem].filter(Boolean).join("-");
4417
- if (expectedStem === currentResponsibility || GENERIC_STEMS.has(expectedStem) || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(expectedStem)) return;
4418
- const suffixTail = suffixStem === "" ? "" : `-${suffixStem}`;
4419
- const renameStem = suffixTail !== "" && expectedStem.endsWith(suffixTail) ? expectedStem.slice(0, -suffixTail.length) : expectedStem;
4420
- if (renameStem === "" || GENERIC_STEMS.has(renameStem)) return;
4421
- const suffix = file.suffixes.map((part) => `.${kebabCase(part)}`).join("");
4401
+ const exportedRole = exported.replaceAll(/[^a-z0-9]/giu, "").toLowerCase();
4402
+ const pathRole = [stem3, ...file.suffixes].join("").replaceAll(/[^a-z0-9]/giu, "");
4403
+ if (exportedRole === pathRole && file.suffixes.length > 0) return;
4422
4404
  context.report({
4423
4405
  node,
4424
4406
  messageId: "genericSingleExport",
4425
- data: { stem: stem3, exported, expected: `${renameStem}${suffix}${file.extension}` }
4407
+ data: { stem: stem3, exported }
4426
4408
  });
4427
4409
  }
4428
4410
  };
@@ -6815,6 +6797,13 @@ function nearestEnclosingFunction(node) {
6815
6797
  }
6816
6798
  return null;
6817
6799
  }
6800
+ function isImmediatelyConsumedSleep(node) {
6801
+ const parent = node.parent;
6802
+ if (parent?.type === import_utils34.AST_NODE_TYPES.AwaitExpression || parent?.type === import_utils34.AST_NODE_TYPES.ReturnStatement || parent?.type === import_utils34.AST_NODE_TYPES.ExpressionStatement) {
6803
+ return true;
6804
+ }
6805
+ return parent?.type === import_utils34.AST_NODE_TYPES.ArrowFunctionExpression && parent.body === node;
6806
+ }
6818
6807
  function isTestBody(fn) {
6819
6808
  const call = fn.parent;
6820
6809
  if (call?.type !== import_utils34.AST_NODE_TYPES.CallExpression || !call.arguments.some((argument) => argument === fn)) {
@@ -6857,6 +6846,9 @@ var no_sleep_in_test_body_default = createRule({
6857
6846
  return {};
6858
6847
  }
6859
6848
  const report = (node) => {
6849
+ if (!isImmediatelyConsumedSleep(node)) {
6850
+ return;
6851
+ }
6860
6852
  const enclosing = nearestEnclosingFunction(node);
6861
6853
  if (enclosing === null || !isTestBody(enclosing)) {
6862
6854
  return;
@@ -7154,6 +7146,27 @@ function isSmallStaticForLoop(node) {
7154
7146
  const iterations = node.test.right.value - declaration.init.value + (node.test.operator === "<=" ? 1 : 0);
7155
7147
  return iterations >= 0 && iterations <= 8;
7156
7148
  }
7149
+ function isStringSeededReduce(node) {
7150
+ if (node.callee.type !== "MemberExpression" || node.callee.computed || node.callee.property.type !== "Identifier" || node.callee.property.name !== "reduce" || node.arguments.length !== 2) {
7151
+ return false;
7152
+ }
7153
+ const [callback, initial] = node.arguments;
7154
+ if (node.callee.object.type === "ArrayExpression" && node.callee.object.elements.length <= 8) {
7155
+ return false;
7156
+ }
7157
+ 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)) {
7158
+ return false;
7159
+ }
7160
+ const accumulator = callback.params[0].name;
7161
+ if (callback.body.type !== "BlockStatement") {
7162
+ return isConcatOntoTarget(callback.body, accumulator);
7163
+ }
7164
+ if (callback.body.body.length !== 1 || callback.body.body[0]?.type !== "ReturnStatement") {
7165
+ return false;
7166
+ }
7167
+ const returned = callback.body.body[0].argument;
7168
+ return returned !== null && isConcatOntoTarget(returned, accumulator);
7169
+ }
7157
7170
  var no_string_concat_in_loop_default = createRule({
7158
7171
  name: "no-string-concat-in-loop",
7159
7172
  documentation: noStringConcatInLoopDocumentation,
@@ -7164,7 +7177,8 @@ var no_string_concat_in_loop_default = createRule({
7164
7177
  },
7165
7178
  schema: [],
7166
7179
  messages: {
7167
- 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.'
7180
+ 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.',
7181
+ noStringReduce: "Avoid concatenating a growing string in `reduce` \u2014 this is O(n^2). Map the fragments and join them once instead."
7168
7182
  }
7169
7183
  },
7170
7184
  defaultOptions: [],
@@ -7174,6 +7188,11 @@ var no_string_concat_in_loop_default = createRule({
7174
7188
  }
7175
7189
  const reported = /* @__PURE__ */ new WeakMap();
7176
7190
  return {
7191
+ CallExpression(node) {
7192
+ if (isStringSeededReduce(node)) {
7193
+ context.report({ node, messageId: "noStringReduce" });
7194
+ }
7195
+ },
7177
7196
  AssignmentExpression(node) {
7178
7197
  if (node.left.type !== "Identifier") {
7179
7198
  return;
@@ -7278,6 +7297,9 @@ function isLiteral(node) {
7278
7297
  return false;
7279
7298
  }
7280
7299
  }
7300
+ function isStructuralLiteral(node) {
7301
+ return node.type === import_utils37.AST_NODE_TYPES.ArrayExpression || node.type === import_utils37.AST_NODE_TYPES.ObjectExpression;
7302
+ }
7281
7303
  function expectOperand(callee) {
7282
7304
  const receiver = callee.object;
7283
7305
  if (receiver.type !== import_utils37.AST_NODE_TYPES.CallExpression || receiver.callee.type !== import_utils37.AST_NODE_TYPES.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
@@ -7334,6 +7356,9 @@ var no_tautological_expect_default = createRule({
7334
7356
  if (!EQUALITY_MATCHERS.has(matcher) || node.arguments.length !== 1 || expected === void 0 || !isLiteral(expected)) {
7335
7357
  return;
7336
7358
  }
7359
+ if (matcher === "toBe" && (isStructuralLiteral(operand) || isStructuralLiteral(expected))) {
7360
+ return;
7361
+ }
7337
7362
  if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {
7338
7363
  return;
7339
7364
  }
@@ -7847,7 +7872,13 @@ var no_declaration_comment_wall_default = createRule({
7847
7872
  const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
7848
7873
  if (lead !== void 0) {
7849
7874
  const before = sourceCode.getTokenBefore(lead, { includeComments: false });
7850
- if (before === null || before.loc.end.line < lead.loc.start.line) return lead;
7875
+ if (before === null || before.loc.end.line < lead.loc.start.line) {
7876
+ const previousLine = endingOn.get(lead.loc.start.line - 1);
7877
+ if (lead.type === import_utils40.AST_TOKEN_TYPES.Line && previousLine?.type === import_utils40.AST_TOKEN_TYPES.Line && previousLine.loc.start.column === lead.loc.start.column) {
7878
+ return void 0;
7879
+ }
7880
+ return lead;
7881
+ }
7851
7882
  }
7852
7883
  const trail = startingOn.get(member.loc.end.line);
7853
7884
  return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
@@ -8149,7 +8180,13 @@ var no_type_member_comment_wall_default = createRule({
8149
8180
  const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
8150
8181
  if (lead !== void 0) {
8151
8182
  const before = sourceCode.getTokenBefore(lead, { includeComments: false });
8152
- if (before === null || before.loc.end.line < lead.loc.start.line) return lead;
8183
+ if (before === null || before.loc.end.line < lead.loc.start.line) {
8184
+ const previousLine = endingOn.get(lead.loc.start.line - 1);
8185
+ if (lead.type === import_utils42.AST_TOKEN_TYPES.Line && previousLine?.type === import_utils42.AST_TOKEN_TYPES.Line && previousLine.loc.start.column === lead.loc.start.column) {
8186
+ return void 0;
8187
+ }
8188
+ return lead;
8189
+ }
8153
8190
  }
8154
8191
  const trail = startingOn.get(member.loc.end.line);
8155
8192
  return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
@@ -8446,6 +8483,10 @@ var no_unnecessary_use_client_default = createRule({
8446
8483
  var import_utils44 = require("@typescript-eslint/utils");
8447
8484
  var MOCK_TYPE_NAMES = /* @__PURE__ */ new Set([
8448
8485
  "Mock",
8486
+ "Mocked",
8487
+ "MockedClass",
8488
+ "MockedFunction",
8489
+ "MockedObject",
8449
8490
  "MockInstance",
8450
8491
  "SpyInstance"
8451
8492
  ]);
@@ -8689,9 +8730,9 @@ var no_zod_native_enum_default = createRule({
8689
8730
  }
8690
8731
  function isZodMemberCall(node, api) {
8691
8732
  const callee = node.callee;
8692
- if (callee.type === import_utils45.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils45.AST_NODE_TYPES.Identifier && callee.property.type === import_utils45.AST_NODE_TYPES.Identifier) {
8733
+ if (callee.type === import_utils45.AST_NODE_TYPES.MemberExpression && callee.object.type === import_utils45.AST_NODE_TYPES.Identifier && (callee.property.type === import_utils45.AST_NODE_TYPES.Identifier && !callee.computed && callee.property.name === api || callee.computed && callee.property.type === import_utils45.AST_NODE_TYPES.Literal && callee.property.value === api)) {
8693
8734
  const binding = resolvedBinding(callee.object);
8694
- return binding !== null && zodNamespaceBindings.has(binding) && callee.property.name === api;
8735
+ return binding !== null && zodNamespaceBindings.has(binding);
8695
8736
  }
8696
8737
  if (callee.type === import_utils45.AST_NODE_TYPES.Identifier) {
8697
8738
  const binding = resolvedBinding(callee);
@@ -9535,6 +9576,27 @@ var prefer_immutable_module_constant_default = createRule({
9535
9576
  }
9536
9577
  const exportedNames2 = /* @__PURE__ */ new Set();
9537
9578
  const typeAliases2 = /* @__PURE__ */ new Map();
9579
+ const mutatesThroughConstAlias = (root) => {
9580
+ const pending = [root];
9581
+ const seen = /* @__PURE__ */ new Set();
9582
+ while (pending.length > 0) {
9583
+ const variable = pending.pop();
9584
+ if (variable === void 0 || seen.has(variable)) continue;
9585
+ seen.add(variable);
9586
+ for (const reference of variable.references) {
9587
+ const identifier = reference.identifier;
9588
+ if (identifier.type !== import_utils51.AST_NODE_TYPES.Identifier) continue;
9589
+ if (referenceMutates(identifier, isUnshadowedGlobal)) return true;
9590
+ const declarator = identifier.parent;
9591
+ if (declarator.type !== import_utils51.AST_NODE_TYPES.VariableDeclarator || declarator.init !== identifier || declarator.id.type !== import_utils51.AST_NODE_TYPES.Identifier || declarator.parent.type !== import_utils51.AST_NODE_TYPES.VariableDeclaration || declarator.parent.kind !== "const") {
9592
+ continue;
9593
+ }
9594
+ const alias = sourceCode.getDeclaredVariables(declarator)[0];
9595
+ if (alias !== void 0) pending.push(alias);
9596
+ }
9597
+ }
9598
+ return false;
9599
+ };
9538
9600
  return {
9539
9601
  Program(node) {
9540
9602
  for (const statement of node.body) {
@@ -9575,9 +9637,7 @@ var prefer_immutable_module_constant_default = createRule({
9575
9637
  return;
9576
9638
  }
9577
9639
  const variable = sourceCode.getDeclaredVariables(node)[0];
9578
- if (!directlyExported && !exportedNames2.has(node.id.name) && variable?.references.some(
9579
- (reference) => reference.identifier.type === import_utils51.AST_NODE_TYPES.Identifier && referenceMutates(reference.identifier, isUnshadowedGlobal)
9580
- ) === true) {
9640
+ if (!directlyExported && !exportedNames2.has(node.id.name) && variable !== void 0 && mutatesThroughConstAlias(variable)) {
9581
9641
  return;
9582
9642
  }
9583
9643
  context.report({
@@ -10356,6 +10416,10 @@ var prefer_module_level_schema_default = createRule({
10356
10416
  }
10357
10417
  for (const definition of resolved.defs) {
10358
10418
  if (definition.type === "ImportBinding") {
10419
+ const parent = reference.identifier.parent;
10420
+ if (parent?.type === import_utils54.AST_NODE_TYPES.CallExpression && parent.callee === reference.identifier && !zodNamespaces.has(reference.identifier.name)) {
10421
+ return false;
10422
+ }
10359
10423
  continue;
10360
10424
  }
10361
10425
  if (definition.node.type === import_utils54.AST_NODE_TYPES.VariableDeclarator && definition.node.parent.type === import_utils54.AST_NODE_TYPES.VariableDeclaration && definition.node.parent.kind !== "const") {
@@ -12187,10 +12251,7 @@ var SHAPE_PRESERVING_METHODS = /* @__PURE__ */ new Set([
12187
12251
  ]);
12188
12252
  var OPTIONAL_MODIFIERS = /* @__PURE__ */ new Set([
12189
12253
  "optional",
12190
- "nullish",
12191
- "default",
12192
- "prefault",
12193
- "catch"
12254
+ "nullish"
12194
12255
  ]);
12195
12256
  var NULLABLE_MODIFIERS = /* @__PURE__ */ new Set(["nullable", "nullish"]);
12196
12257
  var RESHAPING_MODIFIERS = /* @__PURE__ */ new Set([
@@ -13459,6 +13520,7 @@ var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
13459
13520
  "parseAsync",
13460
13521
  "safeParseAsync"
13461
13522
  ]);
13523
+ var FORM_VALUE_METHODS = /* @__PURE__ */ new Set(["get", "getAll"]);
13462
13524
  var zodReceiverRoot = (node) => {
13463
13525
  let current = node;
13464
13526
  while (true) {
@@ -13533,7 +13595,7 @@ var require_zod_form_validation_default = createRule({
13533
13595
  };
13534
13596
  const isFormSourceIdentifier = (node) => {
13535
13597
  if (node.type !== import_utils66.AST_NODE_TYPES.Identifier) return false;
13536
- if (/formdata/i.test(node.name)) return true;
13598
+ const conventionalName = /formdata/i.test(node.name);
13537
13599
  let scope = context.sourceCode.getScope(node);
13538
13600
  while (scope !== null) {
13539
13601
  const variable = scope.set.get(node.name);
@@ -13542,16 +13604,16 @@ var require_zod_form_validation_default = createRule({
13542
13604
  if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils66.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
13543
13605
  return isFormDataMethodCall(def.node.init);
13544
13606
  }
13545
- return false;
13607
+ return def?.type === "Parameter" && conventionalName;
13546
13608
  }
13547
13609
  scope = scope.upper;
13548
13610
  }
13549
- return false;
13611
+ return conventionalName;
13550
13612
  };
13551
13613
  const isFormDataGetCall = (node) => {
13552
13614
  const callee = node.callee;
13553
13615
  if (callee.type !== import_utils66.AST_NODE_TYPES.MemberExpression) return false;
13554
- if (callee.property.type !== import_utils66.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
13616
+ if (callee.property.type !== import_utils66.AST_NODE_TYPES.Identifier || !FORM_VALUE_METHODS.has(callee.property.name)) {
13555
13617
  return false;
13556
13618
  }
13557
13619
  return isFormSourceIdentifier(callee.object);
@@ -13645,6 +13707,40 @@ var require_zod_form_validation_default = createRule({
13645
13707
  }
13646
13708
  return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === import_utils66.AST_NODE_TYPES.Literal && parent.right.value === null || parent.right.type === import_utils66.AST_NODE_TYPES.Identifier && parent.right.name === "undefined");
13647
13709
  };
13710
+ const isDescendantOf = (node, ancestor) => {
13711
+ let current = node;
13712
+ while (current !== void 0 && current !== null) {
13713
+ if (current === ancestor) return true;
13714
+ current = current.parent;
13715
+ }
13716
+ return false;
13717
+ };
13718
+ const blockTerminates = (node) => {
13719
+ if (node.type === import_utils66.AST_NODE_TYPES.ReturnStatement || node.type === import_utils66.AST_NODE_TYPES.ThrowStatement) {
13720
+ return true;
13721
+ }
13722
+ if (node.type !== import_utils66.AST_NODE_TYPES.BlockStatement || node.body.length === 0) return false;
13723
+ const last = node.body.at(-1);
13724
+ return last !== void 0 && blockTerminates(last);
13725
+ };
13726
+ const narrowingIf = (identifier) => {
13727
+ const comparison = identifier.parent;
13728
+ if (comparison?.type !== import_utils66.AST_NODE_TYPES.BinaryExpression || comparison.operator !== "instanceof" || comparison.left !== identifier || comparison.right.type !== import_utils66.AST_NODE_TYPES.Identifier || comparison.right.name !== "File" && comparison.right.name !== "Blob") {
13729
+ return null;
13730
+ }
13731
+ const maybeNegation = comparison.parent;
13732
+ const negated = maybeNegation?.type === import_utils66.AST_NODE_TYPES.UnaryExpression && maybeNegation.operator === "!";
13733
+ const test = negated ? maybeNegation : comparison;
13734
+ const branch = test.parent;
13735
+ return branch?.type === import_utils66.AST_NODE_TYPES.IfStatement && branch.test === test ? { branch, positive: !negated } : null;
13736
+ };
13737
+ const useDominatedByNarrowing = (use, narrowings) => narrowings.some(({ branch, positive }) => {
13738
+ if (positive) return isDescendantOf(use, branch.consequent);
13739
+ if (!blockTerminates(branch.consequent)) return false;
13740
+ const branchStatement = containingStatement(branch);
13741
+ const useStatement = containingStatement(use);
13742
+ return branchStatement !== null && useStatement !== null && branchStatement.parent === useStatement.parent && branchStatement.range[1] < useStatement.range[0];
13743
+ });
13648
13744
  const statementWithinBlock = (node, block) => {
13649
13745
  let current = node;
13650
13746
  while (current.parent !== void 0 && current.parent !== block) {
@@ -13659,14 +13755,16 @@ var require_zod_form_validation_default = createRule({
13659
13755
  (identifier) => identifier.type === import_utils66.AST_NODE_TYPES.Identifier
13660
13756
  );
13661
13757
  if (references.length === 0) return false;
13662
- if (references.some(isInstanceofNarrowing)) return true;
13758
+ const narrowings = references.map(narrowingIf).filter(
13759
+ (value) => value !== null
13760
+ );
13663
13761
  const validationStatements = references.map((reference) => guaranteedValidationStatement(declarator, reference)).filter(
13664
13762
  (statement) => statement !== null
13665
13763
  );
13666
13764
  const declarationStatement = containingStatement(declarator);
13667
13765
  const declarationBlock = declarationStatement?.parent;
13668
13766
  return references.every((reference) => {
13669
- if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference)) {
13767
+ if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference) || useDominatedByNarrowing(reference, narrowings)) {
13670
13768
  return true;
13671
13769
  }
13672
13770
  if (declarationBlock === void 0) return false;
@@ -13769,8 +13867,13 @@ var import_utils68 = require("@typescript-eslint/utils");
13769
13867
  var stepdownDocumentation = {
13770
13868
  summary: "Place a private helper below its sole direct same-scope caller.",
13771
13869
  rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
13772
- remediation: "Move the private helper immediately below its sole caller.",
13870
+ remediation: "Move the private helper below its sole caller.",
13773
13871
  category: "maintainability",
13872
+ limitations: [
13873
+ "Generated and test files, cycles, dynamic references, overload targets, and helpers with multiple callers are excluded.",
13874
+ "Class methods are reported only when both helper and caller are private so accessibility ordering remains authoritative.",
13875
+ "Runtime class-field, static-block, computed-member, and decorator barriers are never crossed."
13876
+ ],
13774
13877
  examples: [
13775
13878
  { 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 },
13776
13879
  { 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 }
@@ -13779,7 +13882,7 @@ var stepdownDocumentation = {
13779
13882
  function isFunction(node) {
13780
13883
  return node.type === import_utils68.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils68.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils68.AST_NODE_TYPES.FunctionExpression;
13781
13884
  }
13782
- function reportMisordered(context, candidates, scopeDefinitions, calls, pinned) {
13885
+ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
13783
13886
  const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
13784
13887
  const cycles = cycleComponents(calls);
13785
13888
  const callers = /* @__PURE__ */ new Map();
@@ -13797,7 +13900,7 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned)
13797
13900
  const callerName2 = helperCallers[0];
13798
13901
  if (callerName2 === void 0 || cycles.has(helper.name) && cycles.get(helper.name) === cycles.get(callerName2)) continue;
13799
13902
  const caller = byName.get(callerName2);
13800
- if (caller === void 0 || helper.node.range[0] >= caller.node.range[0]) continue;
13903
+ if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
13801
13904
  context.report({
13802
13905
  node: helper.node,
13803
13906
  messageId: "helperAboveOnlyCaller",
@@ -13877,8 +13980,10 @@ function moduleScope(context, program) {
13877
13980
  })
13878
13981
  );
13879
13982
  const exported = exportedNames(program);
13880
- const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1 && !overloadNames.has(node.name));
13881
- const definitions = scopeDefinitions.filter((definition) => !exported.has(definition.name));
13983
+ const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1);
13984
+ const definitions = scopeDefinitions.filter(
13985
+ (definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
13986
+ );
13882
13987
  const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
13883
13988
  const calls = /* @__PURE__ */ new Map();
13884
13989
  const pinned = /* @__PURE__ */ new Set();
@@ -14122,11 +14227,36 @@ function classScope(context, node, computedReferenceNames) {
14122
14227
  return [definition.name, accessibility2];
14123
14228
  })
14124
14229
  );
14230
+ const methodByName = new Map(scopeDefinitions.map((definition) => [definition.name, definition.node]));
14125
14231
  for (const [caller, callees] of calls) {
14126
- if (accessibility.get(caller) === "private") continue;
14232
+ const callerMethod = methodByName.get(caller);
14233
+ if (accessibility.get(caller) === "private" && callerMethod?.type === import_utils68.AST_NODE_TYPES.MethodDefinition && callerMethod.decorators.length === 0) continue;
14127
14234
  for (const callee of callees) pinned.add(callee);
14128
14235
  }
14129
- reportMisordered(context, definitions, scopeDefinitions, calls, pinned);
14236
+ const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
14237
+ const runtimeBarrierPrefix = [0];
14238
+ for (const member of node.body.body) {
14239
+ runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
14240
+ }
14241
+ reportMisordered(context, definitions, scopeDefinitions, calls, pinned, (helper, caller) => {
14242
+ const helperIndex = memberIndexes.get(helper.node);
14243
+ const callerIndex = memberIndexes.get(caller.node);
14244
+ if (helperIndex === void 0 || callerIndex === void 0) return false;
14245
+ return runtimeBarrierPrefix[callerIndex + 1] === runtimeBarrierPrefix[helperIndex + 1];
14246
+ });
14247
+ }
14248
+ function isClassRuntimeBarrier(member) {
14249
+ switch (member.type) {
14250
+ case import_utils68.AST_NODE_TYPES.StaticBlock:
14251
+ return true;
14252
+ case import_utils68.AST_NODE_TYPES.PropertyDefinition:
14253
+ case import_utils68.AST_NODE_TYPES.AccessorProperty:
14254
+ return member.static || member.computed || member.decorators.length > 0 || member.value !== null;
14255
+ case import_utils68.AST_NODE_TYPES.MethodDefinition:
14256
+ return member.computed || member.decorators.length > 0;
14257
+ default:
14258
+ return false;
14259
+ }
14130
14260
  }
14131
14261
  var stepdown_default = createRule({
14132
14262
  name: "stepdown",
@@ -14196,7 +14326,13 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
14196
14326
  "safeDecodeAsync",
14197
14327
  "toJSONSchema",
14198
14328
  "registry",
14199
- "implement"
14329
+ "implement",
14330
+ "flattenError",
14331
+ "formatError",
14332
+ "isNullable",
14333
+ "isOptional",
14334
+ "prettifyError",
14335
+ "treeifyError"
14200
14336
  ]);
14201
14337
  var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils69.AST_NODE_TYPES.Identifier ? callee.property.name : null;
14202
14338
  var calleeChainRoot = (node) => {
@@ -14445,7 +14581,7 @@ var rules = {
14445
14581
  };
14446
14582
  var meta = {
14447
14583
  name: "@sarj/eslint-plugin",
14448
- version: "15.4.0"
14584
+ version: "15.6.0"
14449
14585
  };
14450
14586
  var applicationOnlyRules = [
14451
14587
  "no-restricted-library-load",