@sarj/eslint-plugin 11.0.1 → 11.1.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.d.ts CHANGED
@@ -453,7 +453,7 @@ type FlatPreset = {
453
453
  declare const plugin: {
454
454
  readonly meta: {
455
455
  readonly name: "@sarj/eslint-plugin";
456
- readonly version: "11.0.1";
456
+ readonly version: "11.1.0";
457
457
  };
458
458
  readonly rules: {
459
459
  readonly "duplicate-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"duplicateTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
package/dist/index.js CHANGED
@@ -2899,6 +2899,16 @@ var PAYLOAD_PROPS = /* @__PURE__ */ new Set([
2899
2899
  "metadata",
2900
2900
  "context"
2901
2901
  ]);
2902
+ var BUILTIN_ERROR_CONSTRUCTORS = /* @__PURE__ */ new Set([
2903
+ "AggregateError",
2904
+ "Error",
2905
+ "EvalError",
2906
+ "RangeError",
2907
+ "ReferenceError",
2908
+ "SyntaxError",
2909
+ "TypeError",
2910
+ "URIError"
2911
+ ]);
2902
2912
  function isCatchBinding(scope, name) {
2903
2913
  let current = scope;
2904
2914
  while (current) {
@@ -3013,6 +3023,48 @@ function nodeWithin(node, container) {
3013
3023
  function isJsonStringify(callee) {
3014
3024
  return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
3015
3025
  }
3026
+ function directLiteralValues(argument) {
3027
+ if (argument.type === "ObjectExpression") {
3028
+ return argument.properties.flatMap((property) => {
3029
+ if (property.type !== "Property" || property.computed) return [];
3030
+ const value = property.value;
3031
+ if (value.type === "AssignmentPattern" || value.type === "ArrayPattern" || value.type === "ObjectPattern" || value.type === "TSEmptyBodyFunctionExpression") {
3032
+ return [];
3033
+ }
3034
+ return [value];
3035
+ });
3036
+ }
3037
+ if (argument.type === "ArrayExpression") {
3038
+ return argument.elements.flatMap(
3039
+ (element) => element !== null && element.type !== "SpreadElement" ? [element] : []
3040
+ );
3041
+ }
3042
+ return argument.type === "SpreadElement" ? [] : [argument];
3043
+ }
3044
+ function expressionSuggestsError(expression, scope) {
3045
+ if (expression.type === "Identifier") {
3046
+ return ERROR_NAME_PATTERN.test(expression.name) || isCatchBinding(scope, expression.name);
3047
+ }
3048
+ return expression.type === "MemberExpression" && memberSuggestsError(expression, scope);
3049
+ }
3050
+ function nestedExpressionSuggestsError(expression, scope) {
3051
+ if (expression.type === "Identifier") {
3052
+ if (isCatchBinding(scope, expression.name)) return true;
3053
+ let current = scope;
3054
+ while (current !== null && !current.set.has(expression.name)) {
3055
+ current = current.upper;
3056
+ }
3057
+ const variable = current?.set.get(expression.name);
3058
+ if (variable === void 0 || variable.defs.length !== 1) return false;
3059
+ const definition = variable.defs[0];
3060
+ if (definition?.type !== "Variable") return false;
3061
+ const initializer = definition.node.init;
3062
+ return initializer?.type === "NewExpression" && initializer.callee.type === "Identifier" && BUILTIN_ERROR_CONSTRUCTORS.has(initializer.callee.name) && variable.references.every(
3063
+ (reference) => !reference.isWrite() || reference.init === true
3064
+ );
3065
+ }
3066
+ return expression.type === "MemberExpression" && memberSuggestsError(expression, scope);
3067
+ }
3016
3068
  var no_json_stringify_error_default = createRule({
3017
3069
  name: "no-json-stringify-error",
3018
3070
  meta: {
@@ -3037,18 +3089,11 @@ var no_json_stringify_error_default = createRule({
3037
3089
  return;
3038
3090
  }
3039
3091
  const scope = context.sourceCode.getScope(firstArg);
3040
- let suggestsError;
3041
- if (firstArg.type === "Identifier") {
3042
- suggestsError = ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);
3043
- } else if (firstArg.type === "MemberExpression") {
3044
- suggestsError = memberSuggestsError(firstArg, scope);
3045
- } else {
3046
- return;
3047
- }
3048
- if (!suggestsError) {
3049
- return;
3050
- }
3051
- if (isGuardedByInstanceofError(node, firstArg, context.sourceCode) || isNarrowedByEarlyReturn(node, firstArg, context.sourceCode)) {
3092
+ const isNestedLiteral = firstArg.type === "ObjectExpression" || firstArg.type === "ArrayExpression";
3093
+ const unsafeValue = directLiteralValues(firstArg).find(
3094
+ (value) => (isNestedLiteral ? nestedExpressionSuggestsError(value, scope) : expressionSuggestsError(value, scope)) && !isGuardedByInstanceofError(node, value, context.sourceCode) && !isNarrowedByEarlyReturn(node, value, context.sourceCode)
3095
+ );
3096
+ if (unsafeValue === void 0) {
3052
3097
  return;
3053
3098
  }
3054
3099
  context.report({
@@ -5118,50 +5163,59 @@ var no_select_star_default = createRule({
5118
5163
 
5119
5164
  // src/rules/no-sentinel-return-on-catch.ts
5120
5165
  import { AST_NODE_TYPES as AST_NODE_TYPES23 } from "@typescript-eslint/utils";
5166
+ function unwrapSentinelExpression(arg) {
5167
+ let current = arg;
5168
+ while (current?.type === AST_NODE_TYPES23.TSAsExpression || current?.type === AST_NODE_TYPES23.TSTypeAssertion || current?.type === AST_NODE_TYPES23.TSSatisfiesExpression) {
5169
+ current = current.expression;
5170
+ }
5171
+ return current;
5172
+ }
5121
5173
  function sentinelKind(arg) {
5122
- if (arg === null) {
5174
+ const value = unwrapSentinelExpression(arg);
5175
+ if (value === null) {
5123
5176
  return null;
5124
5177
  }
5125
- if (arg.type === AST_NODE_TYPES23.Literal) {
5126
- if (arg.value === null) {
5178
+ if (value.type === AST_NODE_TYPES23.Literal) {
5179
+ if (value.value === null) {
5127
5180
  return "nullish";
5128
5181
  }
5129
- if (typeof arg.value === "boolean") {
5182
+ if (typeof value.value === "boolean") {
5130
5183
  return "boolean";
5131
5184
  }
5132
- if (typeof arg.value === "string") {
5185
+ if (typeof value.value === "string") {
5133
5186
  return "string";
5134
5187
  }
5135
5188
  return null;
5136
5189
  }
5137
- if (arg.type === AST_NODE_TYPES23.Identifier && arg.name === "undefined") {
5190
+ if (value.type === AST_NODE_TYPES23.Identifier && value.name === "undefined") {
5138
5191
  return "nullish";
5139
5192
  }
5140
- if (arg.type === AST_NODE_TYPES23.ArrayExpression) {
5193
+ if (value.type === AST_NODE_TYPES23.ArrayExpression) {
5141
5194
  return "array";
5142
5195
  }
5143
- if (arg.type === AST_NODE_TYPES23.ObjectExpression) {
5196
+ if (value.type === AST_NODE_TYPES23.ObjectExpression) {
5144
5197
  return "object";
5145
5198
  }
5146
5199
  return null;
5147
5200
  }
5148
5201
  function isSentinelArgument(arg) {
5149
- if (arg === null) {
5202
+ const value = unwrapSentinelExpression(arg);
5203
+ if (value === null) {
5150
5204
  return false;
5151
5205
  }
5152
- if (arg.type === AST_NODE_TYPES23.Literal && arg.value === null) {
5206
+ if (value.type === AST_NODE_TYPES23.Literal && value.value === null) {
5153
5207
  return true;
5154
5208
  }
5155
- if (arg.type === AST_NODE_TYPES23.Literal && arg.value === false) {
5209
+ if (value.type === AST_NODE_TYPES23.Literal && value.value === false) {
5156
5210
  return true;
5157
5211
  }
5158
- if (arg.type === AST_NODE_TYPES23.Identifier && arg.name === "undefined") {
5212
+ if (value.type === AST_NODE_TYPES23.Identifier && value.name === "undefined") {
5159
5213
  return true;
5160
5214
  }
5161
- if (arg.type === AST_NODE_TYPES23.ArrayExpression && arg.elements.length === 0) {
5215
+ if (value.type === AST_NODE_TYPES23.ArrayExpression && value.elements.length === 0) {
5162
5216
  return true;
5163
5217
  }
5164
- if (arg.type === AST_NODE_TYPES23.ObjectExpression && arg.properties.length === 0) {
5218
+ if (value.type === AST_NODE_TYPES23.ObjectExpression && value.properties.length === 0) {
5165
5219
  return true;
5166
5220
  }
5167
5221
  return false;
@@ -5359,6 +5413,16 @@ function tryReturnsSafeParse(catchNode) {
5359
5413
  const only = tryBlock.body.length === 1 ? tryBlock.body[0] : void 0;
5360
5414
  return only !== void 0 && returnsMatching(only, isBodyDecodeNode);
5361
5415
  }
5416
+ function isIntentionalStackCapture(catchNode, caughtName) {
5417
+ if (caughtName === null) return false;
5418
+ const tryBody = tryBlockOf(catchNode).body;
5419
+ const only = tryBody.length === 1 ? tryBody[0] : void 0;
5420
+ if (only?.type !== AST_NODE_TYPES23.ThrowStatement) return false;
5421
+ const thrown = unwrapSentinelExpression(only.argument);
5422
+ const constructsError = (thrown?.type === AST_NODE_TYPES23.CallExpression || thrown?.type === AST_NODE_TYPES23.NewExpression) && thrown.callee.type === AST_NODE_TYPES23.Identifier && thrown.callee.name === "Error";
5423
+ if (!constructsError) return false;
5424
+ return catchNode.body.body.slice(0, -1).some((statement) => subtreeReadsName(statement, caughtName));
5425
+ }
5362
5426
  function tryBlockOf(catchNode) {
5363
5427
  return catchNode.parent.block;
5364
5428
  }
@@ -5479,6 +5543,9 @@ var no_sentinel_return_on_catch_default = createRule({
5479
5543
  if (tryReturnsSafeParse(node)) {
5480
5544
  return;
5481
5545
  }
5546
+ if (isIntentionalStackCapture(node, caughtName)) {
5547
+ return;
5548
+ }
5482
5549
  const kind = sentinelKind(last.argument);
5483
5550
  if (kind !== null && isNamedBooleanPredicate(node, kind)) {
5484
5551
  return;
@@ -5557,11 +5624,11 @@ var no_silent_promise_catch_default = createRule({
5557
5624
  meta: {
5558
5625
  type: "problem",
5559
5626
  docs: {
5560
- description: "Disallow `.catch()` handlers that silently swallow the rejection (e.g. `.catch(() => null)`); log, rethrow, or handle the error."
5627
+ description: "Disallow `.catch()` and second-argument `.then()` handlers that silently swallow a rejection; log, rethrow, or handle the error."
5561
5628
  },
5562
5629
  schema: [],
5563
5630
  messages: {
5564
- silentCatch: "This `.catch()` swallows the rejection without logging, rethrowing, or handling it \u2014 failures become invisible and callers get an indistinguishable sentinel. Log the error (and only then map to a fallback), or let it propagate."
5631
+ silentCatch: "This rejection handler swallows the error without logging, rethrowing, or handling it \u2014 failures become invisible and callers get an indistinguishable sentinel. Log the error (and only then map to a fallback), or let it propagate."
5565
5632
  }
5566
5633
  },
5567
5634
  defaultOptions: [],
@@ -5587,9 +5654,12 @@ var no_silent_promise_catch_default = createRule({
5587
5654
  };
5588
5655
  return {
5589
5656
  CallExpression(node) {
5590
- if (node.callee.type !== AST_NODE_TYPES24.MemberExpression || node.callee.computed || node.callee.property.type !== AST_NODE_TYPES24.Identifier || node.callee.property.name !== "catch") {
5657
+ if (node.callee.type !== AST_NODE_TYPES24.MemberExpression || node.callee.computed || node.callee.property.type !== AST_NODE_TYPES24.Identifier) {
5591
5658
  return;
5592
5659
  }
5660
+ const method = node.callee.property.name;
5661
+ const handlerIndex = method === "catch" ? 0 : method === "then" ? 1 : null;
5662
+ if (handlerIndex === null) return;
5593
5663
  if (isBodyParseCall(node.callee.object)) {
5594
5664
  return;
5595
5665
  }
@@ -5599,10 +5669,11 @@ var no_silent_promise_catch_default = createRule({
5599
5669
  if (node.parent.type === AST_NODE_TYPES24.MemberExpression && node.parent.object === node) {
5600
5670
  return;
5601
5671
  }
5602
- if (node.arguments.length !== 1) {
5672
+ const expectedArguments = method === "catch" ? 1 : 2;
5673
+ if (node.arguments.length !== expectedArguments) {
5603
5674
  return;
5604
5675
  }
5605
- const handler = node.arguments[0];
5676
+ const handler = node.arguments[handlerIndex];
5606
5677
  if (handler === void 0 || handler.type !== AST_NODE_TYPES24.ArrowFunctionExpression && handler.type !== AST_NODE_TYPES24.FunctionExpression) {
5607
5678
  return;
5608
5679
  }
@@ -5865,6 +5936,11 @@ function isStringLiteralInit(node) {
5865
5936
  return false;
5866
5937
  }
5867
5938
  function isConcatOntoTarget(rhs, target) {
5939
+ if (rhs.type === "TemplateLiteral") {
5940
+ return rhs.expressions.some(
5941
+ (expression) => expression.type === "Identifier" && expression.name === target
5942
+ );
5943
+ }
5868
5944
  if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
5869
5945
  return false;
5870
5946
  }
@@ -7420,6 +7496,15 @@ var SUCCESS_PAYLOAD_MEMBER_NAMES = /* @__PURE__ */ new Set([
7420
7496
  "value"
7421
7497
  ]);
7422
7498
  var REQUIRED_STATUS_MEMBER_COUNT = 1;
7499
+ var FUNCTION_RETURN_OWNER_TYPES = /* @__PURE__ */ new Set([
7500
+ AST_NODE_TYPES37.ArrowFunctionExpression,
7501
+ AST_NODE_TYPES37.FunctionDeclaration,
7502
+ AST_NODE_TYPES37.FunctionExpression,
7503
+ AST_NODE_TYPES37.TSDeclareFunction,
7504
+ AST_NODE_TYPES37.TSEmptyBodyFunctionExpression,
7505
+ AST_NODE_TYPES37.TSFunctionType,
7506
+ AST_NODE_TYPES37.TSMethodSignature
7507
+ ]);
7423
7508
  function looksLikeMutuallyExclusiveState(typeLiteral) {
7424
7509
  let statusMemberCount = 0;
7425
7510
  let hasFailurePayload = false;
@@ -7460,6 +7545,17 @@ function getMemberName(member) {
7460
7545
  function isBooleanTyped(member) {
7461
7546
  return member.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES37.TSBooleanKeyword;
7462
7547
  }
7548
+ function inlineReturnTypeLiteral(node) {
7549
+ let annotation = null;
7550
+ if (node.parent.type === AST_NODE_TYPES37.TSTypeAnnotation) {
7551
+ annotation = node.parent;
7552
+ } else if (node.parent.type === AST_NODE_TYPES37.TSTypeParameterInstantiation && node.parent.params.length === 1 && node.parent.params[0] === node && node.parent.parent.type === AST_NODE_TYPES37.TSTypeReference && node.parent.parent.typeName.type === AST_NODE_TYPES37.Identifier && node.parent.parent.typeName.name === "Promise" && node.parent.parent.parent.type === AST_NODE_TYPES37.TSTypeAnnotation) {
7553
+ annotation = node.parent.parent.parent;
7554
+ }
7555
+ if (annotation === null) return null;
7556
+ const owner = annotation.parent;
7557
+ return FUNCTION_RETURN_OWNER_TYPES.has(owner.type) && "returnType" in owner && owner.returnType === annotation ? annotation : null;
7558
+ }
7463
7559
  var prefer_discriminated_union_default = createRule({
7464
7560
  name: "prefer-discriminated-union",
7465
7561
  meta: {
@@ -7499,6 +7595,10 @@ var prefer_discriminated_union_default = createRule({
7499
7595
  },
7500
7596
  "TSTypeAliasDeclaration > TSTypeLiteral"(node) {
7501
7597
  checkTypeLiteral(node, node.parent);
7598
+ },
7599
+ TSTypeLiteral(node) {
7600
+ const annotation = inlineReturnTypeLiteral(node);
7601
+ if (annotation !== null) checkTypeLiteral(node, annotation);
7502
7602
  }
7503
7603
  };
7504
7604
  }
@@ -8921,7 +9021,7 @@ var isSchemaParseReference = (node) => {
8921
9021
  const inner = unwrap4(node);
8922
9022
  return inner !== null && inner.type === AST_NODE_TYPES45.MemberExpression && !inner.computed && inner.property.type === AST_NODE_TYPES45.Identifier && (inner.property.name === "parse" || inner.property.name === "safeParse");
8923
9023
  };
8924
- var isRawPayloadSource = (node) => {
9024
+ var isRawPayloadSource = (node, isKnownLocalText) => {
8925
9025
  let current = unwrap4(node);
8926
9026
  if (current === null) return false;
8927
9027
  if (current.type === AST_NODE_TYPES45.AwaitExpression) {
@@ -8945,20 +9045,26 @@ var isRawPayloadSource = (node) => {
8945
9045
  return !current.arguments.some(isSchemaParseReference) && isRawPayloadSource(callee.object);
8946
9046
  }
8947
9047
  const object = unwrap4(callee.object);
8948
- return property.name === "parse" && object !== null && object.type === AST_NODE_TYPES45.Identifier && object.name === "JSON" && !isLocalFileRead(current.arguments[0]);
9048
+ return property.name === "parse" && object !== null && object.type === AST_NODE_TYPES45.Identifier && object.name === "JSON" && !isLocalFileRead(current.arguments[0]) && isKnownLocalText?.(current.arguments[0]) !== true;
8949
9049
  };
8950
9050
  var FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;
9051
+ var isDirectLocalFileRead = (node) => {
9052
+ let current = unwrap4(node);
9053
+ if (current?.type === AST_NODE_TYPES45.AwaitExpression) {
9054
+ current = unwrap4(current.argument);
9055
+ }
9056
+ if (current?.type !== AST_NODE_TYPES45.CallExpression) return false;
9057
+ const callee = unwrap4(current.callee);
9058
+ const name = callee?.type === AST_NODE_TYPES45.Identifier ? callee.name : callee?.type === AST_NODE_TYPES45.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES45.Identifier ? callee.property.name : null;
9059
+ return name !== null && FILE_READ_RE.test(name);
9060
+ };
8951
9061
  var isLocalFileRead = (node) => {
8952
9062
  let found = false;
8953
9063
  const visit = (current) => {
8954
9064
  if (found || current === null || current === void 0) return;
8955
- if (current.type === AST_NODE_TYPES45.CallExpression) {
8956
- const callee = unwrap4(current.callee);
8957
- const name = callee?.type === AST_NODE_TYPES45.Identifier ? callee.name : callee?.type === AST_NODE_TYPES45.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES45.Identifier ? callee.property.name : null;
8958
- if (name !== null && FILE_READ_RE.test(name)) {
8959
- found = true;
8960
- return;
8961
- }
9065
+ if (isDirectLocalFileRead(current)) {
9066
+ found = true;
9067
+ return;
8962
9068
  }
8963
9069
  for (const key of Object.keys(current)) {
8964
9070
  if (key === "parent") continue;
@@ -9020,6 +9126,87 @@ var isValidationRead = (node) => {
9020
9126
  }
9021
9127
  return callee.type === AST_NODE_TYPES45.Identifier && GUARD_NAME_RE.test(callee.name);
9022
9128
  };
9129
+ var PRIMITIVE_TYPEOF_RESULTS = /* @__PURE__ */ new Set([
9130
+ "bigint",
9131
+ "boolean",
9132
+ "number",
9133
+ "string",
9134
+ "symbol",
9135
+ "undefined"
9136
+ ]);
9137
+ var bindingValidationPolarity = (test, bindingName) => {
9138
+ if (test.type === AST_NODE_TYPES45.UnaryExpression && test.operator === "!") {
9139
+ const inner = bindingValidationPolarity(test.argument, bindingName);
9140
+ return inner === "valid-when-true" ? "valid-when-false" : inner === "valid-when-false" ? "valid-when-true" : null;
9141
+ }
9142
+ if (test.type === AST_NODE_TYPES45.BinaryExpression) {
9143
+ const typeofName = (node) => node.type === AST_NODE_TYPES45.UnaryExpression && node.operator === "typeof" && node.argument.type === AST_NODE_TYPES45.Identifier ? node.argument.name : null;
9144
+ const literalType = (node) => node.type === AST_NODE_TYPES45.Literal && typeof node.value === "string" && PRIMITIVE_TYPEOF_RESULTS.has(node.value) ? node.value : null;
9145
+ const matches = typeofName(test.left) === bindingName && literalType(test.right) !== null || typeofName(test.right) === bindingName && literalType(test.left) !== null;
9146
+ if (!matches) return null;
9147
+ if (test.operator === "===" || test.operator === "==") {
9148
+ return "valid-when-true";
9149
+ }
9150
+ return test.operator === "!==" || test.operator === "!=" ? "valid-when-false" : null;
9151
+ }
9152
+ return test.type === AST_NODE_TYPES45.CallExpression && test.arguments.length === 1 && test.arguments[0]?.type === AST_NODE_TYPES45.Identifier && test.arguments[0].name === bindingName && test.callee.type === AST_NODE_TYPES45.MemberExpression && !test.callee.computed && test.callee.object.type === AST_NODE_TYPES45.Identifier && test.callee.object.name === "Array" && test.callee.property.type === AST_NODE_TYPES45.Identifier && test.callee.property.name === "isArray" ? "valid-when-true" : null;
9153
+ };
9154
+ var nodeWithin2 = (node, container) => node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
9155
+ var isFullyValidatedExtractedBinding = (member, source, context) => {
9156
+ const isValidationReference = (identifier) => {
9157
+ for (let current = identifier.parent; current !== void 0 && current !== null; current = current.parent) {
9158
+ if ((current.type === AST_NODE_TYPES45.BinaryExpression || current.type === AST_NODE_TYPES45.CallExpression || current.type === AST_NODE_TYPES45.UnaryExpression) && bindingValidationPolarity(current, identifier.name) !== null) {
9159
+ return true;
9160
+ }
9161
+ if (current.type !== AST_NODE_TYPES45.UnaryExpression && current.type !== AST_NODE_TYPES45.MemberExpression && current.type !== AST_NODE_TYPES45.CallExpression) {
9162
+ return false;
9163
+ }
9164
+ }
9165
+ return false;
9166
+ };
9167
+ const isGuardedUse = (identifier) => {
9168
+ for (let current = identifier.parent; current !== void 0 && current !== null; current = current.parent) {
9169
+ if (current.type === AST_NODE_TYPES45.ConditionalExpression) {
9170
+ const polarity = bindingValidationPolarity(current.test, identifier.name);
9171
+ if (polarity === "valid-when-true" && nodeWithin2(identifier, current.consequent)) {
9172
+ return true;
9173
+ }
9174
+ if (polarity === "valid-when-false" && nodeWithin2(identifier, current.alternate)) {
9175
+ return true;
9176
+ }
9177
+ }
9178
+ if (current.type === AST_NODE_TYPES45.IfStatement) {
9179
+ const polarity = bindingValidationPolarity(current.test, identifier.name);
9180
+ if (polarity === "valid-when-true" && nodeWithin2(identifier, current.consequent)) {
9181
+ return true;
9182
+ }
9183
+ if (polarity === "valid-when-false" && current.alternate !== null && nodeWithin2(identifier, current.alternate)) {
9184
+ return true;
9185
+ }
9186
+ }
9187
+ if (current.type === AST_NODE_TYPES45.FunctionDeclaration || current.type === AST_NODE_TYPES45.FunctionExpression || current.type === AST_NODE_TYPES45.ArrowFunctionExpression) {
9188
+ return false;
9189
+ }
9190
+ }
9191
+ return false;
9192
+ };
9193
+ const declarator = member.parent;
9194
+ if (declarator.type !== AST_NODE_TYPES45.VariableDeclarator || declarator.init !== member || declarator.id.type !== AST_NODE_TYPES45.Identifier || declarator.parent.type !== AST_NODE_TYPES45.VariableDeclaration || declarator.parent.kind !== "const") {
9195
+ return false;
9196
+ }
9197
+ const extracted = context.sourceCode.getDeclaredVariables(declarator)[0];
9198
+ if (extracted === void 0 || extracted.scope !== source.scope) return false;
9199
+ let hasValueUse = false;
9200
+ for (const reference of extracted.references) {
9201
+ const identifier = reference.identifier;
9202
+ if (identifier.type !== AST_NODE_TYPES45.Identifier) return false;
9203
+ if (nodeWithin2(identifier, declarator)) continue;
9204
+ if (isValidationReference(identifier)) continue;
9205
+ hasValueUse = true;
9206
+ if (!isGuardedUse(identifier)) return false;
9207
+ }
9208
+ return hasValueUse;
9209
+ };
9023
9210
  var isGuardTestPosition = (node) => {
9024
9211
  let current = node;
9025
9212
  let parent = current.parent;
@@ -9043,13 +9230,13 @@ var isGuardTestPosition = (node) => {
9043
9230
  }
9044
9231
  return false;
9045
9232
  };
9046
- var isUnvalidatedVariableRef = (node, scope, tracked) => {
9233
+ var unvalidatedVariableRef = (node, scope, tracked) => {
9047
9234
  const unwrapped = unwrap4(node);
9048
9235
  if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES45.Identifier) {
9049
- return false;
9236
+ return null;
9050
9237
  }
9051
9238
  const variable = findVariable2(scope, unwrapped.name);
9052
- return variable !== null && tracked.has(variable);
9239
+ return variable !== null && tracked.has(variable) ? variable : null;
9053
9240
  };
9054
9241
  var prefer_schema_for_api_payload_default = createRule({
9055
9242
  name: "prefer-schema-for-api-payload",
@@ -9069,6 +9256,56 @@ var prefer_schema_for_api_payload_default = createRule({
9069
9256
  return {};
9070
9257
  }
9071
9258
  const unvalidatedVariables = /* @__PURE__ */ new Set();
9259
+ const aliasGroups = /* @__PURE__ */ new Map();
9260
+ const localFileTextVariables = /* @__PURE__ */ new Set();
9261
+ const localFileTextRef = (node, scope) => {
9262
+ const unwrapped = unwrap4(node);
9263
+ if (unwrapped?.type !== AST_NODE_TYPES45.Identifier) return null;
9264
+ const variable = findVariable2(scope, unwrapped.name);
9265
+ return variable !== null && localFileTextVariables.has(variable) ? variable : null;
9266
+ };
9267
+ const updateLocalFileText = (target, value, scope) => {
9268
+ const source = localFileTextRef(value, scope);
9269
+ if (isDirectLocalFileRead(value) || source !== null && source.scope === target.scope) {
9270
+ localFileTextVariables.add(target);
9271
+ } else {
9272
+ localFileTextVariables.delete(target);
9273
+ }
9274
+ };
9275
+ const clearBinding = (variable) => {
9276
+ unvalidatedVariables.delete(variable);
9277
+ const group = aliasGroups.get(variable);
9278
+ aliasGroups.delete(variable);
9279
+ group?.delete(variable);
9280
+ };
9281
+ const clearAliasGroup = (variable) => {
9282
+ const group = aliasGroups.get(variable);
9283
+ if (group === void 0) {
9284
+ unvalidatedVariables.delete(variable);
9285
+ return;
9286
+ }
9287
+ for (const alias of group) {
9288
+ unvalidatedVariables.delete(alias);
9289
+ aliasGroups.delete(alias);
9290
+ }
9291
+ group.clear();
9292
+ };
9293
+ const trackRawBinding = (variable) => {
9294
+ clearBinding(variable);
9295
+ const group = /* @__PURE__ */ new Set([variable]);
9296
+ unvalidatedVariables.add(variable);
9297
+ aliasGroups.set(variable, group);
9298
+ };
9299
+ const trackAlias = (target, source) => {
9300
+ if (target === source) return;
9301
+ const group = aliasGroups.get(source) ?? /* @__PURE__ */ new Set([source]);
9302
+ if (aliasGroups.get(target) === group) return;
9303
+ clearBinding(target);
9304
+ unvalidatedVariables.add(target);
9305
+ group.add(target);
9306
+ aliasGroups.set(source, group);
9307
+ aliasGroups.set(target, group);
9308
+ };
9072
9309
  const isFullyNarrowedPattern = (declarator) => {
9073
9310
  const declared = context.sourceCode.getDeclaredVariables(declarator);
9074
9311
  return declared.length > 0 && declared.every(
@@ -9077,29 +9314,40 @@ var prefer_schema_for_api_payload_default = createRule({
9077
9314
  )
9078
9315
  );
9079
9316
  };
9080
- const trackInitializer = (declarator) => {
9081
- if (!isRawPayloadSource(declarator.init)) return;
9317
+ const trackInitializer = (declarator, scope) => {
9082
9318
  const declaredVars = context.sourceCode.getDeclaredVariables(declarator);
9083
9319
  const variable = declaredVars[0];
9084
- if (variable !== void 0) {
9085
- unvalidatedVariables.add(variable);
9320
+ if (variable === void 0) return;
9321
+ const localText = (candidate) => localFileTextRef(candidate, scope) !== null;
9322
+ if (isRawPayloadSource(declarator.init, localText)) {
9323
+ trackRawBinding(variable);
9324
+ return;
9086
9325
  }
9326
+ const source = unvalidatedVariableRef(declarator.init, scope, unvalidatedVariables);
9327
+ if (source !== null) trackAlias(variable, source);
9087
9328
  };
9088
9329
  return {
9089
9330
  VariableDeclarator(node) {
9090
9331
  const scope = context.sourceCode.getScope(node);
9091
9332
  if (node.id.type === AST_NODE_TYPES45.Identifier) {
9092
- trackInitializer(node);
9333
+ const variable = context.sourceCode.getDeclaredVariables(node)[0];
9334
+ if (variable !== void 0) {
9335
+ updateLocalFileText(variable, node.init, scope);
9336
+ }
9337
+ trackInitializer(node, scope);
9093
9338
  return;
9094
9339
  }
9095
9340
  if (node.id.type === AST_NODE_TYPES45.ObjectPattern || node.id.type === AST_NODE_TYPES45.ArrayPattern) {
9096
- if (isRawPayloadSource(node.init)) {
9341
+ if (isRawPayloadSource(
9342
+ node.init,
9343
+ (candidate) => localFileTextRef(candidate, scope) !== null
9344
+ )) {
9097
9345
  if (!isFullyNarrowedPattern(node)) {
9098
9346
  context.report({ node: node.id, messageId: "unparsedJsonAccess" });
9099
9347
  }
9100
9348
  return;
9101
9349
  }
9102
- if (isUnvalidatedVariableRef(node.init, scope, unvalidatedVariables)) {
9350
+ if (unvalidatedVariableRef(node.init, scope, unvalidatedVariables) !== null) {
9103
9351
  context.report({ node: node.id, messageId: "unparsedJsonAccess" });
9104
9352
  }
9105
9353
  }
@@ -9109,22 +9357,29 @@ var prefer_schema_for_api_payload_default = createRule({
9109
9357
  if (node.left.type === AST_NODE_TYPES45.Identifier) {
9110
9358
  const variable = findVariable2(scope, node.left.name);
9111
9359
  if (variable === null) return;
9112
- if (isRawPayloadSource(node.right)) {
9113
- unvalidatedVariables.add(variable);
9360
+ const isLocalText = (candidate) => localFileTextRef(candidate, scope) !== null;
9361
+ updateLocalFileText(variable, node.right, scope);
9362
+ if (isRawPayloadSource(node.right, isLocalText)) {
9363
+ trackRawBinding(variable);
9114
9364
  } else {
9115
- unvalidatedVariables.delete(variable);
9365
+ const source = unvalidatedVariableRef(node.right, scope, unvalidatedVariables);
9366
+ if (source === null) clearBinding(variable);
9367
+ else trackAlias(variable, source);
9116
9368
  }
9117
9369
  return;
9118
9370
  }
9119
9371
  if (node.left.type === AST_NODE_TYPES45.ObjectPattern || node.left.type === AST_NODE_TYPES45.ArrayPattern) {
9120
- if (isRawPayloadSource(node.right)) {
9372
+ if (isRawPayloadSource(
9373
+ node.right,
9374
+ (candidate) => localFileTextRef(candidate, scope) !== null
9375
+ )) {
9121
9376
  context.report({
9122
9377
  node: node.left,
9123
9378
  messageId: "unparsedJsonAccess"
9124
9379
  });
9125
9380
  return;
9126
9381
  }
9127
- if (isUnvalidatedVariableRef(node.right, scope, unvalidatedVariables)) {
9382
+ if (unvalidatedVariableRef(node.right, scope, unvalidatedVariables) !== null) {
9128
9383
  context.report({
9129
9384
  node: node.left,
9130
9385
  messageId: "unparsedJsonAccess"
@@ -9145,7 +9400,7 @@ var prefer_schema_for_api_payload_default = createRule({
9145
9400
  continue;
9146
9401
  }
9147
9402
  const variable = findVariable2(scope, unwrapped.name);
9148
- if (variable !== null) unvalidatedVariables.delete(variable);
9403
+ if (variable !== null) clearAliasGroup(variable);
9149
9404
  }
9150
9405
  },
9151
9406
  MemberExpression(node) {
@@ -9153,7 +9408,10 @@ var prefer_schema_for_api_payload_default = createRule({
9153
9408
  if (isValidationRead(node)) return;
9154
9409
  const scope = context.sourceCode.getScope(node);
9155
9410
  const obj = unwrap4(node.object);
9156
- if (isRawPayloadSource(obj)) {
9411
+ if (isRawPayloadSource(
9412
+ obj,
9413
+ (candidate) => localFileTextRef(candidate, scope) !== null
9414
+ )) {
9157
9415
  const parent = node.parent;
9158
9416
  if (parent.type === AST_NODE_TYPES45.CallExpression && parent.callee === node && node.property.type === AST_NODE_TYPES45.Identifier && (node.property.name === "parse" || node.property.name === "safeParse" || PROMISE_CHAIN_METHODS.has(node.property.name))) {
9159
9417
  return;
@@ -9161,12 +9419,13 @@ var prefer_schema_for_api_payload_default = createRule({
9161
9419
  context.report({ node, messageId: "unparsedJsonAccess" });
9162
9420
  return;
9163
9421
  }
9164
- if (obj !== null && obj.type === AST_NODE_TYPES45.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
9165
- context.report({ node, messageId: "unparsedJsonAccess" });
9166
- const variable = findVariable2(scope, obj.name);
9167
- if (variable !== null) {
9168
- unvalidatedVariables.delete(variable);
9422
+ const variable = obj?.type === AST_NODE_TYPES45.Identifier ? unvalidatedVariableRef(obj, scope, unvalidatedVariables) : null;
9423
+ if (variable !== null) {
9424
+ if (isFullyValidatedExtractedBinding(node, variable, context)) {
9425
+ return;
9169
9426
  }
9427
+ context.report({ node, messageId: "unparsedJsonAccess" });
9428
+ clearAliasGroup(variable);
9170
9429
  }
9171
9430
  }
9172
9431
  };
@@ -10933,8 +11192,8 @@ function initProvablyLacksSignal(init) {
10933
11192
  }
10934
11193
  return true;
10935
11194
  }
10936
- function isStringish(node) {
10937
- return node.type === AST_NODE_TYPES52.Literal && typeof node.value === "string" || node.type === AST_NODE_TYPES52.TemplateLiteral;
11195
+ function isInlineUrl(node, resolvesToGlobal) {
11196
+ return node.type === AST_NODE_TYPES52.Literal && typeof node.value === "string" || node.type === AST_NODE_TYPES52.TemplateLiteral || node.type === AST_NODE_TYPES52.NewExpression && node.callee.type === AST_NODE_TYPES52.Identifier && node.callee.name === "URL" && resolvesToGlobal(node.callee);
10938
11197
  }
10939
11198
  var require_fetch_timeout_default = createRule({
10940
11199
  name: "require-fetch-timeout",
@@ -10986,7 +11245,7 @@ var require_fetch_timeout_default = createRule({
10986
11245
  return;
10987
11246
  }
10988
11247
  const [first, init] = node.arguments;
10989
- if (node.arguments.length === 1 && first !== void 0 && !isStringish(first)) {
11248
+ if (node.arguments.length === 1 && first !== void 0 && !isInlineUrl(first, resolvesToGlobal)) {
10990
11249
  return;
10991
11250
  }
10992
11251
  if (init === void 0 || initProvablyLacksSignal(init)) {
@@ -11003,7 +11262,7 @@ var CONFIGISH_TYPE_RE = /(?:Options|Opts|Config|Configuration|Settings|Params|Pr
11003
11262
  var CONFIGISH_NAME_RE = /^(?:options|opts|config|configuration|settings|params|props|args|env|environment|callbacks|flags|logger|log|clock)$/i;
11004
11263
  var HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;
11005
11264
  var BUILTIN_CONTAINER_TYPE_RE = /^(?:Record|Map|WeakMap|Set|WeakSet|Array|ReadonlyArray|ReadonlyMap|ReadonlySet|Promise|Partial|Required|Readonly|Pick|Omit|Exclude|Extract|NonNullable|Awaited|Parameters|ReturnType|InstanceType)$/;
11006
- var VALUE_TYPE_RE = /^(?:ArrayBuffer|Blob|Buffer|Date|RegExp|URL|URLSearchParams)$/;
11265
+ var VALUE_TYPE_RE = /^(?:ArrayBuffer|Blob|Buffer|Date|NodePath|RegExp|URL|URLSearchParams)$/;
11007
11266
  var TRANSPORT_WRAPPER_NAME_RE = /(?:Client$|^Http[A-Z])/;
11008
11267
  var ERROR_VALUE_NAME_RE = /Error$/;
11009
11268
  var FLUENT_BUILDER_NAME_RE = /Builder$/;
@@ -11467,7 +11726,7 @@ var require_interface_for_injected_service_default = createRule({
11467
11726
  if (node.abstract === true) return;
11468
11727
  if (node.decorators.length > 0) return;
11469
11728
  const ctor = node.body.body.find(
11470
- (member) => member.type === AST_NODE_TYPES53.MethodDefinition && member.kind === "constructor"
11729
+ (member) => member.type === AST_NODE_TYPES53.MethodDefinition && member.kind === "constructor" && member.value.body !== null && member.value.body !== void 0
11471
11730
  );
11472
11731
  if (ctor === void 0) return;
11473
11732
  const constructorFacts = readConstructor(
@@ -12346,7 +12605,7 @@ var rules = {
12346
12605
  };
12347
12606
  var meta = {
12348
12607
  name: "@sarj/eslint-plugin",
12349
- version: "11.0.1"
12608
+ version: "11.1.0"
12350
12609
  };
12351
12610
  var applicationOnlyRules = [
12352
12611
  "no-restricted-library-load",