@sarj/eslint-plugin 15.8.2 → 15.9.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
@@ -4718,11 +4718,11 @@ var no_offset_pagination_default = createRule({
4718
4718
  // src/rules/no-positional-tuple-return.ts
4719
4719
  var import_utils24 = require("@typescript-eslint/utils");
4720
4720
  var noPositionalTupleReturnDocumentation = {
4721
- summary: "Disallow returning a multi-field tuple from an exported function; return a named object so call sites cannot mismatch slots.",
4722
- rationale: "Public tuple fields are identified only by position, so reordering can preserve types while changing meaning.",
4721
+ summary: "Disallow returning a multi-field tuple from a named function; return a named object so call sites cannot mismatch slots.",
4722
+ rationale: "Tuple fields are identified only by position, so reordering can preserve types while changing meaning.",
4723
4723
  remediation: "Return an object whose property names describe each value.",
4724
4724
  category: "maintainability",
4725
- limitations: ["Only declared multi-field tuple returns on public TypeScript surfaces are inspected."],
4725
+ limitations: ["Declared or syntax-proven multi-field tuple returns on named functions and public type surfaces are inspected; anonymous inline callbacks and syntax-proven TanStack Query key factories are excluded."],
4726
4726
  examples: [
4727
4727
  { id: "named-object-return", title: "Return named fields", outcome: "no-match", files: [{ path: "src/download.ts", source: "export function download(): { body: string; status: number } { return impl(); }" }], focusPath: "src/download.ts", expectedCount: 0, public: true },
4728
4728
  { id: "tuple-return", title: "Do not expose positional fields", outcome: "match", files: [{ path: "src/download.ts", source: "export function download(): [string, number] { return impl(); }" }], focusPath: "src/download.ts", expectedCount: 1, public: true }
@@ -4760,6 +4760,16 @@ function tupleReturnType(node, aliases, resolving = /* @__PURE__ */ new Set()) {
4760
4760
  }
4761
4761
  return null;
4762
4762
  }
4763
+ function tupleExpression(node, aliases) {
4764
+ if (node.type !== import_utils24.AST_NODE_TYPES.TSAsExpression && node.type !== import_utils24.AST_NODE_TYPES.TSSatisfiesExpression) {
4765
+ return null;
4766
+ }
4767
+ if (node.expression.type !== import_utils24.AST_NODE_TYPES.ArrayExpression || node.expression.elements.length < MIN_ELEMENTS) {
4768
+ return null;
4769
+ }
4770
+ if (node.type === import_utils24.AST_NODE_TYPES.TSAsExpression && node.typeAnnotation.type === import_utils24.AST_NODE_TYPES.TSTypeReference && node.typeAnnotation.typeName.type === import_utils24.AST_NODE_TYPES.Identifier && node.typeAnnotation.typeName.name === "const") return node.expression;
4771
+ return tupleReturnType(node.typeAnnotation, aliases) === null ? null : node.expression;
4772
+ }
4763
4773
  function functionName(node) {
4764
4774
  if (node.type === import_utils24.AST_NODE_TYPES.FunctionDeclaration) {
4765
4775
  if (node.id !== null) return node.id.name;
@@ -4775,60 +4785,25 @@ function functionName(node) {
4775
4785
  return parent.id.name;
4776
4786
  }
4777
4787
  if ((parent?.type === import_utils24.AST_NODE_TYPES.MethodDefinition || parent?.type === import_utils24.AST_NODE_TYPES.TSAbstractMethodDefinition || parent?.type === import_utils24.AST_NODE_TYPES.PropertyDefinition || parent?.type === import_utils24.AST_NODE_TYPES.Property) && parent.key.type === import_utils24.AST_NODE_TYPES.Identifier) {
4778
- if ((parent.type === import_utils24.AST_NODE_TYPES.MethodDefinition || parent.type === import_utils24.AST_NODE_TYPES.TSAbstractMethodDefinition || parent.type === import_utils24.AST_NODE_TYPES.PropertyDefinition) && (parent.accessibility === "private" || parent.accessibility === "protected")) return null;
4779
4788
  return parent.key.name;
4780
4789
  }
4781
4790
  return null;
4782
4791
  }
4783
- function isInlineExported(node) {
4784
- if (moduleScopeBindingName(node) === null) return false;
4785
- for (let current = node; current != null; current = current.parent) {
4786
- const parent = current.parent;
4787
- if (parent?.type === import_utils24.AST_NODE_TYPES.ExportNamedDeclaration || parent?.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration) {
4788
- return true;
4789
- }
4790
- }
4791
- return false;
4792
- }
4793
- function moduleScopeBindingName(node) {
4794
- let current = node;
4795
- while (current.parent != null && current.parent.type !== import_utils24.AST_NODE_TYPES.Program) {
4796
- current = current.parent;
4797
- }
4798
- if (current.parent?.type !== import_utils24.AST_NODE_TYPES.Program) {
4799
- return null;
4800
- }
4801
- let topLevel = current;
4802
- if (current.type === import_utils24.AST_NODE_TYPES.ExportNamedDeclaration || current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration) {
4803
- topLevel = current.declaration;
4804
- }
4805
- if (topLevel === null) return null;
4806
- if (topLevel.type === import_utils24.AST_NODE_TYPES.FunctionDeclaration) {
4807
- if (topLevel !== node) return null;
4808
- return topLevel.id?.name ?? (current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration ? "default" : null);
4809
- }
4810
- if ((topLevel.type === import_utils24.AST_NODE_TYPES.ArrowFunctionExpression || topLevel.type === import_utils24.AST_NODE_TYPES.FunctionExpression) && topLevel === node && current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration) {
4811
- return "default";
4812
- }
4813
- if (topLevel.type === import_utils24.AST_NODE_TYPES.ClassDeclaration) {
4814
- let owner = node.parent;
4815
- while (owner != null && owner.parent !== topLevel.body) owner = owner.parent;
4816
- return (owner?.type === import_utils24.AST_NODE_TYPES.MethodDefinition || owner?.type === import_utils24.AST_NODE_TYPES.TSAbstractMethodDefinition || owner?.type === import_utils24.AST_NODE_TYPES.PropertyDefinition) && owner.value === node ? topLevel.id?.name ?? (current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration ? "default" : null) : null;
4817
- }
4818
- if (topLevel.type === import_utils24.AST_NODE_TYPES.VariableDeclaration) {
4819
- for (const declarator of topLevel.declarations) {
4820
- let initializer = declarator.init;
4821
- while (initializer?.type === import_utils24.AST_NODE_TYPES.TSAsExpression || initializer?.type === import_utils24.AST_NODE_TYPES.TSSatisfiesExpression || initializer?.type === import_utils24.AST_NODE_TYPES.TSNonNullExpression) initializer = initializer.expression;
4822
- if (declarator.id.type === import_utils24.AST_NODE_TYPES.Identifier && initializer === node) return declarator.id.name;
4823
- if (declarator.id.type === import_utils24.AST_NODE_TYPES.Identifier && (initializer?.type === import_utils24.AST_NODE_TYPES.ClassExpression || initializer?.type === import_utils24.AST_NODE_TYPES.ObjectExpression)) {
4824
- let owner = node.parent;
4825
- const container = initializer.type === import_utils24.AST_NODE_TYPES.ClassExpression ? initializer.body : initializer;
4826
- while (owner != null && owner.parent !== container) owner = owner.parent;
4827
- if ((owner?.type === import_utils24.AST_NODE_TYPES.MethodDefinition || owner?.type === import_utils24.AST_NODE_TYPES.PropertyDefinition || owner?.type === import_utils24.AST_NODE_TYPES.Property) && owner.value === node) return declarator.id.name;
4828
- }
4829
- }
4830
- }
4831
- return null;
4792
+ function isQueryKeyFactory(node) {
4793
+ let wrapped = node;
4794
+ while ((wrapped.parent?.type === import_utils24.AST_NODE_TYPES.TSAsExpression || wrapped.parent?.type === import_utils24.AST_NODE_TYPES.TSSatisfiesExpression || wrapped.parent?.type === import_utils24.AST_NODE_TYPES.TSNonNullExpression) && wrapped.parent.expression === wrapped) wrapped = wrapped.parent;
4795
+ const property = wrapped.parent;
4796
+ if (property?.type !== import_utils24.AST_NODE_TYPES.Property || property.value !== wrapped) return false;
4797
+ const object = property.parent;
4798
+ if (object.type !== import_utils24.AST_NODE_TYPES.ObjectExpression) return false;
4799
+ const assertion = object.parent;
4800
+ if (assertion.type !== import_utils24.AST_NODE_TYPES.TSAsExpression || assertion.expression !== object || assertion.typeAnnotation.type !== import_utils24.AST_NODE_TYPES.TSTypeReference || assertion.typeAnnotation.typeName.type !== import_utils24.AST_NODE_TYPES.Identifier || assertion.typeAnnotation.typeName.name !== "const") return false;
4801
+ const declarator = assertion.parent;
4802
+ if (declarator.type !== import_utils24.AST_NODE_TYPES.VariableDeclarator || declarator.id.type !== import_utils24.AST_NODE_TYPES.Identifier || !/Keys$/i.test(declarator.id.name) || declarator.parent.type !== import_utils24.AST_NODE_TYPES.VariableDeclaration || declarator.parent.kind !== "const") return false;
4803
+ return object.properties.some((candidate) => {
4804
+ if (candidate.type !== import_utils24.AST_NODE_TYPES.Property || staticMemberName3(candidate.key) !== "all") return false;
4805
+ return candidate.value.type === import_utils24.AST_NODE_TYPES.TSAsExpression && candidate.value.expression.type === import_utils24.AST_NODE_TYPES.ArrayExpression && candidate.value.typeAnnotation.type === import_utils24.AST_NODE_TYPES.TSTypeReference && candidate.value.typeAnnotation.typeName.type === import_utils24.AST_NODE_TYPES.Identifier && candidate.value.typeAnnotation.typeName.name === "const";
4806
+ });
4832
4807
  }
4833
4808
  function specifierExportedNames(program) {
4834
4809
  const names = /* @__PURE__ */ new Set();
@@ -4937,18 +4912,52 @@ function isExportedClass(node, specifierExports) {
4937
4912
  if (node.parent.type === import_utils24.AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === import_utils24.AST_NODE_TYPES.Identifier) return specifierExports.has(node.parent.id.name) || isInlineExported(node);
4938
4913
  return false;
4939
4914
  }
4940
- function isExportedInterface(node, exports2) {
4941
- return node.parent.type === import_utils24.AST_NODE_TYPES.ExportNamedDeclaration || node.parent.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration || node.parent.type === import_utils24.AST_NODE_TYPES.Program && exports2.has(node.id.name);
4915
+ function isInlineExported(node) {
4916
+ if (moduleScopeBindingName(node) === null) return false;
4917
+ for (let current = node; current != null; current = current.parent) {
4918
+ const parent = current.parent;
4919
+ if (parent?.type === import_utils24.AST_NODE_TYPES.ExportNamedDeclaration || parent?.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration) {
4920
+ return true;
4921
+ }
4922
+ }
4923
+ return false;
4942
4924
  }
4943
- function isExported(node, specifierExports) {
4944
- if (isInlineExported(node)) {
4945
- return true;
4925
+ function moduleScopeBindingName(node) {
4926
+ let current = node;
4927
+ while (current.parent != null && current.parent.type !== import_utils24.AST_NODE_TYPES.Program) {
4928
+ current = current.parent;
4946
4929
  }
4947
- if (specifierExports.size === 0) {
4948
- return false;
4930
+ if (current.parent?.type !== import_utils24.AST_NODE_TYPES.Program) return null;
4931
+ let topLevel = current;
4932
+ if (current.type === import_utils24.AST_NODE_TYPES.ExportNamedDeclaration || current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration) topLevel = current.declaration;
4933
+ if (topLevel === null) return null;
4934
+ if (topLevel.type === import_utils24.AST_NODE_TYPES.FunctionDeclaration) {
4935
+ if (topLevel !== node) return null;
4936
+ return topLevel.id?.name ?? (current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration ? "default" : null);
4937
+ }
4938
+ if ((topLevel.type === import_utils24.AST_NODE_TYPES.ArrowFunctionExpression || topLevel.type === import_utils24.AST_NODE_TYPES.FunctionExpression) && topLevel === node && current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration) return "default";
4939
+ if (topLevel.type === import_utils24.AST_NODE_TYPES.ClassDeclaration) {
4940
+ let owner = node.parent;
4941
+ while (owner != null && owner.parent !== topLevel.body) owner = owner.parent;
4942
+ return (owner?.type === import_utils24.AST_NODE_TYPES.MethodDefinition || owner?.type === import_utils24.AST_NODE_TYPES.TSAbstractMethodDefinition || owner?.type === import_utils24.AST_NODE_TYPES.PropertyDefinition) && owner.value === node ? topLevel.id?.name ?? (current.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration ? "default" : null) : null;
4943
+ }
4944
+ if (topLevel.type === import_utils24.AST_NODE_TYPES.VariableDeclaration) {
4945
+ for (const declarator of topLevel.declarations) {
4946
+ let initializer = declarator.init;
4947
+ while (initializer?.type === import_utils24.AST_NODE_TYPES.TSAsExpression || initializer?.type === import_utils24.AST_NODE_TYPES.TSSatisfiesExpression || initializer?.type === import_utils24.AST_NODE_TYPES.TSNonNullExpression) initializer = initializer.expression;
4948
+ if (declarator.id.type === import_utils24.AST_NODE_TYPES.Identifier && initializer === node) return declarator.id.name;
4949
+ if (declarator.id.type === import_utils24.AST_NODE_TYPES.Identifier && (initializer?.type === import_utils24.AST_NODE_TYPES.ClassExpression || initializer?.type === import_utils24.AST_NODE_TYPES.ObjectExpression)) {
4950
+ let owner = node.parent;
4951
+ const container = initializer.type === import_utils24.AST_NODE_TYPES.ClassExpression ? initializer.body : initializer;
4952
+ while (owner != null && owner.parent !== container) owner = owner.parent;
4953
+ if ((owner?.type === import_utils24.AST_NODE_TYPES.MethodDefinition || owner?.type === import_utils24.AST_NODE_TYPES.PropertyDefinition || owner?.type === import_utils24.AST_NODE_TYPES.Property) && owner.value === node) return declarator.id.name;
4954
+ }
4955
+ }
4949
4956
  }
4950
- const binding = moduleScopeBindingName(node);
4951
- return binding !== null && specifierExports.has(binding);
4957
+ return null;
4958
+ }
4959
+ function isExportedInterface(node, exports2) {
4960
+ return node.parent.type === import_utils24.AST_NODE_TYPES.ExportNamedDeclaration || node.parent.type === import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration || node.parent.type === import_utils24.AST_NODE_TYPES.Program && exports2.has(node.id.name);
4952
4961
  }
4953
4962
  var no_positional_tuple_return_default = createRule({
4954
4963
  name: "no-positional-tuple-return",
@@ -4956,11 +4965,11 @@ var no_positional_tuple_return_default = createRule({
4956
4965
  meta: {
4957
4966
  type: "suggestion",
4958
4967
  docs: {
4959
- description: "Disallow returning a multi-field tuple from an exported function; return a named object so call sites cannot mismatch slots."
4968
+ description: "Disallow returning a multi-field tuple from a named function; return a named object so call sites cannot mismatch slots."
4960
4969
  },
4961
4970
  schema: [],
4962
4971
  messages: {
4963
- noPositionalTupleReturn: "Exported `{{name}}` returns a {{count}}-field tuple, so consumers depend on positional slots that can be reordered silently. Return a named object instead."
4972
+ noPositionalTupleReturn: "`{{name}}` returns a {{count}}-field tuple, so callers depend on positional slots that can be reordered silently. Return a named object instead."
4964
4973
  }
4965
4974
  },
4966
4975
  defaultOptions: [],
@@ -4972,6 +4981,8 @@ var no_positional_tuple_return_default = createRule({
4972
4981
  exportedTypeNames(context.sourceCode.ast)
4973
4982
  );
4974
4983
  const aliases = typeAliases(context.sourceCode.ast);
4984
+ const reportedFunctions = /* @__PURE__ */ new WeakSet();
4985
+ const functionStack = [];
4975
4986
  const report = (annotation, name) => {
4976
4987
  const tuple = tupleReturnType(annotation, aliases);
4977
4988
  if (tuple === null || tuple.elementTypes.length < MIN_ELEMENTS) return;
@@ -4981,25 +4992,54 @@ var no_positional_tuple_return_default = createRule({
4981
4992
  data: { name, count: String(tuple.elementTypes.length) }
4982
4993
  });
4983
4994
  };
4995
+ const reportExpression = (node, expression) => {
4996
+ if (reportedFunctions.has(node)) return;
4997
+ const tuple = tupleExpression(expression, aliases);
4998
+ const name = functionName(node);
4999
+ if (tuple === null || name === null) return;
5000
+ reportedFunctions.add(node);
5001
+ context.report({
5002
+ node: tuple,
5003
+ messageId: "noPositionalTupleReturn",
5004
+ data: { name, count: String(tuple.elements.length) }
5005
+ });
5006
+ };
4984
5007
  const check = (node) => {
5008
+ if (isQueryKeyFactory(node)) return;
4985
5009
  const annotation = node.returnType?.typeAnnotation;
4986
5010
  if (annotation === void 0) {
5011
+ if (node.type === import_utils24.AST_NODE_TYPES.ArrowFunctionExpression && node.expression) {
5012
+ reportExpression(node, node.body);
5013
+ }
4987
5014
  return;
4988
5015
  }
4989
5016
  const name = functionName(node);
4990
5017
  if (name === null) {
4991
5018
  return;
4992
5019
  }
4993
- if (!isExported(node, specifierExports)) {
4994
- return;
4995
- }
4996
5020
  report(annotation, name);
4997
5021
  };
5022
+ const enterFunction = (node) => {
5023
+ functionStack.push(node);
5024
+ check(node);
5025
+ };
5026
+ const exitFunction = () => {
5027
+ functionStack.pop();
5028
+ };
4998
5029
  return {
4999
- FunctionDeclaration: check,
5000
- FunctionExpression: check,
5001
- ArrowFunctionExpression: check,
5002
- TSEmptyBodyFunctionExpression: check,
5030
+ FunctionDeclaration: enterFunction,
5031
+ "FunctionDeclaration:exit": exitFunction,
5032
+ FunctionExpression: enterFunction,
5033
+ "FunctionExpression:exit": exitFunction,
5034
+ ArrowFunctionExpression: enterFunction,
5035
+ "ArrowFunctionExpression:exit": exitFunction,
5036
+ TSEmptyBodyFunctionExpression: enterFunction,
5037
+ "TSEmptyBodyFunctionExpression:exit": exitFunction,
5038
+ ReturnStatement(node) {
5039
+ const owner = functionStack.at(-1);
5040
+ if (owner === void 0 || owner.returnType !== void 0 || node.argument === null) return;
5041
+ reportExpression(owner, node.argument);
5042
+ },
5003
5043
  TSDeclareFunction(node) {
5004
5044
  if (node.id === null || node.returnType === void 0 || node.parent.type !== import_utils24.AST_NODE_TYPES.ExportNamedDeclaration && node.parent.type !== import_utils24.AST_NODE_TYPES.ExportDefaultDeclaration && !specifierExports.has(node.id.name)) return;
5005
5045
  report(node.returnType.typeAnnotation, node.id.name);
@@ -11180,7 +11220,8 @@ var preferAwaitInAsyncReturnDocumentation = {
11180
11220
  since: "15.6.3",
11181
11221
  limitations: [
11182
11222
  "Only a single directly returned `.then` call with an inline callback is checked.",
11183
- "The receiver must be proven Promise-like by TypeScript; untyped files and larger chains are intentionally ignored."
11223
+ "The receiver must be proven Promise-like by TypeScript; untyped files and larger chains are intentionally ignored.",
11224
+ "Direct loader callbacks passed to resolved `React.lazy` and `next/dynamic` imports are excluded because returning the module Promise is their framework contract."
11184
11225
  ],
11185
11226
  examples: [
11186
11227
  {
@@ -11209,19 +11250,19 @@ var preferAwaitInAsyncReturnDocumentation = {
11209
11250
  }
11210
11251
  ]
11211
11252
  };
11212
- function isDirectAsyncReturn(node) {
11253
+ function directAsyncReturnOwner(node) {
11213
11254
  const parent = node.parent;
11214
11255
  if (parent.type === import_utils59.AST_NODE_TYPES.ArrowFunctionExpression && parent.body === node) {
11215
- return parent.async && !parent.generator;
11256
+ return parent.async && !parent.generator ? parent : null;
11216
11257
  }
11217
11258
  if (parent.type !== import_utils59.AST_NODE_TYPES.ReturnStatement || parent.argument !== node) {
11218
- return false;
11259
+ return null;
11219
11260
  }
11220
11261
  let owner = parent.parent;
11221
11262
  while (owner !== void 0 && !isRuntimeFunction(owner)) {
11222
11263
  owner = owner.parent;
11223
11264
  }
11224
- return owner !== void 0 && owner.async && !owner.generator;
11265
+ return owner !== void 0 && owner.async && !owner.generator ? owner : null;
11225
11266
  }
11226
11267
  function isRuntimeFunction(node) {
11227
11268
  return node.type === import_utils59.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils59.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils59.AST_NODE_TYPES.FunctionExpression;
@@ -11278,9 +11319,33 @@ var prefer_await_in_async_return_default = createRule({
11278
11319
  services = null;
11279
11320
  }
11280
11321
  if (services === null) return {};
11322
+ const frameworkLoaders = /* @__PURE__ */ new Set();
11323
+ const rememberFrameworkLoader = (identifier) => {
11324
+ const variable = import_utils59.ASTUtils.findVariable(context.sourceCode.getScope(identifier), identifier.name);
11325
+ if (variable !== null) frameworkLoaders.add(variable);
11326
+ };
11327
+ const isFrameworkLoaderCallback = (owner) => {
11328
+ const parent = owner.parent;
11329
+ if (parent.type !== import_utils59.AST_NODE_TYPES.CallExpression || parent.arguments[0] !== owner || parent.callee.type !== import_utils59.AST_NODE_TYPES.Identifier) return false;
11330
+ const variable = import_utils59.ASTUtils.findVariable(context.sourceCode.getScope(parent.callee), parent.callee.name);
11331
+ return variable !== null && frameworkLoaders.has(variable);
11332
+ };
11281
11333
  return {
11334
+ ImportDeclaration(node) {
11335
+ if (node.source.value === "react") {
11336
+ for (const specifier of node.specifiers) {
11337
+ if (specifier.type === import_utils59.AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === import_utils59.AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value) === "lazy") rememberFrameworkLoader(specifier.local);
11338
+ }
11339
+ }
11340
+ if (node.source.value === "next/dynamic") {
11341
+ for (const specifier of node.specifiers) {
11342
+ if (specifier.type === import_utils59.AST_NODE_TYPES.ImportDefaultSpecifier) rememberFrameworkLoader(specifier.local);
11343
+ }
11344
+ }
11345
+ },
11282
11346
  CallExpression(node) {
11283
- if (!isDirectAsyncReturn(node)) return;
11347
+ const owner = directAsyncReturnOwner(node);
11348
+ if (owner === null || isFrameworkLoaderCallback(owner)) return;
11284
11349
  const receiver = promiseThenReceiver(node);
11285
11350
  if (receiver === null || !isProvenPromiseLike(receiver, services)) {
11286
11351
  return;
@@ -12489,6 +12554,16 @@ function literalIndex(node) {
12489
12554
  }
12490
12555
  return Number.isInteger(node.value) && node.value >= 0 ? node.value : null;
12491
12556
  }
12557
+ function propertyAccess(node) {
12558
+ const path = [];
12559
+ let current = node;
12560
+ while (current.type === import_utils63.AST_NODE_TYPES.MemberExpression && !current.computed && !current.optional) {
12561
+ if (current.property.type !== import_utils63.AST_NODE_TYPES.Identifier || COLLECTION_PROPERTIES.has(current.property.name) || LITERAL_KEY_HAZARDS.has(current.property.name)) return null;
12562
+ path.unshift(current.property.name);
12563
+ current = current.object;
12564
+ }
12565
+ return path.length > 0 && isPureReceiver(current) ? { receiver: current, path } : null;
12566
+ }
12492
12567
  var prefer_whole_object_assertion_default = createRule({
12493
12568
  name: "prefer-whole-object-assertion",
12494
12569
  documentation: preferWholeObjectAssertionDocumentation,
@@ -12535,21 +12610,23 @@ var prefer_whole_object_assertion_default = createRule({
12535
12610
  return null;
12536
12611
  }
12537
12612
  let key;
12613
+ let receiver;
12538
12614
  if (actual.computed) {
12539
12615
  const index = literalIndex(actual.property);
12540
12616
  if (index === null) {
12541
12617
  return null;
12542
12618
  }
12543
12619
  key = { kind: "index", index };
12620
+ receiver = actual.object;
12544
12621
  } else {
12545
- if (actual.property.type !== import_utils63.AST_NODE_TYPES.Identifier || COLLECTION_PROPERTIES.has(actual.property.name) || LITERAL_KEY_HAZARDS.has(actual.property.name)) {
12546
- return null;
12547
- }
12548
- key = { kind: "property", name: actual.property.name };
12622
+ const access = propertyAccess(actual);
12623
+ if (access === null) return null;
12624
+ key = { kind: "property", path: access.path };
12625
+ receiver = access.receiver;
12549
12626
  }
12550
12627
  const synthetic = SYNTHETIC_LITERAL_MATCHERS.get(matcher);
12551
12628
  if (synthetic !== void 0 && call.arguments.length === 0) {
12552
- return { statement, receiver: actual.object, key, matcher, expectedText: synthetic, expectedIsLiteral: true };
12629
+ return { statement, receiver, key, matcher, expectedText: synthetic, expectedIsLiteral: true };
12553
12630
  }
12554
12631
  if (!MERGEABLE_MATCHERS.has(matcher)) {
12555
12632
  return null;
@@ -12561,7 +12638,7 @@ var prefer_whole_object_assertion_default = createRule({
12561
12638
  const literal = literalText(expected, (node) => sourceCode.getText(node));
12562
12639
  return {
12563
12640
  statement,
12564
- receiver: actual.object,
12641
+ receiver,
12565
12642
  key,
12566
12643
  matcher,
12567
12644
  expectedText: literal ?? sourceCode.getText(expected),
@@ -12574,7 +12651,8 @@ var prefer_whole_object_assertion_default = createRule({
12574
12651
  );
12575
12652
  }
12576
12653
  function reportPropertyRun(run) {
12577
- const names = /* @__PURE__ */ new Set();
12654
+ const tree = /* @__PURE__ */ new Map();
12655
+ const paths = [];
12578
12656
  for (const assertion of run) {
12579
12657
  if (assertion.key.kind !== "property" || !assertion.expectedIsLiteral) {
12580
12658
  return;
@@ -12582,19 +12660,44 @@ var prefer_whole_object_assertion_default = createRule({
12582
12660
  if (!MERGEABLE_MATCHERS.has(assertion.matcher) && !SYNTHETIC_LITERAL_MATCHERS.has(assertion.matcher)) {
12583
12661
  return;
12584
12662
  }
12585
- if (names.has(assertion.key.name)) {
12586
- return;
12663
+ paths.push([...assertion.key.path]);
12664
+ }
12665
+ const commonPrefix = [];
12666
+ for (let index = 0; ; index += 1) {
12667
+ const candidate = paths[0]?.[index];
12668
+ if (candidate === void 0 || paths.some((path) => path[index] !== candidate || path.length === index + 1)) {
12669
+ break;
12670
+ }
12671
+ commonPrefix.push(candidate);
12672
+ }
12673
+ for (const [assertionIndex, assertion] of run.entries()) {
12674
+ if (assertion.key.kind !== "property") return;
12675
+ let branch = tree;
12676
+ const relativePath = paths[assertionIndex]?.slice(commonPrefix.length) ?? [];
12677
+ for (const [index, name] of relativePath.entries()) {
12678
+ const leaf = index === relativePath.length - 1;
12679
+ const existing = branch.get(name);
12680
+ if (leaf) {
12681
+ if (existing !== void 0) return;
12682
+ branch.set(name, assertion.expectedText);
12683
+ } else if (existing === void 0) {
12684
+ const nested = /* @__PURE__ */ new Map();
12685
+ branch.set(name, nested);
12686
+ branch = nested;
12687
+ } else if (existing instanceof Map) {
12688
+ branch = existing;
12689
+ } else {
12690
+ return;
12691
+ }
12587
12692
  }
12588
- names.add(assertion.key.name);
12589
12693
  }
12590
12694
  const first = run[0];
12591
12695
  if (first === void 0) {
12592
12696
  return;
12593
12697
  }
12594
- const receiverText = sourceCode.getText(first.receiver);
12595
- const properties = run.map(
12596
- (assertion) => assertion.key.kind === "property" ? `${assertion.key.name}: ${assertion.expectedText}` : ""
12597
- ).join(", ");
12698
+ const receiverText = `${sourceCode.getText(first.receiver)}${commonPrefix.map((name) => `.${name}`).join("")}`;
12699
+ const renderTree = (value) => [...value.entries()].map(([name, child]) => `${name}: ${child instanceof Map ? `{ ${renderTree(child)} }` : child}`).join(", ");
12700
+ const properties = renderTree(tree);
12598
12701
  context.report({
12599
12702
  node: first.statement,
12600
12703
  messageId: "combineAssertions",
@@ -15222,7 +15325,7 @@ var stepdown_default = createRule({
15222
15325
 
15223
15326
  // src/rules/source-coupled-test.ts
15224
15327
  var import_utils73 = require("@typescript-eslint/utils");
15225
- var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|py|[cm]?[jt]s)$/iu;
15328
+ var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc|py|[cm]?[jt]s)$/iu;
15226
15329
  var FS_MODULES = /* @__PURE__ */ new Set(["fs", "node:fs", "fs/promises", "node:fs/promises"]);
15227
15330
  var FS_READERS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
15228
15331
  var TEXT_TRANSFORMS = /* @__PURE__ */ new Set([
@@ -15417,6 +15520,13 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
15417
15520
  if (receiver.type !== import_utils73.AST_NODE_TYPES.Identifier || receiver.name !== "assert" || !ASSERT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
15418
15521
  return new Set(node.arguments.flatMap((argument) => argument.type === import_utils73.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
15419
15522
  };
15523
+ const rawRegexExtractionOrigins = (node) => {
15524
+ const callee = unwrap5(node.callee);
15525
+ if (callee.type !== import_utils73.AST_NODE_TYPES.MemberExpression || staticMemberName7(callee) !== "matchAll" || node.arguments.length !== 1) return /* @__PURE__ */ new Set();
15526
+ const argument = node.arguments[0];
15527
+ if (argument?.type !== import_utils73.AST_NODE_TYPES.Literal || !(argument.value instanceof RegExp)) return /* @__PURE__ */ new Set();
15528
+ return rawOrigins(callee.object);
15529
+ };
15420
15530
  const declare = (name2, state) => {
15421
15531
  const scope = currentScope();
15422
15532
  scope.declared.add(name2);
@@ -15496,7 +15606,10 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
15496
15606
  if (collection && left?.type === import_utils73.AST_NODE_TYPES.Identifier) declare(left.name, { path: true });
15497
15607
  },
15498
15608
  CallExpression(node) {
15499
- const origins = rawAssertionOrigins(node);
15609
+ const origins = /* @__PURE__ */ new Set([
15610
+ ...rawAssertionOrigins(node),
15611
+ ...rawRegexExtractionOrigins(node)
15612
+ ]);
15500
15613
  if (origins.size === 0 || [...origins].every((origin) => reportedOrigins.has(origin))) return;
15501
15614
  for (const origin of origins) reportedOrigins.add(origin);
15502
15615
  context.report({ node, messageId: "rawSourceOracle" });
@@ -15844,7 +15957,7 @@ var rules = {
15844
15957
  };
15845
15958
  var meta = {
15846
15959
  name: "@sarj/eslint-plugin",
15847
- version: "15.8.2"
15960
+ version: "15.9.0"
15848
15961
  };
15849
15962
  var applicationOnlyRules = [
15850
15963
  "no-restricted-library-load",
package/dist/index.d.cts CHANGED
@@ -435,7 +435,7 @@ type FlatPreset = {
435
435
  declare const plugin: {
436
436
  readonly meta: {
437
437
  readonly name: "@sarj/eslint-plugin";
438
- readonly version: "15.8.2";
438
+ readonly version: "15.9.0";
439
439
  };
440
440
  readonly rules: {
441
441
  readonly "iac-source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
package/dist/index.d.ts CHANGED
@@ -435,7 +435,7 @@ type FlatPreset = {
435
435
  declare const plugin: {
436
436
  readonly meta: {
437
437
  readonly name: "@sarj/eslint-plugin";
438
- readonly version: "15.8.2";
438
+ readonly version: "15.9.0";
439
439
  };
440
440
  readonly rules: {
441
441
  readonly "iac-source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
package/dist/index.js CHANGED
@@ -4679,11 +4679,11 @@ var no_offset_pagination_default = createRule({
4679
4679
  // src/rules/no-positional-tuple-return.ts
4680
4680
  import { AST_NODE_TYPES as AST_NODE_TYPES18 } from "@typescript-eslint/utils";
4681
4681
  var noPositionalTupleReturnDocumentation = {
4682
- summary: "Disallow returning a multi-field tuple from an exported function; return a named object so call sites cannot mismatch slots.",
4683
- rationale: "Public tuple fields are identified only by position, so reordering can preserve types while changing meaning.",
4682
+ summary: "Disallow returning a multi-field tuple from a named function; return a named object so call sites cannot mismatch slots.",
4683
+ rationale: "Tuple fields are identified only by position, so reordering can preserve types while changing meaning.",
4684
4684
  remediation: "Return an object whose property names describe each value.",
4685
4685
  category: "maintainability",
4686
- limitations: ["Only declared multi-field tuple returns on public TypeScript surfaces are inspected."],
4686
+ limitations: ["Declared or syntax-proven multi-field tuple returns on named functions and public type surfaces are inspected; anonymous inline callbacks and syntax-proven TanStack Query key factories are excluded."],
4687
4687
  examples: [
4688
4688
  { id: "named-object-return", title: "Return named fields", outcome: "no-match", files: [{ path: "src/download.ts", source: "export function download(): { body: string; status: number } { return impl(); }" }], focusPath: "src/download.ts", expectedCount: 0, public: true },
4689
4689
  { id: "tuple-return", title: "Do not expose positional fields", outcome: "match", files: [{ path: "src/download.ts", source: "export function download(): [string, number] { return impl(); }" }], focusPath: "src/download.ts", expectedCount: 1, public: true }
@@ -4721,6 +4721,16 @@ function tupleReturnType(node, aliases, resolving = /* @__PURE__ */ new Set()) {
4721
4721
  }
4722
4722
  return null;
4723
4723
  }
4724
+ function tupleExpression(node, aliases) {
4725
+ if (node.type !== AST_NODE_TYPES18.TSAsExpression && node.type !== AST_NODE_TYPES18.TSSatisfiesExpression) {
4726
+ return null;
4727
+ }
4728
+ if (node.expression.type !== AST_NODE_TYPES18.ArrayExpression || node.expression.elements.length < MIN_ELEMENTS) {
4729
+ return null;
4730
+ }
4731
+ if (node.type === AST_NODE_TYPES18.TSAsExpression && node.typeAnnotation.type === AST_NODE_TYPES18.TSTypeReference && node.typeAnnotation.typeName.type === AST_NODE_TYPES18.Identifier && node.typeAnnotation.typeName.name === "const") return node.expression;
4732
+ return tupleReturnType(node.typeAnnotation, aliases) === null ? null : node.expression;
4733
+ }
4724
4734
  function functionName(node) {
4725
4735
  if (node.type === AST_NODE_TYPES18.FunctionDeclaration) {
4726
4736
  if (node.id !== null) return node.id.name;
@@ -4736,60 +4746,25 @@ function functionName(node) {
4736
4746
  return parent.id.name;
4737
4747
  }
4738
4748
  if ((parent?.type === AST_NODE_TYPES18.MethodDefinition || parent?.type === AST_NODE_TYPES18.TSAbstractMethodDefinition || parent?.type === AST_NODE_TYPES18.PropertyDefinition || parent?.type === AST_NODE_TYPES18.Property) && parent.key.type === AST_NODE_TYPES18.Identifier) {
4739
- if ((parent.type === AST_NODE_TYPES18.MethodDefinition || parent.type === AST_NODE_TYPES18.TSAbstractMethodDefinition || parent.type === AST_NODE_TYPES18.PropertyDefinition) && (parent.accessibility === "private" || parent.accessibility === "protected")) return null;
4740
4749
  return parent.key.name;
4741
4750
  }
4742
4751
  return null;
4743
4752
  }
4744
- function isInlineExported(node) {
4745
- if (moduleScopeBindingName(node) === null) return false;
4746
- for (let current = node; current != null; current = current.parent) {
4747
- const parent = current.parent;
4748
- if (parent?.type === AST_NODE_TYPES18.ExportNamedDeclaration || parent?.type === AST_NODE_TYPES18.ExportDefaultDeclaration) {
4749
- return true;
4750
- }
4751
- }
4752
- return false;
4753
- }
4754
- function moduleScopeBindingName(node) {
4755
- let current = node;
4756
- while (current.parent != null && current.parent.type !== AST_NODE_TYPES18.Program) {
4757
- current = current.parent;
4758
- }
4759
- if (current.parent?.type !== AST_NODE_TYPES18.Program) {
4760
- return null;
4761
- }
4762
- let topLevel = current;
4763
- if (current.type === AST_NODE_TYPES18.ExportNamedDeclaration || current.type === AST_NODE_TYPES18.ExportDefaultDeclaration) {
4764
- topLevel = current.declaration;
4765
- }
4766
- if (topLevel === null) return null;
4767
- if (topLevel.type === AST_NODE_TYPES18.FunctionDeclaration) {
4768
- if (topLevel !== node) return null;
4769
- return topLevel.id?.name ?? (current.type === AST_NODE_TYPES18.ExportDefaultDeclaration ? "default" : null);
4770
- }
4771
- if ((topLevel.type === AST_NODE_TYPES18.ArrowFunctionExpression || topLevel.type === AST_NODE_TYPES18.FunctionExpression) && topLevel === node && current.type === AST_NODE_TYPES18.ExportDefaultDeclaration) {
4772
- return "default";
4773
- }
4774
- if (topLevel.type === AST_NODE_TYPES18.ClassDeclaration) {
4775
- let owner = node.parent;
4776
- while (owner != null && owner.parent !== topLevel.body) owner = owner.parent;
4777
- return (owner?.type === AST_NODE_TYPES18.MethodDefinition || owner?.type === AST_NODE_TYPES18.TSAbstractMethodDefinition || owner?.type === AST_NODE_TYPES18.PropertyDefinition) && owner.value === node ? topLevel.id?.name ?? (current.type === AST_NODE_TYPES18.ExportDefaultDeclaration ? "default" : null) : null;
4778
- }
4779
- if (topLevel.type === AST_NODE_TYPES18.VariableDeclaration) {
4780
- for (const declarator of topLevel.declarations) {
4781
- let initializer = declarator.init;
4782
- while (initializer?.type === AST_NODE_TYPES18.TSAsExpression || initializer?.type === AST_NODE_TYPES18.TSSatisfiesExpression || initializer?.type === AST_NODE_TYPES18.TSNonNullExpression) initializer = initializer.expression;
4783
- if (declarator.id.type === AST_NODE_TYPES18.Identifier && initializer === node) return declarator.id.name;
4784
- if (declarator.id.type === AST_NODE_TYPES18.Identifier && (initializer?.type === AST_NODE_TYPES18.ClassExpression || initializer?.type === AST_NODE_TYPES18.ObjectExpression)) {
4785
- let owner = node.parent;
4786
- const container = initializer.type === AST_NODE_TYPES18.ClassExpression ? initializer.body : initializer;
4787
- while (owner != null && owner.parent !== container) owner = owner.parent;
4788
- if ((owner?.type === AST_NODE_TYPES18.MethodDefinition || owner?.type === AST_NODE_TYPES18.PropertyDefinition || owner?.type === AST_NODE_TYPES18.Property) && owner.value === node) return declarator.id.name;
4789
- }
4790
- }
4791
- }
4792
- return null;
4753
+ function isQueryKeyFactory(node) {
4754
+ let wrapped = node;
4755
+ while ((wrapped.parent?.type === AST_NODE_TYPES18.TSAsExpression || wrapped.parent?.type === AST_NODE_TYPES18.TSSatisfiesExpression || wrapped.parent?.type === AST_NODE_TYPES18.TSNonNullExpression) && wrapped.parent.expression === wrapped) wrapped = wrapped.parent;
4756
+ const property = wrapped.parent;
4757
+ if (property?.type !== AST_NODE_TYPES18.Property || property.value !== wrapped) return false;
4758
+ const object = property.parent;
4759
+ if (object.type !== AST_NODE_TYPES18.ObjectExpression) return false;
4760
+ const assertion = object.parent;
4761
+ if (assertion.type !== AST_NODE_TYPES18.TSAsExpression || assertion.expression !== object || assertion.typeAnnotation.type !== AST_NODE_TYPES18.TSTypeReference || assertion.typeAnnotation.typeName.type !== AST_NODE_TYPES18.Identifier || assertion.typeAnnotation.typeName.name !== "const") return false;
4762
+ const declarator = assertion.parent;
4763
+ if (declarator.type !== AST_NODE_TYPES18.VariableDeclarator || declarator.id.type !== AST_NODE_TYPES18.Identifier || !/Keys$/i.test(declarator.id.name) || declarator.parent.type !== AST_NODE_TYPES18.VariableDeclaration || declarator.parent.kind !== "const") return false;
4764
+ return object.properties.some((candidate) => {
4765
+ if (candidate.type !== AST_NODE_TYPES18.Property || staticMemberName3(candidate.key) !== "all") return false;
4766
+ return candidate.value.type === AST_NODE_TYPES18.TSAsExpression && candidate.value.expression.type === AST_NODE_TYPES18.ArrayExpression && candidate.value.typeAnnotation.type === AST_NODE_TYPES18.TSTypeReference && candidate.value.typeAnnotation.typeName.type === AST_NODE_TYPES18.Identifier && candidate.value.typeAnnotation.typeName.name === "const";
4767
+ });
4793
4768
  }
4794
4769
  function specifierExportedNames(program) {
4795
4770
  const names = /* @__PURE__ */ new Set();
@@ -4898,18 +4873,52 @@ function isExportedClass(node, specifierExports) {
4898
4873
  if (node.parent.type === AST_NODE_TYPES18.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES18.Identifier) return specifierExports.has(node.parent.id.name) || isInlineExported(node);
4899
4874
  return false;
4900
4875
  }
4901
- function isExportedInterface(node, exports) {
4902
- return node.parent.type === AST_NODE_TYPES18.ExportNamedDeclaration || node.parent.type === AST_NODE_TYPES18.ExportDefaultDeclaration || node.parent.type === AST_NODE_TYPES18.Program && exports.has(node.id.name);
4876
+ function isInlineExported(node) {
4877
+ if (moduleScopeBindingName(node) === null) return false;
4878
+ for (let current = node; current != null; current = current.parent) {
4879
+ const parent = current.parent;
4880
+ if (parent?.type === AST_NODE_TYPES18.ExportNamedDeclaration || parent?.type === AST_NODE_TYPES18.ExportDefaultDeclaration) {
4881
+ return true;
4882
+ }
4883
+ }
4884
+ return false;
4903
4885
  }
4904
- function isExported(node, specifierExports) {
4905
- if (isInlineExported(node)) {
4906
- return true;
4886
+ function moduleScopeBindingName(node) {
4887
+ let current = node;
4888
+ while (current.parent != null && current.parent.type !== AST_NODE_TYPES18.Program) {
4889
+ current = current.parent;
4907
4890
  }
4908
- if (specifierExports.size === 0) {
4909
- return false;
4891
+ if (current.parent?.type !== AST_NODE_TYPES18.Program) return null;
4892
+ let topLevel = current;
4893
+ if (current.type === AST_NODE_TYPES18.ExportNamedDeclaration || current.type === AST_NODE_TYPES18.ExportDefaultDeclaration) topLevel = current.declaration;
4894
+ if (topLevel === null) return null;
4895
+ if (topLevel.type === AST_NODE_TYPES18.FunctionDeclaration) {
4896
+ if (topLevel !== node) return null;
4897
+ return topLevel.id?.name ?? (current.type === AST_NODE_TYPES18.ExportDefaultDeclaration ? "default" : null);
4898
+ }
4899
+ if ((topLevel.type === AST_NODE_TYPES18.ArrowFunctionExpression || topLevel.type === AST_NODE_TYPES18.FunctionExpression) && topLevel === node && current.type === AST_NODE_TYPES18.ExportDefaultDeclaration) return "default";
4900
+ if (topLevel.type === AST_NODE_TYPES18.ClassDeclaration) {
4901
+ let owner = node.parent;
4902
+ while (owner != null && owner.parent !== topLevel.body) owner = owner.parent;
4903
+ return (owner?.type === AST_NODE_TYPES18.MethodDefinition || owner?.type === AST_NODE_TYPES18.TSAbstractMethodDefinition || owner?.type === AST_NODE_TYPES18.PropertyDefinition) && owner.value === node ? topLevel.id?.name ?? (current.type === AST_NODE_TYPES18.ExportDefaultDeclaration ? "default" : null) : null;
4904
+ }
4905
+ if (topLevel.type === AST_NODE_TYPES18.VariableDeclaration) {
4906
+ for (const declarator of topLevel.declarations) {
4907
+ let initializer = declarator.init;
4908
+ while (initializer?.type === AST_NODE_TYPES18.TSAsExpression || initializer?.type === AST_NODE_TYPES18.TSSatisfiesExpression || initializer?.type === AST_NODE_TYPES18.TSNonNullExpression) initializer = initializer.expression;
4909
+ if (declarator.id.type === AST_NODE_TYPES18.Identifier && initializer === node) return declarator.id.name;
4910
+ if (declarator.id.type === AST_NODE_TYPES18.Identifier && (initializer?.type === AST_NODE_TYPES18.ClassExpression || initializer?.type === AST_NODE_TYPES18.ObjectExpression)) {
4911
+ let owner = node.parent;
4912
+ const container = initializer.type === AST_NODE_TYPES18.ClassExpression ? initializer.body : initializer;
4913
+ while (owner != null && owner.parent !== container) owner = owner.parent;
4914
+ if ((owner?.type === AST_NODE_TYPES18.MethodDefinition || owner?.type === AST_NODE_TYPES18.PropertyDefinition || owner?.type === AST_NODE_TYPES18.Property) && owner.value === node) return declarator.id.name;
4915
+ }
4916
+ }
4910
4917
  }
4911
- const binding = moduleScopeBindingName(node);
4912
- return binding !== null && specifierExports.has(binding);
4918
+ return null;
4919
+ }
4920
+ function isExportedInterface(node, exports) {
4921
+ return node.parent.type === AST_NODE_TYPES18.ExportNamedDeclaration || node.parent.type === AST_NODE_TYPES18.ExportDefaultDeclaration || node.parent.type === AST_NODE_TYPES18.Program && exports.has(node.id.name);
4913
4922
  }
4914
4923
  var no_positional_tuple_return_default = createRule({
4915
4924
  name: "no-positional-tuple-return",
@@ -4917,11 +4926,11 @@ var no_positional_tuple_return_default = createRule({
4917
4926
  meta: {
4918
4927
  type: "suggestion",
4919
4928
  docs: {
4920
- description: "Disallow returning a multi-field tuple from an exported function; return a named object so call sites cannot mismatch slots."
4929
+ description: "Disallow returning a multi-field tuple from a named function; return a named object so call sites cannot mismatch slots."
4921
4930
  },
4922
4931
  schema: [],
4923
4932
  messages: {
4924
- noPositionalTupleReturn: "Exported `{{name}}` returns a {{count}}-field tuple, so consumers depend on positional slots that can be reordered silently. Return a named object instead."
4933
+ noPositionalTupleReturn: "`{{name}}` returns a {{count}}-field tuple, so callers depend on positional slots that can be reordered silently. Return a named object instead."
4925
4934
  }
4926
4935
  },
4927
4936
  defaultOptions: [],
@@ -4933,6 +4942,8 @@ var no_positional_tuple_return_default = createRule({
4933
4942
  exportedTypeNames(context.sourceCode.ast)
4934
4943
  );
4935
4944
  const aliases = typeAliases(context.sourceCode.ast);
4945
+ const reportedFunctions = /* @__PURE__ */ new WeakSet();
4946
+ const functionStack = [];
4936
4947
  const report = (annotation, name) => {
4937
4948
  const tuple = tupleReturnType(annotation, aliases);
4938
4949
  if (tuple === null || tuple.elementTypes.length < MIN_ELEMENTS) return;
@@ -4942,25 +4953,54 @@ var no_positional_tuple_return_default = createRule({
4942
4953
  data: { name, count: String(tuple.elementTypes.length) }
4943
4954
  });
4944
4955
  };
4956
+ const reportExpression = (node, expression) => {
4957
+ if (reportedFunctions.has(node)) return;
4958
+ const tuple = tupleExpression(expression, aliases);
4959
+ const name = functionName(node);
4960
+ if (tuple === null || name === null) return;
4961
+ reportedFunctions.add(node);
4962
+ context.report({
4963
+ node: tuple,
4964
+ messageId: "noPositionalTupleReturn",
4965
+ data: { name, count: String(tuple.elements.length) }
4966
+ });
4967
+ };
4945
4968
  const check = (node) => {
4969
+ if (isQueryKeyFactory(node)) return;
4946
4970
  const annotation = node.returnType?.typeAnnotation;
4947
4971
  if (annotation === void 0) {
4972
+ if (node.type === AST_NODE_TYPES18.ArrowFunctionExpression && node.expression) {
4973
+ reportExpression(node, node.body);
4974
+ }
4948
4975
  return;
4949
4976
  }
4950
4977
  const name = functionName(node);
4951
4978
  if (name === null) {
4952
4979
  return;
4953
4980
  }
4954
- if (!isExported(node, specifierExports)) {
4955
- return;
4956
- }
4957
4981
  report(annotation, name);
4958
4982
  };
4983
+ const enterFunction = (node) => {
4984
+ functionStack.push(node);
4985
+ check(node);
4986
+ };
4987
+ const exitFunction = () => {
4988
+ functionStack.pop();
4989
+ };
4959
4990
  return {
4960
- FunctionDeclaration: check,
4961
- FunctionExpression: check,
4962
- ArrowFunctionExpression: check,
4963
- TSEmptyBodyFunctionExpression: check,
4991
+ FunctionDeclaration: enterFunction,
4992
+ "FunctionDeclaration:exit": exitFunction,
4993
+ FunctionExpression: enterFunction,
4994
+ "FunctionExpression:exit": exitFunction,
4995
+ ArrowFunctionExpression: enterFunction,
4996
+ "ArrowFunctionExpression:exit": exitFunction,
4997
+ TSEmptyBodyFunctionExpression: enterFunction,
4998
+ "TSEmptyBodyFunctionExpression:exit": exitFunction,
4999
+ ReturnStatement(node) {
5000
+ const owner = functionStack.at(-1);
5001
+ if (owner === void 0 || owner.returnType !== void 0 || node.argument === null) return;
5002
+ reportExpression(owner, node.argument);
5003
+ },
4964
5004
  TSDeclareFunction(node) {
4965
5005
  if (node.id === null || node.returnType === void 0 || node.parent.type !== AST_NODE_TYPES18.ExportNamedDeclaration && node.parent.type !== AST_NODE_TYPES18.ExportDefaultDeclaration && !specifierExports.has(node.id.name)) return;
4966
5006
  report(node.returnType.typeAnnotation, node.id.name);
@@ -11139,6 +11179,7 @@ var prefer_non_nullable_collection_default = createRule({
11139
11179
 
11140
11180
  // src/rules/prefer-await-in-async-return.ts
11141
11181
  import {
11182
+ ASTUtils as ASTUtils14,
11142
11183
  ESLintUtils as ESLintUtils3,
11143
11184
  AST_NODE_TYPES as AST_NODE_TYPES46
11144
11185
  } from "@typescript-eslint/utils";
@@ -11151,7 +11192,8 @@ var preferAwaitInAsyncReturnDocumentation = {
11151
11192
  since: "15.6.3",
11152
11193
  limitations: [
11153
11194
  "Only a single directly returned `.then` call with an inline callback is checked.",
11154
- "The receiver must be proven Promise-like by TypeScript; untyped files and larger chains are intentionally ignored."
11195
+ "The receiver must be proven Promise-like by TypeScript; untyped files and larger chains are intentionally ignored.",
11196
+ "Direct loader callbacks passed to resolved `React.lazy` and `next/dynamic` imports are excluded because returning the module Promise is their framework contract."
11155
11197
  ],
11156
11198
  examples: [
11157
11199
  {
@@ -11180,19 +11222,19 @@ var preferAwaitInAsyncReturnDocumentation = {
11180
11222
  }
11181
11223
  ]
11182
11224
  };
11183
- function isDirectAsyncReturn(node) {
11225
+ function directAsyncReturnOwner(node) {
11184
11226
  const parent = node.parent;
11185
11227
  if (parent.type === AST_NODE_TYPES46.ArrowFunctionExpression && parent.body === node) {
11186
- return parent.async && !parent.generator;
11228
+ return parent.async && !parent.generator ? parent : null;
11187
11229
  }
11188
11230
  if (parent.type !== AST_NODE_TYPES46.ReturnStatement || parent.argument !== node) {
11189
- return false;
11231
+ return null;
11190
11232
  }
11191
11233
  let owner = parent.parent;
11192
11234
  while (owner !== void 0 && !isRuntimeFunction(owner)) {
11193
11235
  owner = owner.parent;
11194
11236
  }
11195
- return owner !== void 0 && owner.async && !owner.generator;
11237
+ return owner !== void 0 && owner.async && !owner.generator ? owner : null;
11196
11238
  }
11197
11239
  function isRuntimeFunction(node) {
11198
11240
  return node.type === AST_NODE_TYPES46.ArrowFunctionExpression || node.type === AST_NODE_TYPES46.FunctionDeclaration || node.type === AST_NODE_TYPES46.FunctionExpression;
@@ -11249,9 +11291,33 @@ var prefer_await_in_async_return_default = createRule({
11249
11291
  services = null;
11250
11292
  }
11251
11293
  if (services === null) return {};
11294
+ const frameworkLoaders = /* @__PURE__ */ new Set();
11295
+ const rememberFrameworkLoader = (identifier) => {
11296
+ const variable = ASTUtils14.findVariable(context.sourceCode.getScope(identifier), identifier.name);
11297
+ if (variable !== null) frameworkLoaders.add(variable);
11298
+ };
11299
+ const isFrameworkLoaderCallback = (owner) => {
11300
+ const parent = owner.parent;
11301
+ if (parent.type !== AST_NODE_TYPES46.CallExpression || parent.arguments[0] !== owner || parent.callee.type !== AST_NODE_TYPES46.Identifier) return false;
11302
+ const variable = ASTUtils14.findVariable(context.sourceCode.getScope(parent.callee), parent.callee.name);
11303
+ return variable !== null && frameworkLoaders.has(variable);
11304
+ };
11252
11305
  return {
11306
+ ImportDeclaration(node) {
11307
+ if (node.source.value === "react") {
11308
+ for (const specifier of node.specifiers) {
11309
+ if (specifier.type === AST_NODE_TYPES46.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES46.Identifier ? specifier.imported.name : specifier.imported.value) === "lazy") rememberFrameworkLoader(specifier.local);
11310
+ }
11311
+ }
11312
+ if (node.source.value === "next/dynamic") {
11313
+ for (const specifier of node.specifiers) {
11314
+ if (specifier.type === AST_NODE_TYPES46.ImportDefaultSpecifier) rememberFrameworkLoader(specifier.local);
11315
+ }
11316
+ }
11317
+ },
11253
11318
  CallExpression(node) {
11254
- if (!isDirectAsyncReturn(node)) return;
11319
+ const owner = directAsyncReturnOwner(node);
11320
+ if (owner === null || isFrameworkLoaderCallback(owner)) return;
11255
11321
  const receiver = promiseThenReceiver(node);
11256
11322
  if (receiver === null || !isProvenPromiseLike(receiver, services)) {
11257
11323
  return;
@@ -12460,6 +12526,16 @@ function literalIndex(node) {
12460
12526
  }
12461
12527
  return Number.isInteger(node.value) && node.value >= 0 ? node.value : null;
12462
12528
  }
12529
+ function propertyAccess(node) {
12530
+ const path = [];
12531
+ let current = node;
12532
+ while (current.type === AST_NODE_TYPES49.MemberExpression && !current.computed && !current.optional) {
12533
+ if (current.property.type !== AST_NODE_TYPES49.Identifier || COLLECTION_PROPERTIES.has(current.property.name) || LITERAL_KEY_HAZARDS.has(current.property.name)) return null;
12534
+ path.unshift(current.property.name);
12535
+ current = current.object;
12536
+ }
12537
+ return path.length > 0 && isPureReceiver(current) ? { receiver: current, path } : null;
12538
+ }
12463
12539
  var prefer_whole_object_assertion_default = createRule({
12464
12540
  name: "prefer-whole-object-assertion",
12465
12541
  documentation: preferWholeObjectAssertionDocumentation,
@@ -12506,21 +12582,23 @@ var prefer_whole_object_assertion_default = createRule({
12506
12582
  return null;
12507
12583
  }
12508
12584
  let key;
12585
+ let receiver;
12509
12586
  if (actual.computed) {
12510
12587
  const index = literalIndex(actual.property);
12511
12588
  if (index === null) {
12512
12589
  return null;
12513
12590
  }
12514
12591
  key = { kind: "index", index };
12592
+ receiver = actual.object;
12515
12593
  } else {
12516
- if (actual.property.type !== AST_NODE_TYPES49.Identifier || COLLECTION_PROPERTIES.has(actual.property.name) || LITERAL_KEY_HAZARDS.has(actual.property.name)) {
12517
- return null;
12518
- }
12519
- key = { kind: "property", name: actual.property.name };
12594
+ const access = propertyAccess(actual);
12595
+ if (access === null) return null;
12596
+ key = { kind: "property", path: access.path };
12597
+ receiver = access.receiver;
12520
12598
  }
12521
12599
  const synthetic = SYNTHETIC_LITERAL_MATCHERS.get(matcher);
12522
12600
  if (synthetic !== void 0 && call.arguments.length === 0) {
12523
- return { statement, receiver: actual.object, key, matcher, expectedText: synthetic, expectedIsLiteral: true };
12601
+ return { statement, receiver, key, matcher, expectedText: synthetic, expectedIsLiteral: true };
12524
12602
  }
12525
12603
  if (!MERGEABLE_MATCHERS.has(matcher)) {
12526
12604
  return null;
@@ -12532,7 +12610,7 @@ var prefer_whole_object_assertion_default = createRule({
12532
12610
  const literal = literalText(expected, (node) => sourceCode.getText(node));
12533
12611
  return {
12534
12612
  statement,
12535
- receiver: actual.object,
12613
+ receiver,
12536
12614
  key,
12537
12615
  matcher,
12538
12616
  expectedText: literal ?? sourceCode.getText(expected),
@@ -12545,7 +12623,8 @@ var prefer_whole_object_assertion_default = createRule({
12545
12623
  );
12546
12624
  }
12547
12625
  function reportPropertyRun(run) {
12548
- const names = /* @__PURE__ */ new Set();
12626
+ const tree = /* @__PURE__ */ new Map();
12627
+ const paths = [];
12549
12628
  for (const assertion of run) {
12550
12629
  if (assertion.key.kind !== "property" || !assertion.expectedIsLiteral) {
12551
12630
  return;
@@ -12553,19 +12632,44 @@ var prefer_whole_object_assertion_default = createRule({
12553
12632
  if (!MERGEABLE_MATCHERS.has(assertion.matcher) && !SYNTHETIC_LITERAL_MATCHERS.has(assertion.matcher)) {
12554
12633
  return;
12555
12634
  }
12556
- if (names.has(assertion.key.name)) {
12557
- return;
12635
+ paths.push([...assertion.key.path]);
12636
+ }
12637
+ const commonPrefix = [];
12638
+ for (let index = 0; ; index += 1) {
12639
+ const candidate = paths[0]?.[index];
12640
+ if (candidate === void 0 || paths.some((path) => path[index] !== candidate || path.length === index + 1)) {
12641
+ break;
12642
+ }
12643
+ commonPrefix.push(candidate);
12644
+ }
12645
+ for (const [assertionIndex, assertion] of run.entries()) {
12646
+ if (assertion.key.kind !== "property") return;
12647
+ let branch = tree;
12648
+ const relativePath = paths[assertionIndex]?.slice(commonPrefix.length) ?? [];
12649
+ for (const [index, name] of relativePath.entries()) {
12650
+ const leaf = index === relativePath.length - 1;
12651
+ const existing = branch.get(name);
12652
+ if (leaf) {
12653
+ if (existing !== void 0) return;
12654
+ branch.set(name, assertion.expectedText);
12655
+ } else if (existing === void 0) {
12656
+ const nested = /* @__PURE__ */ new Map();
12657
+ branch.set(name, nested);
12658
+ branch = nested;
12659
+ } else if (existing instanceof Map) {
12660
+ branch = existing;
12661
+ } else {
12662
+ return;
12663
+ }
12558
12664
  }
12559
- names.add(assertion.key.name);
12560
12665
  }
12561
12666
  const first = run[0];
12562
12667
  if (first === void 0) {
12563
12668
  return;
12564
12669
  }
12565
- const receiverText = sourceCode.getText(first.receiver);
12566
- const properties = run.map(
12567
- (assertion) => assertion.key.kind === "property" ? `${assertion.key.name}: ${assertion.expectedText}` : ""
12568
- ).join(", ");
12670
+ const receiverText = `${sourceCode.getText(first.receiver)}${commonPrefix.map((name) => `.${name}`).join("")}`;
12671
+ const renderTree = (value) => [...value.entries()].map(([name, child]) => `${name}: ${child instanceof Map ? `{ ${renderTree(child)} }` : child}`).join(", ");
12672
+ const properties = renderTree(tree);
12569
12673
  context.report({
12570
12674
  node: first.statement,
12571
12675
  messageId: "combineAssertions",
@@ -12641,7 +12745,7 @@ var prefer_whole_object_assertion_default = createRule({
12641
12745
  });
12642
12746
 
12643
12747
  // src/rules/repeated-static-call-cases.ts
12644
- import { AST_NODE_TYPES as AST_NODE_TYPES50, ASTUtils as ASTUtils14 } from "@typescript-eslint/utils";
12748
+ import { AST_NODE_TYPES as AST_NODE_TYPES50, ASTUtils as ASTUtils15 } from "@typescript-eslint/utils";
12645
12749
  var repeatedStaticCallCasesDocumentation = {
12646
12750
  summary: "Report three or more consecutive literal call assertions that should be independently named test cases.",
12647
12751
  rationale: "Copy-pasted cases obscure the input table and stop later cases from being reported after the first failure.",
@@ -12667,7 +12771,7 @@ function staticMemberName5(node) {
12667
12771
  return null;
12668
12772
  }
12669
12773
  function importedName3(identifier, context, modules) {
12670
- const variable = ASTUtils14.findVariable(context.sourceCode.getScope(identifier), identifier.name);
12774
+ const variable = ASTUtils15.findVariable(context.sourceCode.getScope(identifier), identifier.name);
12671
12775
  if (variable === null || variable.defs.length === 0) return identifier.name;
12672
12776
  for (const definition of variable.defs) {
12673
12777
  if (definition.node.type !== AST_NODE_TYPES50.ImportSpecifier) continue;
@@ -13668,7 +13772,7 @@ var require_assert_never_default = createRule({
13668
13772
  });
13669
13773
 
13670
13774
  // src/rules/require-fetch-timeout.ts
13671
- import { AST_NODE_TYPES as AST_NODE_TYPES53, ASTUtils as ASTUtils15 } from "@typescript-eslint/utils";
13775
+ import { AST_NODE_TYPES as AST_NODE_TYPES53, ASTUtils as ASTUtils16 } from "@typescript-eslint/utils";
13672
13776
  var requireFetchTimeoutDocumentation = {
13673
13777
  summary: "Require an abort `signal` (e.g. `AbortSignal.timeout(ms)`) on global `fetch()` calls so stalled upstreams cannot hang the caller forever.",
13674
13778
  rationale: "An unbounded request can occupy work indefinitely when an upstream stalls.",
@@ -13749,7 +13853,7 @@ var require_fetch_timeout_default = createRule({
13749
13853
  }
13750
13854
  function resolvesToGlobal(identifier) {
13751
13855
  const scope = context.sourceCode.getScope(identifier);
13752
- const variable = ASTUtils15.findVariable(scope, identifier.name);
13856
+ const variable = ASTUtils16.findVariable(scope, identifier.name);
13753
13857
  return variable === null || variable.defs.length === 0;
13754
13858
  }
13755
13859
  function isGlobalFetchCall2(callee) {
@@ -13759,7 +13863,7 @@ var require_fetch_timeout_default = createRule({
13759
13863
  return callee.type === AST_NODE_TYPES53.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES53.Identifier && callee.property.name === "fetch" && callee.object.type === AST_NODE_TYPES53.Identifier && GLOBAL_OBJECTS2.has(callee.object.name) && resolvesToGlobal(callee.object);
13760
13864
  }
13761
13865
  function localConstInitProvablyLacksSignal(identifier) {
13762
- const variable = ASTUtils15.findVariable(
13866
+ const variable = ASTUtils16.findVariable(
13763
13867
  context.sourceCode.getScope(identifier),
13764
13868
  identifier.name
13765
13869
  );
@@ -14403,7 +14507,7 @@ var require_static_next_matcher_default = createRule({
14403
14507
  // src/rules/require-zod-form-validation.ts
14404
14508
  import {
14405
14509
  AST_NODE_TYPES as AST_NODE_TYPES56,
14406
- ASTUtils as ASTUtils16
14510
+ ASTUtils as ASTUtils17
14407
14511
  } from "@typescript-eslint/utils";
14408
14512
  var requireZodFormValidationDocumentation = {
14409
14513
  summary: "Require Zod validation (`Schema.parse(...)` / `Schema.safeParse(...)`) when reading values out of a `FormData` object.",
@@ -14471,7 +14575,7 @@ var require_zod_form_validation_default = createRule({
14471
14575
  return {};
14472
14576
  }
14473
14577
  const zodBindings = /* @__PURE__ */ new Set();
14474
- const resolvedBinding = (identifier) => ASTUtils16.findVariable(
14578
+ const resolvedBinding = (identifier) => ASTUtils17.findVariable(
14475
14579
  context.sourceCode.getScope(identifier),
14476
14580
  identifier.name
14477
14581
  );
@@ -14768,7 +14872,7 @@ var store_insert_requires_on_conflict_default = createRule({
14768
14872
  });
14769
14873
 
14770
14874
  // src/rules/stepdown.ts
14771
- import { AST_NODE_TYPES as AST_NODE_TYPES57, ASTUtils as ASTUtils17 } from "@typescript-eslint/utils";
14875
+ import { AST_NODE_TYPES as AST_NODE_TYPES57, ASTUtils as ASTUtils18 } from "@typescript-eslint/utils";
14772
14876
  var stepdownDocumentation = {
14773
14877
  summary: "Place a private helper below its sole direct same-scope caller.",
14774
14878
  rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
@@ -14968,7 +15072,7 @@ function methodName(node) {
14968
15072
  return !node.computed && node.key.type === AST_NODE_TYPES57.Identifier ? node.key.name : null;
14969
15073
  }
14970
15074
  function referencedMethod(context, node, classVariables) {
14971
- const objectVariable = node.object.type === AST_NODE_TYPES57.Identifier ? ASTUtils17.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
15075
+ const objectVariable = node.object.type === AST_NODE_TYPES57.Identifier ? ASTUtils18.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
14972
15076
  const isClassReference = objectVariable !== null && classVariables.has(objectVariable);
14973
15077
  if (node.object.type !== AST_NODE_TYPES57.ThisExpression && !isClassReference) return null;
14974
15078
  if (node.property.type === AST_NODE_TYPES57.PrivateIdentifier) return `#${node.property.name}`;
@@ -15021,11 +15125,11 @@ function classScope(context, node, computedReferenceNames) {
15021
15125
  const pinned = /* @__PURE__ */ new Set();
15022
15126
  const classVariables = /* @__PURE__ */ new Set();
15023
15127
  if (node.id !== null) {
15024
- const internal = ASTUtils17.findVariable(context.sourceCode.getScope(node), node.id.name);
15128
+ const internal = ASTUtils18.findVariable(context.sourceCode.getScope(node), node.id.name);
15025
15129
  if (internal !== null) classVariables.add(internal);
15026
15130
  }
15027
15131
  if (node.type === AST_NODE_TYPES57.ClassExpression && node.parent.type === AST_NODE_TYPES57.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES57.Identifier) {
15028
- const outer = ASTUtils17.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
15132
+ const outer = ASTUtils18.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
15029
15133
  if (outer !== null) classVariables.add(outer);
15030
15134
  }
15031
15135
  for (const method of methods) {
@@ -15061,7 +15165,7 @@ function classScope(context, node, computedReferenceNames) {
15061
15165
  return;
15062
15166
  }
15063
15167
  if (binding.type !== AST_NODE_TYPES57.Identifier) return;
15064
- const variable = ASTUtils17.findVariable(context.sourceCode.getScope(binding), binding.name);
15168
+ const variable = ASTUtils18.findVariable(context.sourceCode.getScope(binding), binding.name);
15065
15169
  if (variable !== null) {
15066
15170
  methodClassVariables.add(variable);
15067
15171
  methodAliases.add(variable);
@@ -15091,7 +15195,7 @@ function classScope(context, node, computedReferenceNames) {
15091
15195
  return;
15092
15196
  }
15093
15197
  if (!privateNames.has(target)) return;
15094
- const objectVariable = current.object.type === AST_NODE_TYPES57.Identifier ? ASTUtils17.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
15198
+ const objectVariable = current.object.type === AST_NODE_TYPES57.Identifier ? ASTUtils18.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
15095
15199
  if (objectVariable !== null && methodAliases.has(objectVariable)) {
15096
15200
  pinned.add(target);
15097
15201
  return;
@@ -15199,7 +15303,7 @@ var stepdown_default = createRule({
15199
15303
 
15200
15304
  // src/rules/source-coupled-test.ts
15201
15305
  import { AST_NODE_TYPES as AST_NODE_TYPES58 } from "@typescript-eslint/utils";
15202
- var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|py|[cm]?[jt]s)$/iu;
15306
+ var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc|py|[cm]?[jt]s)$/iu;
15203
15307
  var FS_MODULES = /* @__PURE__ */ new Set(["fs", "node:fs", "fs/promises", "node:fs/promises"]);
15204
15308
  var FS_READERS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
15205
15309
  var TEXT_TRANSFORMS = /* @__PURE__ */ new Set([
@@ -15394,6 +15498,13 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
15394
15498
  if (receiver.type !== AST_NODE_TYPES58.Identifier || receiver.name !== "assert" || !ASSERT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
15395
15499
  return new Set(node.arguments.flatMap((argument) => argument.type === AST_NODE_TYPES58.SpreadElement ? [] : [...evidenceOrigins(argument)]));
15396
15500
  };
15501
+ const rawRegexExtractionOrigins = (node) => {
15502
+ const callee = unwrap5(node.callee);
15503
+ if (callee.type !== AST_NODE_TYPES58.MemberExpression || staticMemberName7(callee) !== "matchAll" || node.arguments.length !== 1) return /* @__PURE__ */ new Set();
15504
+ const argument = node.arguments[0];
15505
+ if (argument?.type !== AST_NODE_TYPES58.Literal || !(argument.value instanceof RegExp)) return /* @__PURE__ */ new Set();
15506
+ return rawOrigins(callee.object);
15507
+ };
15397
15508
  const declare = (name2, state) => {
15398
15509
  const scope = currentScope();
15399
15510
  scope.declared.add(name2);
@@ -15473,7 +15584,10 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
15473
15584
  if (collection && left?.type === AST_NODE_TYPES58.Identifier) declare(left.name, { path: true });
15474
15585
  },
15475
15586
  CallExpression(node) {
15476
- const origins = rawAssertionOrigins(node);
15587
+ const origins = /* @__PURE__ */ new Set([
15588
+ ...rawAssertionOrigins(node),
15589
+ ...rawRegexExtractionOrigins(node)
15590
+ ]);
15477
15591
  if (origins.size === 0 || [...origins].every((origin) => reportedOrigins.has(origin))) return;
15478
15592
  for (const origin of origins) reportedOrigins.add(origin);
15479
15593
  context.report({ node, messageId: "rawSourceOracle" });
@@ -15529,7 +15643,7 @@ var iac_source_coupled_test_default = createSourceCoupledRule(
15529
15643
  // src/rules/zod-naming-convention.ts
15530
15644
  import {
15531
15645
  AST_NODE_TYPES as AST_NODE_TYPES59,
15532
- ASTUtils as ASTUtils18
15646
+ ASTUtils as ASTUtils19
15533
15647
  } from "@typescript-eslint/utils";
15534
15648
  var zodNamingConventionDocumentation = {
15535
15649
  summary: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.",
@@ -15622,7 +15736,7 @@ var zod_naming_convention_default = createRule({
15622
15736
  const acceptsSchemaWord = convention !== "prefix";
15623
15737
  const zodBindings = /* @__PURE__ */ new Set();
15624
15738
  function resolvedBinding(identifier) {
15625
- return ASTUtils18.findVariable(
15739
+ return ASTUtils19.findVariable(
15626
15740
  context.sourceCode.getScope(identifier),
15627
15741
  identifier.name
15628
15742
  );
@@ -15824,7 +15938,7 @@ var rules = {
15824
15938
  };
15825
15939
  var meta = {
15826
15940
  name: "@sarj/eslint-plugin",
15827
- version: "15.8.2"
15941
+ version: "15.9.0"
15828
15942
  };
15829
15943
  var applicationOnlyRules = [
15830
15944
  "no-restricted-library-load",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "15.8.2",
3
+ "version": "15.9.0",
4
4
  "packageManager": "npm@12.0.2",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",