@sarj/eslint-plugin 11.0.1 → 11.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -827,6 +827,7 @@ var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}|[\u2500-\u257f]{4,}/;
827
827
  var REGION_MARKER_RE = /^#?(?:end)?region\b(.*)$/i;
828
828
  var REGION_TITLE_RE = /^[\s:\-\u2013\u2014]*\w[\w \-/&+]*$/;
829
829
  var REGION_TITLE_MAX_WORDS = 5;
830
+ var REGION_PROSE_VERB_RE = /^(?:is|are|was|were|comes?|defaults?|derives?|inherits?|depends?|uses?|maps?|resolves?)\b/i;
830
831
  function stripCommentMarker(line) {
831
832
  return line.replace(/^\s*\/{1,2}/, "").replace(/^\s*\*+/, "").trim();
832
833
  }
@@ -888,7 +889,9 @@ var CALL_RE = /^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
888
889
  var ASSERTION_CODE_RE = /^(?:await\s+)?(?:expect(?:TypeOf)?|assert(?:\.\w+)?)\s*\(/;
889
890
  var HTTP_CONTRACT_RE = /\b(?:GET|HEAD|OPTIONS|PATCH|POST|PUT|DELETE)\s+(?:https?:\/\/|\/|\{[A-Za-z_$])/;
890
891
  var PROSE_ASSIGNMENT_RE = /^[A-Za-z_$][\w.$[\]]*\s*=\s*[A-Za-z][A-Za-z '-]{2,}\s+\((?:how|what|when|where|which|who|why)\b[^)]*\)[.!]?$/i;
891
- var PSEUDOCODE_RE = /%\w+%|\[opt\]|(?:^|\s)<[A-Za-z]\w*>|…|\.\.\./;
892
+ var PSEUDOCODE_RE = /%\w+%|\[opt\]|(?:^|\s)<[A-Za-z]\w*>|\b[A-Za-z_$][\w$]*\s+x\d+\b|…|\.\.\./;
893
+ var CALL_LABEL_RE = /^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\([^;{}]*\)$/;
894
+ var MULTIPLIER_PSEUDOCODE_RE = /\b[A-Za-z_$][\w$]*\s+x\d+\b/;
892
895
  function isBanner(text) {
893
896
  const t = text.trim();
894
897
  if (!t) return false;
@@ -900,6 +903,7 @@ function isRegionMarker(text) {
900
903
  if (match === null) return false;
901
904
  const title = (match[1] ?? "").trim();
902
905
  if (title.length === 0) return true;
906
+ if (REGION_PROSE_VERB_RE.test(title)) return false;
903
907
  if (!REGION_TITLE_RE.test(title)) return false;
904
908
  return title.split(/\s+/).length <= REGION_TITLE_MAX_WORDS;
905
909
  }
@@ -915,6 +919,15 @@ function looksLikeCode(text, allowCall = true) {
915
919
  function hasPseudocode(text) {
916
920
  return PSEUDOCODE_RE.test(text);
917
921
  }
922
+ function testCallMatrixStems(comments) {
923
+ const stems = /* @__PURE__ */ new Set();
924
+ for (const comment of comments) {
925
+ const body2 = stripCommentMarker(comment.value);
926
+ const stem3 = CALL_LABEL_RE.exec(body2)?.[1];
927
+ if (stem3 !== void 0 && MULTIPLIER_PSEUDOCODE_RE.test(body2)) stems.add(stem3);
928
+ }
929
+ return stems;
930
+ }
918
931
  function isEnumeratedProseItem(text) {
919
932
  const t = text.trim();
920
933
  return ENUMERATED_ITEM_RE.test(t) && t.split(/\s+/).length >= ENUMERATED_ITEM_MIN_WORDS;
@@ -1203,6 +1216,7 @@ var no_comment_cruft_default = createRule({
1203
1216
  return {
1204
1217
  Program() {
1205
1218
  const comments = sourceCode.getAllComments();
1219
+ const callMatrixStems = isTestFile(context.filename) ? testCallMatrixStems(comments) : /* @__PURE__ */ new Set();
1206
1220
  const walls = findCommentWalls(comments);
1207
1221
  const wallByLeader = new Map(walls.map((wall) => [wall.leader, wall]));
1208
1222
  const wallMembers = new Set(walls.flatMap((wall) => [...wall.members]));
@@ -1240,6 +1254,8 @@ var no_comment_cruft_default = createRule({
1240
1254
  continue;
1241
1255
  }
1242
1256
  const firstText = texts[0];
1257
+ const firstTextStem = firstText === void 0 ? void 0 : CALL_LABEL_RE.exec(firstText)?.[1];
1258
+ if (firstTextStem !== void 0 && texts.length === 1 && callMatrixStems.has(firstTextStem)) continue;
1243
1259
  if (firstText !== void 0 && /^(?:todo|fixme)\b/i.test(firstText)) {
1244
1260
  if (!runCitesAReference(comments, i)) {
1245
1261
  context.report({
@@ -2169,6 +2185,20 @@ var PURE_METHODS = /* @__PURE__ */ new Set([
2169
2185
  "startsWith",
2170
2186
  "endsWith"
2171
2187
  ]);
2188
+ var SYNC_CALLBACK_METHODS = /* @__PURE__ */ new Set([
2189
+ "every",
2190
+ "filter",
2191
+ "findIndex",
2192
+ "findLast",
2193
+ "findLastIndex",
2194
+ "flatMap",
2195
+ "forEach",
2196
+ "map",
2197
+ "reduce",
2198
+ "reduceRight",
2199
+ "some",
2200
+ "sort"
2201
+ ]);
2172
2202
  var PURE_NAMESPACES = /* @__PURE__ */ new Set([
2173
2203
  "Object",
2174
2204
  "Array",
@@ -2218,7 +2248,17 @@ function isPureCall(node) {
2218
2248
  if (callee.object.type === AST_NODE_TYPES10.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
2219
2249
  return !IMPURE_NAMESPACE_METHODS.has(`${callee.object.name}.${property.name}`);
2220
2250
  }
2221
- return PURE_METHODS.has(property.name);
2251
+ return PURE_METHODS.has(property.name) && (!SYNC_CALLBACK_METHODS.has(property.name) || !synchronousCallbackCanThrow(node));
2252
+ }
2253
+ function synchronousCallbackCanThrow(node) {
2254
+ return node.arguments.some((argument) => {
2255
+ if (argument.type !== AST_NODE_TYPES10.ArrowFunctionExpression && argument.type !== AST_NODE_TYPES10.FunctionExpression) return false;
2256
+ if (argument.async) return false;
2257
+ return subtreeMatches2(
2258
+ argument.body,
2259
+ (current) => current.type === AST_NODE_TYPES10.ThrowStatement || current.type === AST_NODE_TYPES10.CallExpression && current.callee.type === AST_NODE_TYPES10.MemberExpression && !current.callee.computed && current.callee.object.type === AST_NODE_TYPES10.Identifier && current.callee.object.name === "JSON" && current.callee.property.type === AST_NODE_TYPES10.Identifier && current.callee.property.name === "parse"
2260
+ );
2261
+ });
2222
2262
  }
2223
2263
  function isPureNew(node) {
2224
2264
  return node.callee.type === AST_NODE_TYPES10.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
@@ -2899,6 +2939,16 @@ var PAYLOAD_PROPS = /* @__PURE__ */ new Set([
2899
2939
  "metadata",
2900
2940
  "context"
2901
2941
  ]);
2942
+ var BUILTIN_ERROR_CONSTRUCTORS = /* @__PURE__ */ new Set([
2943
+ "AggregateError",
2944
+ "Error",
2945
+ "EvalError",
2946
+ "RangeError",
2947
+ "ReferenceError",
2948
+ "SyntaxError",
2949
+ "TypeError",
2950
+ "URIError"
2951
+ ]);
2902
2952
  function isCatchBinding(scope, name) {
2903
2953
  let current = scope;
2904
2954
  while (current) {
@@ -3013,6 +3063,48 @@ function nodeWithin(node, container) {
3013
3063
  function isJsonStringify(callee) {
3014
3064
  return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
3015
3065
  }
3066
+ function directLiteralValues(argument) {
3067
+ if (argument.type === "ObjectExpression") {
3068
+ return argument.properties.flatMap((property) => {
3069
+ if (property.type !== "Property" || property.computed) return [];
3070
+ const value = property.value;
3071
+ if (value.type === "AssignmentPattern" || value.type === "ArrayPattern" || value.type === "ObjectPattern" || value.type === "TSEmptyBodyFunctionExpression") {
3072
+ return [];
3073
+ }
3074
+ return [value];
3075
+ });
3076
+ }
3077
+ if (argument.type === "ArrayExpression") {
3078
+ return argument.elements.flatMap(
3079
+ (element) => element !== null && element.type !== "SpreadElement" ? [element] : []
3080
+ );
3081
+ }
3082
+ return argument.type === "SpreadElement" ? [] : [argument];
3083
+ }
3084
+ function expressionSuggestsError(expression, scope) {
3085
+ if (expression.type === "Identifier") {
3086
+ return ERROR_NAME_PATTERN.test(expression.name) || isCatchBinding(scope, expression.name);
3087
+ }
3088
+ return expression.type === "MemberExpression" && memberSuggestsError(expression, scope);
3089
+ }
3090
+ function nestedExpressionSuggestsError(expression, scope) {
3091
+ if (expression.type === "Identifier") {
3092
+ if (isCatchBinding(scope, expression.name)) return true;
3093
+ let current = scope;
3094
+ while (current !== null && !current.set.has(expression.name)) {
3095
+ current = current.upper;
3096
+ }
3097
+ const variable = current?.set.get(expression.name);
3098
+ if (variable === void 0 || variable.defs.length !== 1) return false;
3099
+ const definition = variable.defs[0];
3100
+ if (definition?.type !== "Variable") return false;
3101
+ const initializer = definition.node.init;
3102
+ return initializer?.type === "NewExpression" && initializer.callee.type === "Identifier" && BUILTIN_ERROR_CONSTRUCTORS.has(initializer.callee.name) && variable.references.every(
3103
+ (reference) => !reference.isWrite() || reference.init === true
3104
+ );
3105
+ }
3106
+ return expression.type === "MemberExpression" && memberSuggestsError(expression, scope);
3107
+ }
3016
3108
  var no_json_stringify_error_default = createRule({
3017
3109
  name: "no-json-stringify-error",
3018
3110
  meta: {
@@ -3037,18 +3129,11 @@ var no_json_stringify_error_default = createRule({
3037
3129
  return;
3038
3130
  }
3039
3131
  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)) {
3132
+ const isNestedLiteral = firstArg.type === "ObjectExpression" || firstArg.type === "ArrayExpression";
3133
+ const unsafeValue = directLiteralValues(firstArg).find(
3134
+ (value) => (isNestedLiteral ? nestedExpressionSuggestsError(value, scope) : expressionSuggestsError(value, scope)) && !isGuardedByInstanceofError(node, value, context.sourceCode) && !isNarrowedByEarlyReturn(node, value, context.sourceCode)
3135
+ );
3136
+ if (unsafeValue === void 0) {
3052
3137
  return;
3053
3138
  }
3054
3139
  context.report({
@@ -3435,6 +3520,8 @@ function typedFunction(node) {
3435
3520
 
3436
3521
  // src/rules/no-long-comment.ts
3437
3522
  var EXCESSIVE_SENTENCE_COUNT = 8;
3523
+ var EXCESSIVE_WORD_COUNT = 120;
3524
+ var PROSE_WORD_RE = /[\p{L}\p{N}][\p{L}\p{N}'’-]*/gu;
3438
3525
  var VERSIONED_DEPENDENCY_TREE_RE = /(?:^|\/)lib\/[^/]*-?v?\d+\.\d+(?:\.\d+)?[^/]*\//iu;
3439
3526
  function documentsTypeOrMember(sourceCode, comment) {
3440
3527
  const token = sourceCode.getTokenAfter(comment, { includeComments: false });
@@ -3446,6 +3533,9 @@ function documentsTypeOrMember(sourceCode, comment) {
3446
3533
  }
3447
3534
  return false;
3448
3535
  }
3536
+ function wordUnits(text) {
3537
+ return text.match(PROSE_WORD_RE)?.length ?? 0;
3538
+ }
3449
3539
  var no_long_comment_default = createRule({
3450
3540
  name: "no-long-comment",
3451
3541
  meta: {
@@ -3463,7 +3553,7 @@ var no_long_comment_default = createRule({
3463
3553
  return {
3464
3554
  Program() {
3465
3555
  for (const group of proseGroups(context.filename, context.sourceCode)) {
3466
- if (group.comment.type !== "Block" || !group.comment.value.startsWith("*") || group.hasTypedTags || hasDocumentationStructure(group.text) || hasTechnicalAnchor(group.text) || documentsTypedFunction(context.sourceCode, group.comment) || documentsTypeOrMember(context.sourceCode, group.comment) || sentenceUnits(group.text) < EXCESSIVE_SENTENCE_COUNT) continue;
3556
+ if (group.comment.type !== "Block" || !group.comment.value.startsWith("*") || group.hasTypedTags || hasDocumentationStructure(group.text) || hasTechnicalAnchor(group.text) || documentsTypedFunction(context.sourceCode, group.comment) || documentsTypeOrMember(context.sourceCode, group.comment) || sentenceUnits(group.text) < EXCESSIVE_SENTENCE_COUNT && wordUnits(group.text) < EXCESSIVE_WORD_COUNT) continue;
3467
3557
  context.report({ node: group.comment, messageId: "tooLong" });
3468
3558
  }
3469
3559
  }
@@ -5118,50 +5208,59 @@ var no_select_star_default = createRule({
5118
5208
 
5119
5209
  // src/rules/no-sentinel-return-on-catch.ts
5120
5210
  import { AST_NODE_TYPES as AST_NODE_TYPES23 } from "@typescript-eslint/utils";
5211
+ function unwrapSentinelExpression(arg) {
5212
+ let current = arg;
5213
+ while (current?.type === AST_NODE_TYPES23.TSAsExpression || current?.type === AST_NODE_TYPES23.TSTypeAssertion || current?.type === AST_NODE_TYPES23.TSSatisfiesExpression) {
5214
+ current = current.expression;
5215
+ }
5216
+ return current;
5217
+ }
5121
5218
  function sentinelKind(arg) {
5122
- if (arg === null) {
5219
+ const value = unwrapSentinelExpression(arg);
5220
+ if (value === null) {
5123
5221
  return null;
5124
5222
  }
5125
- if (arg.type === AST_NODE_TYPES23.Literal) {
5126
- if (arg.value === null) {
5223
+ if (value.type === AST_NODE_TYPES23.Literal) {
5224
+ if (value.value === null) {
5127
5225
  return "nullish";
5128
5226
  }
5129
- if (typeof arg.value === "boolean") {
5227
+ if (typeof value.value === "boolean") {
5130
5228
  return "boolean";
5131
5229
  }
5132
- if (typeof arg.value === "string") {
5230
+ if (typeof value.value === "string") {
5133
5231
  return "string";
5134
5232
  }
5135
5233
  return null;
5136
5234
  }
5137
- if (arg.type === AST_NODE_TYPES23.Identifier && arg.name === "undefined") {
5235
+ if (value.type === AST_NODE_TYPES23.Identifier && value.name === "undefined") {
5138
5236
  return "nullish";
5139
5237
  }
5140
- if (arg.type === AST_NODE_TYPES23.ArrayExpression) {
5238
+ if (value.type === AST_NODE_TYPES23.ArrayExpression) {
5141
5239
  return "array";
5142
5240
  }
5143
- if (arg.type === AST_NODE_TYPES23.ObjectExpression) {
5241
+ if (value.type === AST_NODE_TYPES23.ObjectExpression) {
5144
5242
  return "object";
5145
5243
  }
5146
5244
  return null;
5147
5245
  }
5148
5246
  function isSentinelArgument(arg) {
5149
- if (arg === null) {
5247
+ const value = unwrapSentinelExpression(arg);
5248
+ if (value === null) {
5150
5249
  return false;
5151
5250
  }
5152
- if (arg.type === AST_NODE_TYPES23.Literal && arg.value === null) {
5251
+ if (value.type === AST_NODE_TYPES23.Literal && value.value === null) {
5153
5252
  return true;
5154
5253
  }
5155
- if (arg.type === AST_NODE_TYPES23.Literal && arg.value === false) {
5254
+ if (value.type === AST_NODE_TYPES23.Literal && value.value === false) {
5156
5255
  return true;
5157
5256
  }
5158
- if (arg.type === AST_NODE_TYPES23.Identifier && arg.name === "undefined") {
5257
+ if (value.type === AST_NODE_TYPES23.Identifier && value.name === "undefined") {
5159
5258
  return true;
5160
5259
  }
5161
- if (arg.type === AST_NODE_TYPES23.ArrayExpression && arg.elements.length === 0) {
5260
+ if (value.type === AST_NODE_TYPES23.ArrayExpression && value.elements.length === 0) {
5162
5261
  return true;
5163
5262
  }
5164
- if (arg.type === AST_NODE_TYPES23.ObjectExpression && arg.properties.length === 0) {
5263
+ if (value.type === AST_NODE_TYPES23.ObjectExpression && value.properties.length === 0) {
5165
5264
  return true;
5166
5265
  }
5167
5266
  return false;
@@ -5359,6 +5458,16 @@ function tryReturnsSafeParse(catchNode) {
5359
5458
  const only = tryBlock.body.length === 1 ? tryBlock.body[0] : void 0;
5360
5459
  return only !== void 0 && returnsMatching(only, isBodyDecodeNode);
5361
5460
  }
5461
+ function isIntentionalStackCapture(catchNode, caughtName) {
5462
+ if (caughtName === null) return false;
5463
+ const tryBody = tryBlockOf(catchNode).body;
5464
+ const only = tryBody.length === 1 ? tryBody[0] : void 0;
5465
+ if (only?.type !== AST_NODE_TYPES23.ThrowStatement) return false;
5466
+ const thrown = unwrapSentinelExpression(only.argument);
5467
+ 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";
5468
+ if (!constructsError) return false;
5469
+ return catchNode.body.body.slice(0, -1).some((statement) => subtreeReadsName(statement, caughtName));
5470
+ }
5362
5471
  function tryBlockOf(catchNode) {
5363
5472
  return catchNode.parent.block;
5364
5473
  }
@@ -5479,6 +5588,9 @@ var no_sentinel_return_on_catch_default = createRule({
5479
5588
  if (tryReturnsSafeParse(node)) {
5480
5589
  return;
5481
5590
  }
5591
+ if (isIntentionalStackCapture(node, caughtName)) {
5592
+ return;
5593
+ }
5482
5594
  const kind = sentinelKind(last.argument);
5483
5595
  if (kind !== null && isNamedBooleanPredicate(node, kind)) {
5484
5596
  return;
@@ -5557,11 +5669,11 @@ var no_silent_promise_catch_default = createRule({
5557
5669
  meta: {
5558
5670
  type: "problem",
5559
5671
  docs: {
5560
- description: "Disallow `.catch()` handlers that silently swallow the rejection (e.g. `.catch(() => null)`); log, rethrow, or handle the error."
5672
+ description: "Disallow `.catch()` and second-argument `.then()` handlers that silently swallow a rejection; log, rethrow, or handle the error."
5561
5673
  },
5562
5674
  schema: [],
5563
5675
  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."
5676
+ 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
5677
  }
5566
5678
  },
5567
5679
  defaultOptions: [],
@@ -5587,9 +5699,12 @@ var no_silent_promise_catch_default = createRule({
5587
5699
  };
5588
5700
  return {
5589
5701
  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") {
5702
+ if (node.callee.type !== AST_NODE_TYPES24.MemberExpression || node.callee.computed || node.callee.property.type !== AST_NODE_TYPES24.Identifier) {
5591
5703
  return;
5592
5704
  }
5705
+ const method = node.callee.property.name;
5706
+ const handlerIndex = method === "catch" ? 0 : method === "then" ? 1 : null;
5707
+ if (handlerIndex === null) return;
5593
5708
  if (isBodyParseCall(node.callee.object)) {
5594
5709
  return;
5595
5710
  }
@@ -5599,10 +5714,11 @@ var no_silent_promise_catch_default = createRule({
5599
5714
  if (node.parent.type === AST_NODE_TYPES24.MemberExpression && node.parent.object === node) {
5600
5715
  return;
5601
5716
  }
5602
- if (node.arguments.length !== 1) {
5717
+ const expectedArguments = method === "catch" ? 1 : 2;
5718
+ if (node.arguments.length !== expectedArguments) {
5603
5719
  return;
5604
5720
  }
5605
- const handler = node.arguments[0];
5721
+ const handler = node.arguments[handlerIndex];
5606
5722
  if (handler === void 0 || handler.type !== AST_NODE_TYPES24.ArrowFunctionExpression && handler.type !== AST_NODE_TYPES24.FunctionExpression) {
5607
5723
  return;
5608
5724
  }
@@ -5865,6 +5981,11 @@ function isStringLiteralInit(node) {
5865
5981
  return false;
5866
5982
  }
5867
5983
  function isConcatOntoTarget(rhs, target) {
5984
+ if (rhs.type === "TemplateLiteral") {
5985
+ return rhs.expressions.some(
5986
+ (expression) => expression.type === "Identifier" && expression.name === target
5987
+ );
5988
+ }
5868
5989
  if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
5869
5990
  return false;
5870
5991
  }
@@ -7420,6 +7541,15 @@ var SUCCESS_PAYLOAD_MEMBER_NAMES = /* @__PURE__ */ new Set([
7420
7541
  "value"
7421
7542
  ]);
7422
7543
  var REQUIRED_STATUS_MEMBER_COUNT = 1;
7544
+ var FUNCTION_RETURN_OWNER_TYPES = /* @__PURE__ */ new Set([
7545
+ AST_NODE_TYPES37.ArrowFunctionExpression,
7546
+ AST_NODE_TYPES37.FunctionDeclaration,
7547
+ AST_NODE_TYPES37.FunctionExpression,
7548
+ AST_NODE_TYPES37.TSDeclareFunction,
7549
+ AST_NODE_TYPES37.TSEmptyBodyFunctionExpression,
7550
+ AST_NODE_TYPES37.TSFunctionType,
7551
+ AST_NODE_TYPES37.TSMethodSignature
7552
+ ]);
7423
7553
  function looksLikeMutuallyExclusiveState(typeLiteral) {
7424
7554
  let statusMemberCount = 0;
7425
7555
  let hasFailurePayload = false;
@@ -7460,6 +7590,17 @@ function getMemberName(member) {
7460
7590
  function isBooleanTyped(member) {
7461
7591
  return member.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES37.TSBooleanKeyword;
7462
7592
  }
7593
+ function inlineReturnTypeLiteral(node) {
7594
+ let annotation = null;
7595
+ if (node.parent.type === AST_NODE_TYPES37.TSTypeAnnotation) {
7596
+ annotation = node.parent;
7597
+ } 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) {
7598
+ annotation = node.parent.parent.parent;
7599
+ }
7600
+ if (annotation === null) return null;
7601
+ const owner = annotation.parent;
7602
+ return FUNCTION_RETURN_OWNER_TYPES.has(owner.type) && "returnType" in owner && owner.returnType === annotation ? annotation : null;
7603
+ }
7463
7604
  var prefer_discriminated_union_default = createRule({
7464
7605
  name: "prefer-discriminated-union",
7465
7606
  meta: {
@@ -7499,6 +7640,10 @@ var prefer_discriminated_union_default = createRule({
7499
7640
  },
7500
7641
  "TSTypeAliasDeclaration > TSTypeLiteral"(node) {
7501
7642
  checkTypeLiteral(node, node.parent);
7643
+ },
7644
+ TSTypeLiteral(node) {
7645
+ const annotation = inlineReturnTypeLiteral(node);
7646
+ if (annotation !== null) checkTypeLiteral(node, annotation);
7502
7647
  }
7503
7648
  };
7504
7649
  }
@@ -8415,6 +8560,11 @@ var prefer_module_level_schema_default = createRule({
8415
8560
  type: "boolean",
8416
8561
  description: "Skip test files, where a fixture schema belongs next to its assertion."
8417
8562
  },
8563
+ memoCallees: {
8564
+ type: "array",
8565
+ items: { type: "string" },
8566
+ description: "Wrappers that construct their callback at most once. Defaults to lazy, memo, once, and useMemo."
8567
+ },
8418
8568
  minProperties: {
8419
8569
  type: "number",
8420
8570
  minimum: 0,
@@ -8432,6 +8582,7 @@ var prefer_module_level_schema_default = createRule({
8432
8582
  create(context, [options]) {
8433
8583
  const factories = new Set(options?.factories ?? DEFAULT_FACTORIES);
8434
8584
  const ignoreTestFiles = options?.ignoreTestFiles ?? true;
8585
+ const memoCallees = new Set(options?.memoCallees ?? MEMO_CALLEES);
8435
8586
  const minProperties = options?.minProperties ?? DEFAULT_MIN_PROPERTIES;
8436
8587
  const sourceCode = context.sourceCode;
8437
8588
  const filename = context.filename;
@@ -8451,7 +8602,7 @@ var prefer_module_level_schema_default = createRule({
8451
8602
  if (current !== node && isZodCall(current) && current.callee.type === AST_NODE_TYPES42.MemberExpression && current.callee.property.type === AST_NODE_TYPES42.Identifier && factories.has(current.callee.property.name)) {
8452
8603
  return true;
8453
8604
  }
8454
- if (current.type === AST_NODE_TYPES42.CallExpression && (current.callee.type === AST_NODE_TYPES42.Identifier && MEMO_CALLEES.has(current.callee.name) || current.callee.type === AST_NODE_TYPES42.MemberExpression && !current.callee.computed && current.callee.property.type === AST_NODE_TYPES42.Identifier && MEMO_CALLEES.has(current.callee.property.name))) {
8605
+ if (current.type === AST_NODE_TYPES42.CallExpression && (current.callee.type === AST_NODE_TYPES42.Identifier && memoCallees.has(current.callee.name) || current.callee.type === AST_NODE_TYPES42.MemberExpression && !current.callee.computed && current.callee.property.type === AST_NODE_TYPES42.Identifier && memoCallees.has(current.callee.property.name))) {
8455
8606
  return true;
8456
8607
  }
8457
8608
  current = current.parent ?? void 0;
@@ -8501,6 +8652,9 @@ var prefer_module_level_schema_default = createRule({
8501
8652
  if (definition.type === "ImportBinding") {
8502
8653
  continue;
8503
8654
  }
8655
+ if (definition.node.type === AST_NODE_TYPES42.VariableDeclarator && definition.node.parent.type === AST_NODE_TYPES42.VariableDeclaration && definition.node.parent.kind !== "const") {
8656
+ return false;
8657
+ }
8504
8658
  const [defStart, defEnd] = definition.node.range;
8505
8659
  if (defStart >= schemaStart && defEnd <= schemaEnd) {
8506
8660
  continue;
@@ -8735,7 +8889,7 @@ function sameAccess(node, access) {
8735
8889
  return node.type === AST_NODE_TYPES44.MemberExpression && !node.computed && node.object.type === AST_NODE_TYPES44.Identifier && node.object.name === access.object && node.property.type === AST_NODE_TYPES44.Identifier && node.property.name === access.property;
8736
8890
  }
8737
8891
  function isNullGuard(node, access) {
8738
- if (node.type === AST_NODE_TYPES44.UnaryExpression && node.operator === "!" && sameAccess(node.argument, access)) return true;
8892
+ if (node.type === AST_NODE_TYPES44.UnaryExpression && node.operator === "!" && (sameAccess(node.argument, access) || optionalMemberLengthOf(node.argument, access))) return true;
8739
8893
  if (node.type !== AST_NODE_TYPES44.BinaryExpression || !["==", "==="].includes(node.operator)) {
8740
8894
  return false;
8741
8895
  }
@@ -8751,7 +8905,11 @@ function isEmptyGuard(node, access) {
8751
8905
  return memberLengthOf(node.left, access) && zero(node.right) || memberLengthOf(node.right, access) && zero(node.left);
8752
8906
  }
8753
8907
  function memberLengthOf(node, access) {
8754
- return node.type === AST_NODE_TYPES44.MemberExpression && !node.computed && node.property.type === AST_NODE_TYPES44.Identifier && node.property.name === "length" && sameAccess(node.object, access);
8908
+ const target = node.type === AST_NODE_TYPES44.ChainExpression ? node.expression : node;
8909
+ return target.type === AST_NODE_TYPES44.MemberExpression && !target.computed && target.property.type === AST_NODE_TYPES44.Identifier && target.property.name === "length" && sameAccess(target.object, access);
8910
+ }
8911
+ function optionalMemberLengthOf(node, access) {
8912
+ return node.type === AST_NODE_TYPES44.ChainExpression && node.expression.type === AST_NODE_TYPES44.MemberExpression && node.expression.optional && memberLengthOf(node, access);
8755
8913
  }
8756
8914
  function hasEquivalentLeadingGuard(fn, access, visitorKeys) {
8757
8915
  if (fn.body.type !== AST_NODE_TYPES44.BlockStatement) return false;
@@ -8759,6 +8917,7 @@ function hasEquivalentLeadingGuard(fn, access, visitorKeys) {
8759
8917
  if (first?.type !== AST_NODE_TYPES44.IfStatement) return false;
8760
8918
  const terminating = first.consequent.type === AST_NODE_TYPES44.ReturnStatement || first.consequent.type === AST_NODE_TYPES44.ThrowStatement || first.consequent.type === AST_NODE_TYPES44.BlockStatement && first.consequent.body.length === 1 && (first.consequent.body[0]?.type === AST_NODE_TYPES44.ReturnStatement || first.consequent.body[0]?.type === AST_NODE_TYPES44.ThrowStatement);
8761
8919
  if (!terminating) return false;
8920
+ if (contains(first.consequent, visitorKeys, (node) => sameAccess(node, access))) return false;
8762
8921
  return contains(first.test, visitorKeys, (node) => isNullGuard(node, access)) && contains(first.test, visitorKeys, (node) => isEmptyGuard(node, access));
8763
8922
  }
8764
8923
  function contains(node, visitorKeys, predicate) {
@@ -8921,7 +9080,7 @@ var isSchemaParseReference = (node) => {
8921
9080
  const inner = unwrap4(node);
8922
9081
  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
9082
  };
8924
- var isRawPayloadSource = (node) => {
9083
+ var isRawPayloadSource = (node, isKnownLocalText) => {
8925
9084
  let current = unwrap4(node);
8926
9085
  if (current === null) return false;
8927
9086
  if (current.type === AST_NODE_TYPES45.AwaitExpression) {
@@ -8945,20 +9104,26 @@ var isRawPayloadSource = (node) => {
8945
9104
  return !current.arguments.some(isSchemaParseReference) && isRawPayloadSource(callee.object);
8946
9105
  }
8947
9106
  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]);
9107
+ 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
9108
  };
8950
9109
  var FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;
9110
+ var isDirectLocalFileRead = (node) => {
9111
+ let current = unwrap4(node);
9112
+ if (current?.type === AST_NODE_TYPES45.AwaitExpression) {
9113
+ current = unwrap4(current.argument);
9114
+ }
9115
+ if (current?.type !== AST_NODE_TYPES45.CallExpression) return false;
9116
+ const callee = unwrap4(current.callee);
9117
+ 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;
9118
+ return name !== null && FILE_READ_RE.test(name);
9119
+ };
8951
9120
  var isLocalFileRead = (node) => {
8952
9121
  let found = false;
8953
9122
  const visit = (current) => {
8954
9123
  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
- }
9124
+ if (isDirectLocalFileRead(current)) {
9125
+ found = true;
9126
+ return;
8962
9127
  }
8963
9128
  for (const key of Object.keys(current)) {
8964
9129
  if (key === "parent") continue;
@@ -9020,6 +9185,87 @@ var isValidationRead = (node) => {
9020
9185
  }
9021
9186
  return callee.type === AST_NODE_TYPES45.Identifier && GUARD_NAME_RE.test(callee.name);
9022
9187
  };
9188
+ var PRIMITIVE_TYPEOF_RESULTS = /* @__PURE__ */ new Set([
9189
+ "bigint",
9190
+ "boolean",
9191
+ "number",
9192
+ "string",
9193
+ "symbol",
9194
+ "undefined"
9195
+ ]);
9196
+ var bindingValidationPolarity = (test, bindingName) => {
9197
+ if (test.type === AST_NODE_TYPES45.UnaryExpression && test.operator === "!") {
9198
+ const inner = bindingValidationPolarity(test.argument, bindingName);
9199
+ return inner === "valid-when-true" ? "valid-when-false" : inner === "valid-when-false" ? "valid-when-true" : null;
9200
+ }
9201
+ if (test.type === AST_NODE_TYPES45.BinaryExpression) {
9202
+ const typeofName = (node) => node.type === AST_NODE_TYPES45.UnaryExpression && node.operator === "typeof" && node.argument.type === AST_NODE_TYPES45.Identifier ? node.argument.name : null;
9203
+ const literalType = (node) => node.type === AST_NODE_TYPES45.Literal && typeof node.value === "string" && PRIMITIVE_TYPEOF_RESULTS.has(node.value) ? node.value : null;
9204
+ const matches = typeofName(test.left) === bindingName && literalType(test.right) !== null || typeofName(test.right) === bindingName && literalType(test.left) !== null;
9205
+ if (!matches) return null;
9206
+ if (test.operator === "===" || test.operator === "==") {
9207
+ return "valid-when-true";
9208
+ }
9209
+ return test.operator === "!==" || test.operator === "!=" ? "valid-when-false" : null;
9210
+ }
9211
+ 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;
9212
+ };
9213
+ var nodeWithin2 = (node, container) => node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
9214
+ var isFullyValidatedExtractedBinding = (member, source, context) => {
9215
+ const isValidationReference = (identifier) => {
9216
+ for (let current = identifier.parent; current !== void 0 && current !== null; current = current.parent) {
9217
+ if ((current.type === AST_NODE_TYPES45.BinaryExpression || current.type === AST_NODE_TYPES45.CallExpression || current.type === AST_NODE_TYPES45.UnaryExpression) && bindingValidationPolarity(current, identifier.name) !== null) {
9218
+ return true;
9219
+ }
9220
+ if (current.type !== AST_NODE_TYPES45.UnaryExpression && current.type !== AST_NODE_TYPES45.MemberExpression && current.type !== AST_NODE_TYPES45.CallExpression) {
9221
+ return false;
9222
+ }
9223
+ }
9224
+ return false;
9225
+ };
9226
+ const isGuardedUse = (identifier) => {
9227
+ for (let current = identifier.parent; current !== void 0 && current !== null; current = current.parent) {
9228
+ if (current.type === AST_NODE_TYPES45.ConditionalExpression) {
9229
+ const polarity = bindingValidationPolarity(current.test, identifier.name);
9230
+ if (polarity === "valid-when-true" && nodeWithin2(identifier, current.consequent)) {
9231
+ return true;
9232
+ }
9233
+ if (polarity === "valid-when-false" && nodeWithin2(identifier, current.alternate)) {
9234
+ return true;
9235
+ }
9236
+ }
9237
+ if (current.type === AST_NODE_TYPES45.IfStatement) {
9238
+ const polarity = bindingValidationPolarity(current.test, identifier.name);
9239
+ if (polarity === "valid-when-true" && nodeWithin2(identifier, current.consequent)) {
9240
+ return true;
9241
+ }
9242
+ if (polarity === "valid-when-false" && current.alternate !== null && nodeWithin2(identifier, current.alternate)) {
9243
+ return true;
9244
+ }
9245
+ }
9246
+ if (current.type === AST_NODE_TYPES45.FunctionDeclaration || current.type === AST_NODE_TYPES45.FunctionExpression || current.type === AST_NODE_TYPES45.ArrowFunctionExpression) {
9247
+ return false;
9248
+ }
9249
+ }
9250
+ return false;
9251
+ };
9252
+ const declarator = member.parent;
9253
+ 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") {
9254
+ return false;
9255
+ }
9256
+ const extracted = context.sourceCode.getDeclaredVariables(declarator)[0];
9257
+ if (extracted === void 0 || extracted.scope !== source.scope) return false;
9258
+ let hasValueUse = false;
9259
+ for (const reference of extracted.references) {
9260
+ const identifier = reference.identifier;
9261
+ if (identifier.type !== AST_NODE_TYPES45.Identifier) return false;
9262
+ if (nodeWithin2(identifier, declarator)) continue;
9263
+ if (isValidationReference(identifier)) continue;
9264
+ hasValueUse = true;
9265
+ if (!isGuardedUse(identifier)) return false;
9266
+ }
9267
+ return hasValueUse;
9268
+ };
9023
9269
  var isGuardTestPosition = (node) => {
9024
9270
  let current = node;
9025
9271
  let parent = current.parent;
@@ -9043,13 +9289,13 @@ var isGuardTestPosition = (node) => {
9043
9289
  }
9044
9290
  return false;
9045
9291
  };
9046
- var isUnvalidatedVariableRef = (node, scope, tracked) => {
9292
+ var unvalidatedVariableRef = (node, scope, tracked) => {
9047
9293
  const unwrapped = unwrap4(node);
9048
9294
  if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES45.Identifier) {
9049
- return false;
9295
+ return null;
9050
9296
  }
9051
9297
  const variable = findVariable2(scope, unwrapped.name);
9052
- return variable !== null && tracked.has(variable);
9298
+ return variable !== null && tracked.has(variable) ? variable : null;
9053
9299
  };
9054
9300
  var prefer_schema_for_api_payload_default = createRule({
9055
9301
  name: "prefer-schema-for-api-payload",
@@ -9069,6 +9315,56 @@ var prefer_schema_for_api_payload_default = createRule({
9069
9315
  return {};
9070
9316
  }
9071
9317
  const unvalidatedVariables = /* @__PURE__ */ new Set();
9318
+ const aliasGroups = /* @__PURE__ */ new Map();
9319
+ const localFileTextVariables = /* @__PURE__ */ new Set();
9320
+ const localFileTextRef = (node, scope) => {
9321
+ const unwrapped = unwrap4(node);
9322
+ if (unwrapped?.type !== AST_NODE_TYPES45.Identifier) return null;
9323
+ const variable = findVariable2(scope, unwrapped.name);
9324
+ return variable !== null && localFileTextVariables.has(variable) ? variable : null;
9325
+ };
9326
+ const updateLocalFileText = (target, value, scope) => {
9327
+ const source = localFileTextRef(value, scope);
9328
+ if (isDirectLocalFileRead(value) || source !== null && source.scope === target.scope) {
9329
+ localFileTextVariables.add(target);
9330
+ } else {
9331
+ localFileTextVariables.delete(target);
9332
+ }
9333
+ };
9334
+ const clearBinding = (variable) => {
9335
+ unvalidatedVariables.delete(variable);
9336
+ const group = aliasGroups.get(variable);
9337
+ aliasGroups.delete(variable);
9338
+ group?.delete(variable);
9339
+ };
9340
+ const clearAliasGroup = (variable) => {
9341
+ const group = aliasGroups.get(variable);
9342
+ if (group === void 0) {
9343
+ unvalidatedVariables.delete(variable);
9344
+ return;
9345
+ }
9346
+ for (const alias of group) {
9347
+ unvalidatedVariables.delete(alias);
9348
+ aliasGroups.delete(alias);
9349
+ }
9350
+ group.clear();
9351
+ };
9352
+ const trackRawBinding = (variable) => {
9353
+ clearBinding(variable);
9354
+ const group = /* @__PURE__ */ new Set([variable]);
9355
+ unvalidatedVariables.add(variable);
9356
+ aliasGroups.set(variable, group);
9357
+ };
9358
+ const trackAlias = (target, source) => {
9359
+ if (target === source) return;
9360
+ const group = aliasGroups.get(source) ?? /* @__PURE__ */ new Set([source]);
9361
+ if (aliasGroups.get(target) === group) return;
9362
+ clearBinding(target);
9363
+ unvalidatedVariables.add(target);
9364
+ group.add(target);
9365
+ aliasGroups.set(source, group);
9366
+ aliasGroups.set(target, group);
9367
+ };
9072
9368
  const isFullyNarrowedPattern = (declarator) => {
9073
9369
  const declared = context.sourceCode.getDeclaredVariables(declarator);
9074
9370
  return declared.length > 0 && declared.every(
@@ -9077,29 +9373,40 @@ var prefer_schema_for_api_payload_default = createRule({
9077
9373
  )
9078
9374
  );
9079
9375
  };
9080
- const trackInitializer = (declarator) => {
9081
- if (!isRawPayloadSource(declarator.init)) return;
9376
+ const trackInitializer = (declarator, scope) => {
9082
9377
  const declaredVars = context.sourceCode.getDeclaredVariables(declarator);
9083
9378
  const variable = declaredVars[0];
9084
- if (variable !== void 0) {
9085
- unvalidatedVariables.add(variable);
9379
+ if (variable === void 0) return;
9380
+ const localText = (candidate) => localFileTextRef(candidate, scope) !== null;
9381
+ if (isRawPayloadSource(declarator.init, localText)) {
9382
+ trackRawBinding(variable);
9383
+ return;
9086
9384
  }
9385
+ const source = unvalidatedVariableRef(declarator.init, scope, unvalidatedVariables);
9386
+ if (source !== null) trackAlias(variable, source);
9087
9387
  };
9088
9388
  return {
9089
9389
  VariableDeclarator(node) {
9090
9390
  const scope = context.sourceCode.getScope(node);
9091
9391
  if (node.id.type === AST_NODE_TYPES45.Identifier) {
9092
- trackInitializer(node);
9392
+ const variable = context.sourceCode.getDeclaredVariables(node)[0];
9393
+ if (variable !== void 0) {
9394
+ updateLocalFileText(variable, node.init, scope);
9395
+ }
9396
+ trackInitializer(node, scope);
9093
9397
  return;
9094
9398
  }
9095
9399
  if (node.id.type === AST_NODE_TYPES45.ObjectPattern || node.id.type === AST_NODE_TYPES45.ArrayPattern) {
9096
- if (isRawPayloadSource(node.init)) {
9400
+ if (isRawPayloadSource(
9401
+ node.init,
9402
+ (candidate) => localFileTextRef(candidate, scope) !== null
9403
+ )) {
9097
9404
  if (!isFullyNarrowedPattern(node)) {
9098
9405
  context.report({ node: node.id, messageId: "unparsedJsonAccess" });
9099
9406
  }
9100
9407
  return;
9101
9408
  }
9102
- if (isUnvalidatedVariableRef(node.init, scope, unvalidatedVariables)) {
9409
+ if (unvalidatedVariableRef(node.init, scope, unvalidatedVariables) !== null) {
9103
9410
  context.report({ node: node.id, messageId: "unparsedJsonAccess" });
9104
9411
  }
9105
9412
  }
@@ -9109,22 +9416,29 @@ var prefer_schema_for_api_payload_default = createRule({
9109
9416
  if (node.left.type === AST_NODE_TYPES45.Identifier) {
9110
9417
  const variable = findVariable2(scope, node.left.name);
9111
9418
  if (variable === null) return;
9112
- if (isRawPayloadSource(node.right)) {
9113
- unvalidatedVariables.add(variable);
9419
+ const isLocalText = (candidate) => localFileTextRef(candidate, scope) !== null;
9420
+ updateLocalFileText(variable, node.right, scope);
9421
+ if (isRawPayloadSource(node.right, isLocalText)) {
9422
+ trackRawBinding(variable);
9114
9423
  } else {
9115
- unvalidatedVariables.delete(variable);
9424
+ const source = unvalidatedVariableRef(node.right, scope, unvalidatedVariables);
9425
+ if (source === null) clearBinding(variable);
9426
+ else trackAlias(variable, source);
9116
9427
  }
9117
9428
  return;
9118
9429
  }
9119
9430
  if (node.left.type === AST_NODE_TYPES45.ObjectPattern || node.left.type === AST_NODE_TYPES45.ArrayPattern) {
9120
- if (isRawPayloadSource(node.right)) {
9431
+ if (isRawPayloadSource(
9432
+ node.right,
9433
+ (candidate) => localFileTextRef(candidate, scope) !== null
9434
+ )) {
9121
9435
  context.report({
9122
9436
  node: node.left,
9123
9437
  messageId: "unparsedJsonAccess"
9124
9438
  });
9125
9439
  return;
9126
9440
  }
9127
- if (isUnvalidatedVariableRef(node.right, scope, unvalidatedVariables)) {
9441
+ if (unvalidatedVariableRef(node.right, scope, unvalidatedVariables) !== null) {
9128
9442
  context.report({
9129
9443
  node: node.left,
9130
9444
  messageId: "unparsedJsonAccess"
@@ -9145,7 +9459,7 @@ var prefer_schema_for_api_payload_default = createRule({
9145
9459
  continue;
9146
9460
  }
9147
9461
  const variable = findVariable2(scope, unwrapped.name);
9148
- if (variable !== null) unvalidatedVariables.delete(variable);
9462
+ if (variable !== null) clearAliasGroup(variable);
9149
9463
  }
9150
9464
  },
9151
9465
  MemberExpression(node) {
@@ -9153,7 +9467,10 @@ var prefer_schema_for_api_payload_default = createRule({
9153
9467
  if (isValidationRead(node)) return;
9154
9468
  const scope = context.sourceCode.getScope(node);
9155
9469
  const obj = unwrap4(node.object);
9156
- if (isRawPayloadSource(obj)) {
9470
+ if (isRawPayloadSource(
9471
+ obj,
9472
+ (candidate) => localFileTextRef(candidate, scope) !== null
9473
+ )) {
9157
9474
  const parent = node.parent;
9158
9475
  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
9476
  return;
@@ -9161,12 +9478,13 @@ var prefer_schema_for_api_payload_default = createRule({
9161
9478
  context.report({ node, messageId: "unparsedJsonAccess" });
9162
9479
  return;
9163
9480
  }
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);
9481
+ const variable = obj?.type === AST_NODE_TYPES45.Identifier ? unvalidatedVariableRef(obj, scope, unvalidatedVariables) : null;
9482
+ if (variable !== null) {
9483
+ if (isFullyValidatedExtractedBinding(node, variable, context)) {
9484
+ return;
9169
9485
  }
9486
+ context.report({ node, messageId: "unparsedJsonAccess" });
9487
+ clearAliasGroup(variable);
9170
9488
  }
9171
9489
  }
9172
9490
  };
@@ -10933,8 +11251,8 @@ function initProvablyLacksSignal(init) {
10933
11251
  }
10934
11252
  return true;
10935
11253
  }
10936
- function isStringish(node) {
10937
- return node.type === AST_NODE_TYPES52.Literal && typeof node.value === "string" || node.type === AST_NODE_TYPES52.TemplateLiteral;
11254
+ function isInlineUrl(node, resolvesToGlobal) {
11255
+ 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
11256
  }
10939
11257
  var require_fetch_timeout_default = createRule({
10940
11258
  name: "require-fetch-timeout",
@@ -10986,7 +11304,7 @@ var require_fetch_timeout_default = createRule({
10986
11304
  return;
10987
11305
  }
10988
11306
  const [first, init] = node.arguments;
10989
- if (node.arguments.length === 1 && first !== void 0 && !isStringish(first)) {
11307
+ if (node.arguments.length === 1 && first !== void 0 && !isInlineUrl(first, resolvesToGlobal)) {
10990
11308
  return;
10991
11309
  }
10992
11310
  if (init === void 0 || initProvablyLacksSignal(init)) {
@@ -11003,7 +11321,7 @@ var CONFIGISH_TYPE_RE = /(?:Options|Opts|Config|Configuration|Settings|Params|Pr
11003
11321
  var CONFIGISH_NAME_RE = /^(?:options|opts|config|configuration|settings|params|props|args|env|environment|callbacks|flags|logger|log|clock)$/i;
11004
11322
  var HTTP_TRANSPORT_TYPE_RE = /^(?:KyInstance|AxiosInstance|Session)$/;
11005
11323
  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)$/;
11324
+ var VALUE_TYPE_RE = /^(?:ArrayBuffer|Blob|Buffer|Date|NodePath|RegExp|URL|URLSearchParams)$/;
11007
11325
  var TRANSPORT_WRAPPER_NAME_RE = /(?:Client$|^Http[A-Z])/;
11008
11326
  var ERROR_VALUE_NAME_RE = /Error$/;
11009
11327
  var FLUENT_BUILDER_NAME_RE = /Builder$/;
@@ -11467,7 +11785,7 @@ var require_interface_for_injected_service_default = createRule({
11467
11785
  if (node.abstract === true) return;
11468
11786
  if (node.decorators.length > 0) return;
11469
11787
  const ctor = node.body.body.find(
11470
- (member) => member.type === AST_NODE_TYPES53.MethodDefinition && member.kind === "constructor"
11788
+ (member) => member.type === AST_NODE_TYPES53.MethodDefinition && member.kind === "constructor" && member.value.body !== null && member.value.body !== void 0
11471
11789
  );
11472
11790
  if (ctor === void 0) return;
11473
11791
  const constructorFacts = readConstructor(
@@ -12346,7 +12664,7 @@ var rules = {
12346
12664
  };
12347
12665
  var meta = {
12348
12666
  name: "@sarj/eslint-plugin",
12349
- version: "11.0.1"
12667
+ version: "11.2.0"
12350
12668
  };
12351
12669
  var applicationOnlyRules = [
12352
12670
  "no-restricted-library-load",