@sarj/eslint-plugin 15.17.16 → 15.17.17

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
@@ -2555,12 +2555,12 @@ function exportedNextConfigProperty(sourceCode, path) {
2555
2555
  // src/rules/no-dangerously-allow-svg.ts
2556
2556
  var NEXT_CONFIG_RE = /(?:^|\/)next\.config\.[cm]?[jt]s$/;
2557
2557
  var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
2558
- summary: "Next.js image configuration enables unsanitized SVG rendering",
2558
+ summary: "Next.js image configuration enables SVG rendering without the required response hardening",
2559
2559
  rationale: "SVG files can contain scripts and other active content; enabling dangerouslyAllowSVG makes the image optimizer serve that content from the application origin.",
2560
- remediation: "Keep dangerouslyAllowSVG disabled. If SVG delivery is unavoidable, use a separately reviewed asset path with restrictive Content-Disposition and Content-Security-Policy headers.",
2560
+ remediation: "Keep dangerouslyAllowSVG disabled. If SVG optimization is required, retain attachment disposition and set the image Content-Security-Policy to `script-src 'none'; sandbox;`.",
2561
2561
  category: "security",
2562
2562
  limitations: [
2563
- "Only a literal true in the effective images property of a directly exported object, unescaped const alias, or isolated module.exports object is reported. Wrappers, factories, spreads, computed keys and mutations are not inferred."
2563
+ "Only literal effective properties of a directly exported object, unescaped const alias, or isolated module.exports object are analyzed. Wrappers, factories, spreads, computed keys, dynamic policies and mutations are not inferred."
2564
2564
  ],
2565
2565
  examples: [
2566
2566
  {
@@ -2583,6 +2583,19 @@ var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
2583
2583
  }
2584
2584
  ]
2585
2585
  };
2586
+ function literalString(node) {
2587
+ if (node?.value.type === "Literal" && typeof node.value.value === "string") return node.value.value;
2588
+ if (node?.value.type === "TemplateLiteral" && node.value.expressions.length === 0) return node.value.quasis[0]?.value.cooked ?? null;
2589
+ return null;
2590
+ }
2591
+ function hasHardenedSvgPolicy(policy) {
2592
+ const directives = policy.split(";").map((part) => part.trim().split(/\s+/u).filter(Boolean)).filter((parts) => parts.length > 0);
2593
+ const scriptSources = directives.filter(([name]) => name?.toLowerCase() === "script-src");
2594
+ const sandboxes = directives.filter(([name]) => name?.toLowerCase() === "sandbox");
2595
+ const scriptSource = scriptSources[0];
2596
+ const sandbox = sandboxes[0];
2597
+ return scriptSources.length === 1 && sandboxes.length === 1 && scriptSource?.length === 2 && scriptSource[1]?.toLowerCase() === "'none'" && sandbox?.length === 1;
2598
+ }
2586
2599
  var no_dangerously_allow_svg_default = createRule({
2587
2600
  name: "no-dangerously-allow-svg",
2588
2601
  documentation: NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION,
@@ -2591,7 +2604,7 @@ var no_dangerously_allow_svg_default = createRule({
2591
2604
  docs: { description: NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION.summary },
2592
2605
  schema: [],
2593
2606
  messages: {
2594
- noDangerouslyAllowSvg: "Do not enable dangerouslyAllowSVG. SVG can carry active content served from the application origin."
2607
+ noDangerouslyAllowSvg: "Do not enable dangerouslyAllowSVG without a script-blocking sandbox policy and attachment disposition. SVG can carry active content served from the application origin."
2595
2608
  }
2596
2609
  },
2597
2610
  defaultOptions: [],
@@ -2601,6 +2614,12 @@ var no_dangerously_allow_svg_default = createRule({
2601
2614
  "Program:exit"() {
2602
2615
  const node = exportedNextConfigProperty(context.sourceCode, ["images", "dangerouslyAllowSVG"]);
2603
2616
  if (node !== null && node.value.type === "Literal" && node.value.value === true) {
2617
+ const disposition = exportedNextConfigProperty(context.sourceCode, ["images", "contentDispositionType"]);
2618
+ const policy = literalString(
2619
+ exportedNextConfigProperty(context.sourceCode, ["images", "contentSecurityPolicy"])
2620
+ );
2621
+ const attachmentDisposition = disposition === null || literalString(disposition) === "attachment";
2622
+ if (attachmentDisposition && policy !== null && hasHardenedSvgPolicy(policy)) return;
2604
2623
  context.report({ node, messageId: "noDangerouslyAllowSvg" });
2605
2624
  }
2606
2625
  }
@@ -3719,12 +3738,41 @@ var no_hand_rolled_sleep_default = createRule({
3719
3738
 
3720
3739
  // src/rules/no-hand-rolled-spinner.ts
3721
3740
  var import_utils17 = require("@typescript-eslint/utils");
3741
+
3742
+ // src/rules/_tailwind.ts
3743
+ var tailwindVariantPrefix = (token) => {
3744
+ let bracketDepth = 0;
3745
+ let parenthesisDepth = 0;
3746
+ let escaped = false;
3747
+ let end = 0;
3748
+ for (let index = 0; index < token.length; index += 1) {
3749
+ const character = token[index];
3750
+ if (escaped) {
3751
+ escaped = false;
3752
+ continue;
3753
+ }
3754
+ if (character === "\\") {
3755
+ escaped = true;
3756
+ continue;
3757
+ }
3758
+ if (character === "[") bracketDepth += 1;
3759
+ else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
3760
+ else if (character === "(") parenthesisDepth += 1;
3761
+ else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
3762
+ else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
3763
+ }
3764
+ return token.slice(0, end);
3765
+ };
3766
+ var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
3767
+ var classTokens = (value) => value.split(/\s+/).filter(Boolean);
3768
+
3769
+ // src/rules/no-hand-rolled-spinner.ts
3722
3770
  var NO_HAND_ROLLED_SPINNER_DOCUMENTATION = {
3723
3771
  summary: "Disallow intrinsic elements styled as Tailwind border-ring spinners outside the design-system implementation.",
3724
3772
  rationale: "One-off loading indicators duplicate a shared primitive and let accessibility and styling diverge.",
3725
3773
  remediation: "Render the design-system Spinner component instead.",
3726
3774
  category: "maintainability",
3727
- limitations: ["Only effective static className values on div and span elements are inspected; a later spread makes the value unknown. Tests, stories, generated files, and the design-system implementation are excluded."],
3775
+ limitations: ["Only effective static className values on div and span elements are inspected; Tailwind utilities are combined only when they are unprefixed or share one exact variant context, while a later spread makes the value unknown. Tests, stories, generated files, and the design-system implementation are excluded."],
3728
3776
  examples: [
3729
3777
  { id: "design-system-spinner", title: "Use the shared spinner", outcome: "no-match", files: [{ path: "src/loading-state.tsx", source: '<Spinner className="size-4" />' }], focusPath: "src/loading-state.tsx", expectedCount: 0, public: true },
3730
3778
  { id: "border-ring-spinner", title: "Do not rebuild a spinner", outcome: "match", files: [{ path: "src/loading-state.tsx", source: '<div className="size-4 animate-spin rounded-full border-2 border-t-transparent" />' }], focusPath: "src/loading-state.tsx", expectedCount: 1, public: true }
@@ -3750,6 +3798,10 @@ function isContrastingEdge(token) {
3750
3798
  const match = DIRECTIONAL_BORDER.exec(token);
3751
3799
  return match?.[2] !== void 0 && !isBorderWidthValue(match[2]);
3752
3800
  }
3801
+ function hasSpinnerInVariant(classes, variant) {
3802
+ const effective = classes.filter((entry) => entry.variant === "" || entry.variant === variant).map((entry) => entry.base);
3803
+ return effective.includes("animate-spin") && effective.includes("rounded-full") && effective.some(isBorderWidth) && effective.some(isContrastingEdge);
3804
+ }
3753
3805
  function staticClassName(attribute) {
3754
3806
  const value = attribute.value;
3755
3807
  if (value?.type === import_utils17.AST_NODE_TYPES.Literal && typeof value.value === "string") {
@@ -3792,8 +3844,12 @@ var no_hand_rolled_spinner_default = createRule({
3792
3844
  if (classNameAttribute?.type !== import_utils17.AST_NODE_TYPES.JSXAttribute) return;
3793
3845
  const className = staticClassName(classNameAttribute);
3794
3846
  if (className === null) return;
3795
- const classes = className.split(/\s+/u);
3796
- if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some(isBorderWidth) && classes.some(isContrastingEdge)) {
3847
+ const classes = className.split(/\s+/u).filter(Boolean).map((token) => ({
3848
+ base: tailwindBase(token),
3849
+ variant: tailwindVariantPrefix(token)
3850
+ }));
3851
+ const variants = new Set(classes.map((entry) => entry.variant));
3852
+ if ([...variants].some((variant) => hasSpinnerInVariant(classes, variant))) {
3797
3853
  context.report({ node, messageId: "handRolledSpinner" });
3798
3854
  }
3799
3855
  }
@@ -4036,31 +4092,31 @@ var no_insecure_random_id_default = createRule({
4036
4092
  // src/rules/no-detached-global-fetch.ts
4037
4093
  var import_utils19 = require("@typescript-eslint/utils");
4038
4094
  var NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION = {
4039
- summary: "Preserve the receiver when the ambient global fetch is used as a value.",
4040
- rationale: "Some host runtimes expose fetch as a receiver-sensitive callable, so storing or passing the bare function can fail only after deployment.",
4041
- remediation: "Forward through `(input, init) => globalThis.fetch(input, init)` or bind the callable to its global receiver.",
4095
+ summary: "Keep the ambient global fetch receiver-safe when storing or explicitly rebinding it.",
4096
+ rationale: "Calling a raw ambient fetch through a class or object property supplies that object as `this`; receiver-sensitive hosts can reject that invocation only after deployment.",
4097
+ remediation: "Store a forwarding wrapper such as `(input, init) => fetch(input, init)`, or bind the callable to a compatible global or undefined receiver before storing it.",
4042
4098
  category: "correctness",
4043
4099
  autofix: "none",
4044
4100
  limitations: [
4045
- "The rule recognizes the unshadowed ambient fetch, globalThis.fetch, self.fetch, and window.fetch syntactically.",
4046
- "Direct calls, explicit receiver binding or invocation, tests, scripts, generated files, and locally defined fetch implementations are excluded.",
4047
- "The rule does not follow aliases or prove which JavaScript host executes a file."
4101
+ "The rule follows stable local variables and defaulted parameters from an unshadowed ambient fetch or globalThis/self/window fetch into direct class, object, or member storage.",
4102
+ "Bare local aliases, callback arguments, returns, direct calls, compatible global or undefined bind/call/apply receivers, tests, scripts, generated files, and locally defined fetch implementations are excluded.",
4103
+ "Interprocedural aliases, mutable aliases, collection storage, and host identity remain manual review boundaries."
4048
4104
  ],
4049
4105
  examples: [
4050
4106
  {
4051
4107
  id: "forwarded-global-fetch",
4052
- title: "Forward through the global receiver",
4108
+ title: "Store a receiver-safe forwarding wrapper",
4053
4109
  outcome: "no-match",
4054
- files: [{ path: "src/client.ts", source: "const request = (input: RequestInfo | URL, init?: RequestInit) => globalThis.fetch(input, init);" }],
4110
+ files: [{ path: "src/client.ts", source: "class Client { readonly request = (input: RequestInfo | URL, init?: RequestInit) => fetch(input, init); }" }],
4055
4111
  focusPath: "src/client.ts",
4056
4112
  expectedCount: 0,
4057
4113
  public: true
4058
4114
  },
4059
4115
  {
4060
- id: "detached-global-fetch",
4061
- title: "Do not detach the ambient fetch",
4116
+ id: "receiver-unsafe-global-fetch",
4117
+ title: "Do not store raw ambient fetch as an object method",
4062
4118
  outcome: "match",
4063
- files: [{ path: "src/client.ts", source: "class Client { constructor(private readonly request: typeof fetch = fetch) {} }" }],
4119
+ files: [{ path: "src/client.ts", source: "class Client { readonly request = fetch; }" }],
4064
4120
  focusPath: "src/client.ts",
4065
4121
  expectedCount: 1,
4066
4122
  public: true
@@ -4068,85 +4124,131 @@ var NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION = {
4068
4124
  ]
4069
4125
  };
4070
4126
  var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set(["globalThis", "self", "window"]);
4071
- var RECEIVER_METHODS = /* @__PURE__ */ new Set(["apply", "bind", "call"]);
4127
+ var EXPLICIT_RECEIVER_METHODS = /* @__PURE__ */ new Set(["apply", "bind", "call"]);
4128
+ var STORAGE_ASSIGNMENT_OPERATORS = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
4129
+ function unwrapExpression(node) {
4130
+ let current = node;
4131
+ while (current.type === import_utils19.AST_NODE_TYPES.ChainExpression || current.type === import_utils19.AST_NODE_TYPES.TSAsExpression || current.type === import_utils19.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils19.AST_NODE_TYPES.TSTypeAssertion) {
4132
+ current = current.expression;
4133
+ }
4134
+ return current;
4135
+ }
4072
4136
  var no_detached_global_fetch_default = createRule({
4073
4137
  name: "no-detached-global-fetch",
4074
4138
  documentation: NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION,
4075
4139
  meta: {
4076
4140
  type: "problem",
4077
- docs: { description: "Preserve the receiver when the ambient global fetch is used as a value." },
4141
+ docs: { description: NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION.summary },
4078
4142
  schema: [],
4079
4143
  messages: {
4080
- detachedGlobalFetch: "The ambient global `fetch` is being detached from its receiver. Forward through `globalThis.fetch(...)` or bind it to the global receiver."
4144
+ detachedGlobalFetch: "Raw ambient `fetch` becomes receiver-unsafe when stored or rebound this way. Store a forwarding wrapper or bind it to a compatible global or undefined receiver."
4081
4145
  }
4082
4146
  },
4083
4147
  defaultOptions: [],
4084
4148
  create(context) {
4085
4149
  const sourceCode = context.sourceCode;
4086
- if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, sourceCode.text)) {
4087
- return {};
4150
+ if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, sourceCode.text)) return {};
4151
+ const aliases = /* @__PURE__ */ new Set();
4152
+ function bindingOf(identifier) {
4153
+ return import_utils19.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
4088
4154
  }
4089
4155
  function resolvesToGlobal(identifier) {
4090
- const variable = import_utils19.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
4156
+ const variable = bindingOf(identifier);
4091
4157
  return variable === null || variable.defs.length === 0;
4092
4158
  }
4093
4159
  function isGlobalReceiver(node) {
4094
4160
  return node.type === import_utils19.AST_NODE_TYPES.Identifier && GLOBAL_RECEIVERS.has(node.name) && resolvesToGlobal(node);
4095
4161
  }
4096
- function isTypePosition(node) {
4097
- let parent = node.parent;
4098
- while (parent !== void 0) {
4099
- if (parent.type === import_utils19.AST_NODE_TYPES.TSTypeQuery) return true;
4100
- if (!parent.type.startsWith("TS")) return false;
4101
- parent = parent.parent;
4102
- }
4103
- return false;
4162
+ function isStableAlias(variable) {
4163
+ return !variable.references.some(
4164
+ (reference) => reference.isWrite() && reference.init !== true
4165
+ );
4104
4166
  }
4105
- function receiverOf(node) {
4106
- if (node.type === import_utils19.AST_NODE_TYPES.Identifier) return null;
4107
- return isGlobalReceiver(node.object) ? node.object : null;
4167
+ function recordAlias(identifier) {
4168
+ const variable = bindingOf(identifier);
4169
+ if (variable !== null && isStableAlias(variable)) aliases.add(variable);
4108
4170
  }
4109
- function isDirectCall(node) {
4110
- return node.parent.type === import_utils19.AST_NODE_TYPES.CallExpression && node.parent.callee === node;
4171
+ function isGlobalFetchMember(node) {
4172
+ if (!isGlobalReceiver(node.object)) return false;
4173
+ if (!node.computed) {
4174
+ return node.property.type === import_utils19.AST_NODE_TYPES.Identifier && node.property.name === "fetch";
4175
+ }
4176
+ return node.property.type === import_utils19.AST_NODE_TYPES.Literal && node.property.value === "fetch";
4111
4177
  }
4112
- function isSafeExplicitReceiverUse(node) {
4113
- const member = node.parent;
4114
- if (member.type !== import_utils19.AST_NODE_TYPES.MemberExpression || member.object !== node || member.computed || member.property.type !== import_utils19.AST_NODE_TYPES.Identifier || !RECEIVER_METHODS.has(member.property.name)) {
4115
- return false;
4178
+ function staticMemberName9(node) {
4179
+ if (!node.computed) {
4180
+ return node.property.type === import_utils19.AST_NODE_TYPES.Identifier ? node.property.name : null;
4116
4181
  }
4117
- const invocation = member.parent;
4118
- if (invocation.type !== import_utils19.AST_NODE_TYPES.CallExpression || invocation.callee !== member) {
4119
- return false;
4182
+ return node.property.type === import_utils19.AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : null;
4183
+ }
4184
+ function mayBeRawFetch(node) {
4185
+ const expression = unwrapExpression(node);
4186
+ if (expression.type === import_utils19.AST_NODE_TYPES.Identifier) {
4187
+ if (expression.name === "fetch" && resolvesToGlobal(expression)) return true;
4188
+ const variable = bindingOf(expression);
4189
+ return variable !== null && aliases.has(variable) && isStableAlias(variable);
4190
+ }
4191
+ if (expression.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
4192
+ return isGlobalFetchMember(expression);
4120
4193
  }
4121
- const receiver = invocation.arguments[0];
4122
- if (receiver === void 0 || receiver.type === import_utils19.AST_NODE_TYPES.SpreadElement) return false;
4123
- if (node.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
4124
- const original = receiverOf(node);
4125
- return original !== null && receiver.type === import_utils19.AST_NODE_TYPES.Identifier && receiver.name === original.name;
4194
+ if (expression.type === import_utils19.AST_NODE_TYPES.LogicalExpression) {
4195
+ return expression.operator === "&&" ? mayBeRawFetch(expression.right) : mayBeRawFetch(expression.left) || mayBeRawFetch(expression.right);
4126
4196
  }
4127
- return isGlobalReceiver(receiver);
4197
+ if (expression.type === import_utils19.AST_NODE_TYPES.ConditionalExpression) {
4198
+ return mayBeRawFetch(expression.consequent) || mayBeRawFetch(expression.alternate);
4199
+ }
4200
+ if (expression.type === import_utils19.AST_NODE_TYPES.SequenceExpression) {
4201
+ const last = expression.expressions.at(-1);
4202
+ return last !== void 0 && mayBeRawFetch(last);
4203
+ }
4204
+ return false;
4128
4205
  }
4129
- function reportIfDetached(node) {
4130
- if (isTypePosition(node) || isDirectCall(node) || isSafeExplicitReceiverUse(node)) return;
4131
- context.report({ node, messageId: "detachedGlobalFetch" });
4206
+ function recordAliasFromValue(identifier, value) {
4207
+ if (mayBeRawFetch(value)) recordAlias(identifier);
4208
+ }
4209
+ function reportStored(node) {
4210
+ if (mayBeRawFetch(node)) context.report({ node, messageId: "detachedGlobalFetch" });
4211
+ }
4212
+ function isCompatibleReceiver(node) {
4213
+ if (node === void 0 || node.type === import_utils19.AST_NODE_TYPES.SpreadElement) return false;
4214
+ if (node.type !== import_utils19.AST_NODE_TYPES.Identifier || !resolvesToGlobal(node)) return false;
4215
+ return GLOBAL_RECEIVERS.has(node.name) || node.name === "undefined";
4132
4216
  }
4133
4217
  return {
4134
- Identifier(node) {
4135
- if (node.name !== "fetch" || !resolvesToGlobal(node)) return;
4136
- const parent = node.parent;
4137
- if (parent.type === import_utils19.AST_NODE_TYPES.MemberExpression && parent.property === node) {
4138
- return;
4139
- }
4140
- if (parent.type === import_utils19.AST_NODE_TYPES.Property && parent.key === node && !parent.computed && !parent.shorthand) {
4141
- return;
4218
+ AssignmentExpression(node) {
4219
+ if (STORAGE_ASSIGNMENT_OPERATORS.has(node.operator) && node.left.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
4220
+ reportStored(node.right);
4142
4221
  }
4143
- reportIfDetached(node);
4144
4222
  },
4145
- "MemberExpression[computed=false]"(node) {
4146
- if (node.property.type !== import_utils19.AST_NODE_TYPES.Identifier || node.property.name !== "fetch" || !isGlobalReceiver(node.object)) {
4223
+ AssignmentPattern(node) {
4224
+ if (node.left.type !== import_utils19.AST_NODE_TYPES.Identifier) return;
4225
+ recordAliasFromValue(node.left, node.right);
4226
+ if (node.parent.type === import_utils19.AST_NODE_TYPES.TSParameterProperty) reportStored(node.right);
4227
+ },
4228
+ CallExpression(node) {
4229
+ const callee = unwrapExpression(node.callee);
4230
+ if (callee.type !== import_utils19.AST_NODE_TYPES.MemberExpression || !EXPLICIT_RECEIVER_METHODS.has(staticMemberName9(callee) ?? "") || !mayBeRawFetch(callee.object) || isCompatibleReceiver(node.arguments[0])) return;
4231
+ context.report({ node: callee.object, messageId: "detachedGlobalFetch" });
4232
+ },
4233
+ Property(node) {
4234
+ if (node.parent.type === import_utils19.AST_NODE_TYPES.ObjectPattern || node.method || node.value.type === import_utils19.AST_NODE_TYPES.AssignmentPattern || node.value.type === import_utils19.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) return;
4235
+ reportStored(node.value);
4236
+ },
4237
+ PropertyDefinition(node) {
4238
+ if (node.value !== null) reportStored(node.value);
4239
+ },
4240
+ VariableDeclarator(node) {
4241
+ if (node.init === null) return;
4242
+ if (node.id.type === import_utils19.AST_NODE_TYPES.Identifier) {
4243
+ recordAliasFromValue(node.id, node.init);
4147
4244
  return;
4148
4245
  }
4149
- reportIfDetached(node);
4246
+ if (node.id.type !== import_utils19.AST_NODE_TYPES.ObjectPattern || !isGlobalReceiver(unwrapExpression(node.init))) return;
4247
+ for (const property of node.id.properties) {
4248
+ if (property.type !== import_utils19.AST_NODE_TYPES.Property || property.computed || property.key.type !== import_utils19.AST_NODE_TYPES.Identifier || property.key.name !== "fetch") continue;
4249
+ const value = property.value.type === import_utils19.AST_NODE_TYPES.AssignmentPattern ? property.value.left : property.value;
4250
+ if (value.type === import_utils19.AST_NODE_TYPES.Identifier) recordAlias(value);
4251
+ }
4150
4252
  }
4151
4253
  };
4152
4254
  }
@@ -4408,7 +4510,7 @@ var NO_JSON_STRINGIFY_OBJECT_EQUALITY_DOCUMENTATION = {
4408
4510
  };
4409
4511
  var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["!=", "!==", "==", "==="]);
4410
4512
  var PRIMITIVE_FLAGS = import_typescript.default.TypeFlags.BigIntLike | import_typescript.default.TypeFlags.BooleanLike | import_typescript.default.TypeFlags.ESSymbolLike | import_typescript.default.TypeFlags.Never | import_typescript.default.TypeFlags.Null | import_typescript.default.TypeFlags.NumberLike | import_typescript.default.TypeFlags.StringLike | import_typescript.default.TypeFlags.Undefined | import_typescript.default.TypeFlags.Void;
4411
- function unwrapExpression(node) {
4513
+ function unwrapExpression2(node) {
4412
4514
  let current = node;
4413
4515
  while (current.type === import_utils21.AST_NODE_TYPES.ChainExpression || current.type === import_utils21.AST_NODE_TYPES.TSAsExpression || current.type === import_utils21.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils21.AST_NODE_TYPES.TSTypeAssertion) {
4414
4516
  current = current.expression;
@@ -4416,10 +4518,10 @@ function unwrapExpression(node) {
4416
4518
  return current;
4417
4519
  }
4418
4520
  function jsonStringifyArgument(node, sourceCode) {
4419
- const expression = unwrapExpression(node);
4521
+ const expression = unwrapExpression2(node);
4420
4522
  if (expression.type !== import_utils21.AST_NODE_TYPES.CallExpression) return null;
4421
4523
  const { callee } = expression;
4422
- if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression || callee.computed || callee.object.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.object.name !== "JSON" || callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "stringify") {
4524
+ if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression || callee.object.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.object.name !== "JSON" || (!callee.computed ? callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "stringify" : callee.property.type !== import_utils21.AST_NODE_TYPES.Literal || callee.property.value !== "stringify")) {
4423
4525
  return null;
4424
4526
  }
4425
4527
  const variable = import_utils21.ASTUtils.findVariable(sourceCode.getScope(callee.object), "JSON");
@@ -4447,7 +4549,7 @@ function typeMayContainObject(type, checker) {
4447
4549
  return true;
4448
4550
  }
4449
4551
  function syntaxMayContainObject(node) {
4450
- const expression = unwrapExpression(node);
4552
+ const expression = unwrapExpression2(node);
4451
4553
  if (expression.type === import_utils21.AST_NODE_TYPES.ObjectExpression) return true;
4452
4554
  if (expression.type !== import_utils21.AST_NODE_TYPES.ArrayExpression) return null;
4453
4555
  for (const element of expression.elements) {
@@ -5076,7 +5178,7 @@ var INTERFACE_CONTRACT_MEMBERS_PRIVATE_DOCUMENTATION = {
5076
5178
  category: "architecture",
5077
5179
  autofix: "none",
5078
5180
  limitations: [
5079
- "Only concrete classes with an explicit `implements` clause are checked; constructors and static members are excluded.",
5181
+ "Only concrete classes with an explicit `implements` clause are checked; constructors, static members, protected extension hooks, and overrides are excluded.",
5080
5182
  "Inherited interface members are resolved by TypeScript. Computed names are excluded because their contract identity is not stable syntax.",
5081
5183
  "The rule abstains for the whole class when TypeScript cannot resolve any implemented contract, avoiding false positives for missing or unavailable package declarations.",
5082
5184
  "The rule is report-only because a public member can have consumers in another source file; the developer must choose whether to extend the interface or privatize it.",
@@ -5128,7 +5230,7 @@ function reportClass(context, services, owner) {
5128
5230
  }
5129
5231
  }
5130
5232
  function candidate(member) {
5131
- return member.type === import_utils26.AST_NODE_TYPES.MethodDefinition && member.kind !== "constructor" && !member.static && !member.computed && member.key.type === import_utils26.AST_NODE_TYPES.Identifier && member.value.body !== null;
5233
+ return member.type === import_utils26.AST_NODE_TYPES.MethodDefinition && member.kind !== "constructor" && !member.static && member.accessibility !== "protected" && !member.override && !member.computed && member.key.type === import_utils26.AST_NODE_TYPES.Identifier && member.value.body !== null;
5132
5234
  }
5133
5235
  function interfaceNames(services, owner) {
5134
5236
  const tsOwner = services.esTreeNodeToTSNodeMap.get(owner);
@@ -5486,7 +5588,7 @@ var NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION = {
5486
5588
  remediation: "Rethrow the error, assert on it, or use the runner's explicit skip mechanism when the capability is optional.",
5487
5589
  category: "testing",
5488
5590
  filePatterns: ["**/*.test.*", "**/*.spec.*", "**/tests/**", "**/__tests__/**"],
5489
- limitations: ["Only bare returns owned by a direct supported test callback and followed lexically by a framework assertion are reported."],
5591
+ limitations: ["Only bare returns owned by a direct supported test callback and followed lexically by a framework assertion are reported. A runner skip suppresses the finding only when it is an unconditional earlier statement in the return's block."],
5490
5592
  examples: [
5491
5593
  { id: "rethrow", title: "Preserve the failure", outcome: "no-match", files: [{ path: "src/codec.test.ts", source: "test('decodes', () => { try { decode(); } catch (error) { throw error; } expect(result()).toBe('ok'); });" }], focusPath: "src/codec.test.ts", expectedCount: 0, public: true },
5492
5594
  { id: "bare-return", title: "Do not silently pass", outcome: "match", files: [{ path: "src/codec.test.ts", source: "test('decodes', () => { try { decode(); } catch { return; } expect(result()).toBe('ok'); });" }], focusPath: "src/codec.test.ts", expectedCount: 1, public: true }
@@ -5507,10 +5609,11 @@ function importedName4(identifier, context, modules) {
5507
5609
  const variable = import_utils29.ASTUtils.findVariable(context.sourceCode.getScope(identifier), identifier.name);
5508
5610
  if (variable === null || variable.defs.length === 0) return identifier.name;
5509
5611
  for (const definition of variable.defs) {
5510
- if (definition.node.type !== import_utils29.AST_NODE_TYPES.ImportSpecifier) continue;
5612
+ if (definition.node.type !== import_utils29.AST_NODE_TYPES.ImportSpecifier && definition.node.type !== import_utils29.AST_NODE_TYPES.ImportDefaultSpecifier && definition.node.type !== import_utils29.AST_NODE_TYPES.ImportNamespaceSpecifier) continue;
5511
5613
  const declaration = definition.node.parent;
5512
5614
  if (declaration.type !== import_utils29.AST_NODE_TYPES.ImportDeclaration || typeof declaration.source.value !== "string" || !modules.has(declaration.source.value)) continue;
5513
5615
  if (declaration.source.value === "node:assert" || declaration.source.value === "node:assert/strict") return "assert";
5616
+ if (definition.node.type !== import_utils29.AST_NODE_TYPES.ImportSpecifier) continue;
5514
5617
  const imported = definition.node.imported;
5515
5618
  return imported.type === import_utils29.AST_NODE_TYPES.Identifier ? imported.name : String(imported.value);
5516
5619
  }
@@ -5562,6 +5665,13 @@ function isExplicitSkip(node, context) {
5562
5665
  const root = rootIdentifier2(node.callee.object);
5563
5666
  return root !== null && TEST_NAMES.has(importedName4(root, context, TEST_MODULES2) ?? "");
5564
5667
  }
5668
+ function hasDominatingExplicitSkip(node, context) {
5669
+ const block = node.parent;
5670
+ if (block?.type !== import_utils29.AST_NODE_TYPES.BlockStatement) return false;
5671
+ return block.body.some(
5672
+ (candidate2) => candidate2.range[1] <= node.range[0] && candidate2.type === import_utils29.AST_NODE_TYPES.ExpressionStatement && isExplicitSkip(candidate2.expression, context)
5673
+ );
5674
+ }
5565
5675
  var no_bare_return_from_test_catch_default = createRule({
5566
5676
  name: "no-bare-return-from-test-catch",
5567
5677
  documentation: NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION,
@@ -5587,11 +5697,12 @@ var no_bare_return_from_test_catch_default = createRule({
5587
5697
  }
5588
5698
  if (current === null || current === void 0) break;
5589
5699
  }
5590
- if (catchClause === null || catchClause.parent.finalizer !== null) return;
5700
+ if (catchClause === null) return;
5591
5701
  const parameter = catchClause.param;
5592
- if (parameter?.type === import_utils29.AST_NODE_TYPES.Identifier && node.parent === catchClause.body) {
5702
+ const returnBlock = node.parent;
5703
+ if (parameter?.type === import_utils29.AST_NODE_TYPES.Identifier && returnBlock?.type === import_utils29.AST_NODE_TYPES.BlockStatement) {
5593
5704
  const errorBinding = import_utils29.ASTUtils.findVariable(context.sourceCode.getScope(parameter), parameter.name);
5594
- const assertedError = catchClause.body.body.some((statement) => {
5705
+ const assertedError = returnBlock.body.some((statement) => {
5595
5706
  if (statement.range[1] >= node.range[0] || statement.type !== import_utils29.AST_NODE_TYPES.ExpressionStatement) return false;
5596
5707
  const expression = statement.expression;
5597
5708
  if (expression.type !== import_utils29.AST_NODE_TYPES.CallExpression || !isAssertion(expression, context)) return false;
@@ -5605,7 +5716,7 @@ var no_bare_return_from_test_catch_default = createRule({
5605
5716
  });
5606
5717
  if (assertedError) return;
5607
5718
  }
5608
- if (walkOwnScope(catchClause.body, (current) => current.type === import_utils29.AST_NODE_TYPES.ThrowStatement || isExplicitSkip(current, context))) return;
5719
+ if (hasDominatingExplicitSkip(node, context)) return;
5609
5720
  if (!walkOwnScope(owner.body, (current) => current.range[0] > node.range[1] && isAssertion(current, context))) return;
5610
5721
  context.report({ node, messageId: "bareReturnFromTestCatch" });
5611
5722
  }
@@ -5843,7 +5954,8 @@ var no_long_comment_default = createRule({
5843
5954
  });
5844
5955
 
5845
5956
  // src/rules/no-vague-suppression-description.ts
5846
- var DIRECTIVE_WITH_DESCRIPTION_RE = /^(?:eslint-(?:disable|disable-next-line|disable-line)\b[^:\n]*?|@ts-expect-error\b)\s*(?::|--)\s*(.+?)\s*$/iu;
5957
+ var ESLINT_DIRECTIVE_WITH_DESCRIPTION_RE = /^eslint-(?:disable|disable-next-line|disable-line)\b[^:\n]*?\s*(?::|--)\s*(.+?)\s*$/iu;
5958
+ var TS_EXPECT_ERROR_WITH_DESCRIPTION_RE = /^@ts-expect-error\b(?:(?:\s*(?::|--)\s*)|\s+)(.+?)\s*$/iu;
5847
5959
  var VAGUE_RE = /^(?:needed|required|intentional(?:ly)?|ignore(?:d)?|false positive|type error|typescript|to satisfy (?:the )?(?:linter|typescript|type checker))\.?$/iu;
5848
5960
  var NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION = {
5849
5961
  summary: "Require suppression descriptions to name the concrete mismatch or invariant instead of a generic non-reason.",
@@ -5886,6 +5998,9 @@ var NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION = {
5886
5998
  }
5887
5999
  ]
5888
6000
  };
6001
+ function suppressionDescription(text) {
6002
+ return (ESLINT_DIRECTIVE_WITH_DESCRIPTION_RE.exec(text)?.[1] ?? TS_EXPECT_ERROR_WITH_DESCRIPTION_RE.exec(text)?.[1])?.trim();
6003
+ }
5889
6004
  var no_vague_suppression_description_default = createRule({
5890
6005
  name: "no-vague-suppression-description",
5891
6006
  documentation: NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION,
@@ -5908,7 +6023,7 @@ var no_vague_suppression_description_default = createRule({
5908
6023
  Program() {
5909
6024
  for (const comment of context.sourceCode.getAllComments()) {
5910
6025
  const text = comment.value.trim();
5911
- const description = DIRECTIVE_WITH_DESCRIPTION_RE.exec(text)?.[1]?.trim();
6026
+ const description = suppressionDescription(text);
5912
6027
  if (description === void 0 || !VAGUE_RE.test(description)) continue;
5913
6028
  context.report({
5914
6029
  loc: comment.loc,
@@ -6972,12 +7087,24 @@ var NO_RESTRICTED_LIBRARY_LOAD_DOCUMENTATION = {
6972
7087
  { id: "runtime-load", title: "Do not load a restricted library at runtime", outcome: "match", files: [{ path: "src/client.ts", source: "const client = require('axios');" }], focusPath: "src/client.ts", expectedCount: 1, public: true }
6973
7088
  ]
6974
7089
  };
6975
- function literalModule(node) {
6976
- return node?.type === import_utils37.AST_NODE_TYPES.Literal && typeof node.value === "string" ? node.value : null;
7090
+ function staticModule(node) {
7091
+ if (node?.type === import_utils37.AST_NODE_TYPES.Literal && typeof node.value === "string") {
7092
+ return node.value;
7093
+ }
7094
+ if (node?.type === import_utils37.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0) {
7095
+ return node.quasis[0]?.value.cooked ?? null;
7096
+ }
7097
+ return null;
6977
7098
  }
6978
7099
  function matchesModule(source, module2) {
6979
7100
  return source === module2 || source.startsWith(`${module2}/`);
6980
7101
  }
7102
+ function staticMemberName4(node) {
7103
+ if (!node.computed && node.property.type === import_utils37.AST_NODE_TYPES.Identifier) {
7104
+ return node.property.name;
7105
+ }
7106
+ return node.computed && node.property.type === import_utils37.AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : null;
7107
+ }
6981
7108
  var no_restricted_library_load_default = createRule({
6982
7109
  name: "no-restricted-library-load",
6983
7110
  documentation: NO_RESTRICTED_LIBRARY_LOAD_DOCUMENTATION,
@@ -7041,24 +7168,24 @@ var no_restricted_library_load_default = createRule({
7041
7168
  }
7042
7169
  return {
7043
7170
  ImportExpression(node) {
7044
- const source = literalModule(node.source);
7171
+ const source = staticModule(node.source);
7045
7172
  if (source !== null) report2(node.source, source);
7046
7173
  },
7047
7174
  CallExpression(node) {
7048
7175
  let requireIdentifier = null;
7049
7176
  if (node.callee.type === import_utils37.AST_NODE_TYPES.Identifier && node.callee.name === "require") {
7050
7177
  requireIdentifier = node.callee;
7051
- } else if (node.callee.type === import_utils37.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.object.type === import_utils37.AST_NODE_TYPES.Identifier && node.callee.object.name === "require" && node.callee.property.type === import_utils37.AST_NODE_TYPES.Identifier && node.callee.property.name === "resolve") {
7178
+ } else if (node.callee.type === import_utils37.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils37.AST_NODE_TYPES.Identifier && node.callee.object.name === "require" && staticMemberName4(node.callee) === "resolve") {
7052
7179
  requireIdentifier = node.callee.object;
7053
7180
  }
7054
7181
  if (requireIdentifier === null || !isUnshadowedRequire(requireIdentifier)) return;
7055
- const source = literalModule(node.arguments[0]);
7182
+ const source = staticModule(node.arguments[0]);
7056
7183
  if (source !== null) report2(node.arguments[0], source);
7057
7184
  },
7058
7185
  TSImportEqualsDeclaration(node) {
7059
7186
  if (node.importKind === "type") return;
7060
7187
  if (node.moduleReference.type !== import_utils37.AST_NODE_TYPES.TSExternalModuleReference) return;
7061
- const source = literalModule(node.moduleReference.expression);
7188
+ const source = staticModule(node.moduleReference.expression);
7062
7189
  if (source !== null) report2(node.moduleReference.expression, source);
7063
7190
  }
7064
7191
  };
@@ -11098,7 +11225,7 @@ function rootIdentifier3(callee) {
11098
11225
  if (callee.type === import_utils60.AST_NODE_TYPES.TaggedTemplateExpression) return rootIdentifier3(callee.tag);
11099
11226
  return null;
11100
11227
  }
11101
- function staticMemberName4(member) {
11228
+ function staticMemberName5(member) {
11102
11229
  if (!member.computed && member.property.type === import_utils60.AST_NODE_TYPES.Identifier) return member.property.name;
11103
11230
  if (member.computed && member.property.type === import_utils60.AST_NODE_TYPES.Literal && typeof member.property.value === "string") {
11104
11231
  return member.property.value;
@@ -11113,7 +11240,7 @@ function isTestBody2(node, isFrameworkTest) {
11113
11240
  function isTestCaller(callee) {
11114
11241
  if (callee.type === import_utils60.AST_NODE_TYPES.Identifier) return TEST_CALLERS3.has(callee.name);
11115
11242
  if (callee.type !== import_utils60.AST_NODE_TYPES.MemberExpression) return false;
11116
- const member = staticMemberName4(callee);
11243
+ const member = staticMemberName5(callee);
11117
11244
  return member !== null && TEST_MODIFIERS3.has(member) && isTestCaller(callee.object);
11118
11245
  }
11119
11246
  function nearestEnclosingFunction2(node) {
@@ -11194,7 +11321,7 @@ function opensSubtest(node, callbackParameters) {
11194
11321
  return false;
11195
11322
  }
11196
11323
  const callee = node.callee;
11197
- return callee.type === import_utils60.AST_NODE_TYPES.MemberExpression && staticMemberName4(callee) === "test" && callee.object.type === import_utils60.AST_NODE_TYPES.Identifier && callbackParameters.has(callee.object.name) && node.arguments.some(
11324
+ return callee.type === import_utils60.AST_NODE_TYPES.MemberExpression && staticMemberName5(callee) === "test" && callee.object.type === import_utils60.AST_NODE_TYPES.Identifier && callbackParameters.has(callee.object.name) && node.arguments.some(
11198
11325
  (argument) => argument.type !== import_utils60.AST_NODE_TYPES.SpreadElement && FUNCTION_TYPES6.has(argument.type)
11199
11326
  );
11200
11327
  }
@@ -11244,7 +11371,7 @@ var test_loops_over_literal_cases_default = createRule({
11244
11371
  for (let current = node; current !== void 0 && current !== enclosing; current = current.parent) {
11245
11372
  if (current.parent?.type === import_utils60.AST_NODE_TYPES.BlockStatement && current.parent.body.at(-1) !== current) return;
11246
11373
  }
11247
- const cases = unwrapExpression2(node.right);
11374
+ const cases = unwrapExpression3(node.right);
11248
11375
  const callbackParameters = new Set(
11249
11376
  enclosing.params.flatMap((parameter) => parameter.type === import_utils60.AST_NODE_TYPES.Identifier ? [parameter.name] : [])
11250
11377
  );
@@ -11272,9 +11399,9 @@ var test_loops_over_literal_cases_default = createRule({
11272
11399
  };
11273
11400
  }
11274
11401
  });
11275
- function unwrapExpression2(node) {
11402
+ function unwrapExpression3(node) {
11276
11403
  if (node.type === import_utils60.AST_NODE_TYPES.TSAsExpression || node.type === import_utils60.AST_NODE_TYPES.TSTypeAssertion || node.type === import_utils60.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils60.AST_NODE_TYPES.TSNonNullExpression) {
11277
- return unwrapExpression2(node.expression);
11404
+ return unwrapExpression3(node.expression);
11278
11405
  }
11279
11406
  return node;
11280
11407
  }
@@ -12061,14 +12188,14 @@ function isAsConst(node, sourceText) {
12061
12188
  if (node.type !== import_utils68.AST_NODE_TYPES.TSAsExpression) return false;
12062
12189
  return sourceText(node.typeAnnotation).trim() === "const";
12063
12190
  }
12064
- function unwrapExpression3(node) {
12191
+ function unwrapExpression4(node) {
12065
12192
  if (node.type === import_utils68.AST_NODE_TYPES.TSAsExpression || node.type === import_utils68.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils68.AST_NODE_TYPES.TSNonNullExpression) {
12066
- return unwrapExpression3(node.expression);
12193
+ return unwrapExpression4(node.expression);
12067
12194
  }
12068
12195
  return node;
12069
12196
  }
12070
12197
  function isObjectFreeze(node, isUnshadowedGlobal3) {
12071
- const inner = unwrapExpression3(node);
12198
+ const inner = unwrapExpression4(node);
12072
12199
  if (inner.type === import_utils68.AST_NODE_TYPES.CallExpression && inner.callee.type === import_utils68.AST_NODE_TYPES.MemberExpression && !inner.callee.computed && inner.callee.object.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.object.name === "Object" && isUnshadowedGlobal3(inner.callee.object) && inner.callee.property.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.property.name === "freeze" && inner.arguments.length === 1) {
12073
12200
  const argument = inner.arguments[0];
12074
12201
  return argument !== void 0 && argument.type !== import_utils68.AST_NODE_TYPES.SpreadElement && collectionKind(argument, isUnshadowedGlobal3) === "literal";
@@ -12076,7 +12203,7 @@ function isObjectFreeze(node, isUnshadowedGlobal3) {
12076
12203
  return false;
12077
12204
  }
12078
12205
  function collectionKind(node, isUnshadowedGlobal3) {
12079
- const inner = unwrapExpression3(node);
12206
+ const inner = unwrapExpression4(node);
12080
12207
  if (inner.type === import_utils68.AST_NODE_TYPES.CallExpression && inner.callee.type === import_utils68.AST_NODE_TYPES.MemberExpression && !inner.callee.computed && inner.callee.object.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.object.name === "Object" && isUnshadowedGlobal3(inner.callee.object) && inner.callee.property.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.property.name === "freeze" && inner.arguments.length === 1 && inner.arguments[0] !== void 0 && inner.arguments[0].type !== import_utils68.AST_NODE_TYPES.SpreadElement) {
12081
12208
  return collectionKind(inner.arguments[0], isUnshadowedGlobal3);
12082
12209
  }
@@ -15503,17 +15630,18 @@ var prefer_schema_for_api_payload_default = createRule({
15503
15630
  // src/rules/prefer-shared-zod-enum.ts
15504
15631
  var import_utils83 = require("@typescript-eslint/utils");
15505
15632
  var PREFER_SHARED_ZOD_ENUM_DOCUMENTATION = {
15506
- summary: "Give literal Zod enum domains one reusable module-level schema.",
15507
- rationale: "Inline or repeated literal domains hide a reusable contract and allow equivalent fields to drift independently.",
15633
+ summary: "Give repeated literal Zod enum domains one reusable module-level schema.",
15634
+ rationale: "Repeated literal domains hide a shared contract and allow equivalent fields to drift independently.",
15508
15635
  remediation: "Declare a module-level named Zod enum schema and reuse it at each field or contract site.",
15509
15636
  category: "maintainability",
15510
- limitations: ["Only direct z.enum calls with string-literal arrays are inspected; computed domains require review. Equal values do not prove a shared business domain: retain local schemas when ownership, error customization, or future evolution differs, and review initialization order before extraction."],
15637
+ limitations: ["Only two or more exact, same-order direct z.enum calls with string-literal arrays and no customization argument in one module are inspected; one-off, computed, and customized domains require review. Equal values do not prove a shared business domain: retain local schemas when ownership or future evolution differs, and review initialization order before extraction."],
15511
15638
  examples: [
15512
15639
  { id: "shared-provider", title: "Reuse a named enum schema", outcome: "no-match", files: [{ path: "src/provider.ts", source: "import { z } from 'zod'; const ProviderSchema = z.enum(['agy', 'claude', 'sol']); const JobSchema = z.object({ provider: ProviderSchema }); const StatusSchema = z.object({ provider: ProviderSchema.optional() });" }], focusPath: "src/provider.ts", expectedCount: 0, public: true },
15513
- { id: "inline-provider", title: "Do not inline enum domains in object fields", outcome: "match", files: [{ path: "src/provider.ts", source: "import { z } from 'zod'; const JobSchema = z.object({ provider: z.enum(['agy', 'claude', 'sol']) });" }], focusPath: "src/provider.ts", expectedCount: 1, public: true }
15640
+ { id: "inline-provider", title: "Do not repeat an inline enum domain", outcome: "match", files: [{ path: "src/provider.ts", source: "import { z } from 'zod'; const JobSchema = z.object({ provider: z.enum(['agy', 'claude', 'sol']) }); const StatusSchema = z.object({ provider: z.enum(['agy', 'claude', 'sol']).optional() });" }], focusPath: "src/provider.ts", expectedCount: 2, public: true }
15514
15641
  ]
15515
15642
  };
15516
15643
  function literalDomain(node) {
15644
+ if (node.arguments.length !== 1) return null;
15517
15645
  const [argument] = node.arguments;
15518
15646
  if (argument?.type !== import_utils83.AST_NODE_TYPES.ArrayExpression || argument.elements.length < 2) return null;
15519
15647
  const values = [];
@@ -15539,7 +15667,7 @@ var prefer_shared_zod_enum_default = createRule({
15539
15667
  documentation: PREFER_SHARED_ZOD_ENUM_DOCUMENTATION,
15540
15668
  meta: {
15541
15669
  type: "suggestion",
15542
- docs: { description: "Give literal Zod enum domains one reusable module-level schema." },
15670
+ docs: { description: PREFER_SHARED_ZOD_ENUM_DOCUMENTATION.summary },
15543
15671
  schema: [],
15544
15672
  messages: {
15545
15673
  shareEnumDomain: "Extract this literal Zod enum to one module-level named schema and reuse it."
@@ -15550,7 +15678,7 @@ var prefer_shared_zod_enum_default = createRule({
15550
15678
  if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
15551
15679
  const zodBindings = /* @__PURE__ */ new Set();
15552
15680
  const bindingOf = (node) => import_utils83.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
15553
- const seen = /* @__PURE__ */ new Set();
15681
+ const candidates = /* @__PURE__ */ new Map();
15554
15682
  return {
15555
15683
  ImportDeclaration(node) {
15556
15684
  if (!isZodModule(node.source.value)) return;
@@ -15568,10 +15696,19 @@ var prefer_shared_zod_enum_default = createRule({
15568
15696
  const domain = literalDomain(node);
15569
15697
  if (domain === null) return;
15570
15698
  const key = JSON.stringify(domain);
15571
- const inline = !isModuleLevelNamedSchema(node);
15572
- if (inline || seen.has(key))
15573
- context.report({ node, messageId: "shareEnumDomain" });
15574
- else seen.add(key);
15699
+ const group = candidates.get(key) ?? [];
15700
+ group.push({ node, named: isModuleLevelNamedSchema(node) });
15701
+ candidates.set(key, group);
15702
+ },
15703
+ "Program:exit"() {
15704
+ for (const group of candidates.values()) {
15705
+ if (group.length < 2) continue;
15706
+ const canonical = group.find((candidate2) => candidate2.named);
15707
+ for (const candidate2 of group) {
15708
+ if (candidate2 === canonical) continue;
15709
+ context.report({ node: candidate2.node, messageId: "shareEnumDomain" });
15710
+ }
15711
+ }
15575
15712
  }
15576
15713
  };
15577
15714
  }
@@ -15646,35 +15783,6 @@ var prefer_switch_for_repeated_equality_default = createRule({
15646
15783
  var import_utils85 = require("@typescript-eslint/utils");
15647
15784
  var import_fs = require("fs");
15648
15785
  var import_path = require("path");
15649
-
15650
- // src/rules/_tailwind.ts
15651
- var tailwindVariantPrefix = (token) => {
15652
- let bracketDepth = 0;
15653
- let parenthesisDepth = 0;
15654
- let escaped = false;
15655
- let end = 0;
15656
- for (let index = 0; index < token.length; index += 1) {
15657
- const character = token[index];
15658
- if (escaped) {
15659
- escaped = false;
15660
- continue;
15661
- }
15662
- if (character === "\\") {
15663
- escaped = true;
15664
- continue;
15665
- }
15666
- if (character === "[") bracketDepth += 1;
15667
- else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
15668
- else if (character === "(") parenthesisDepth += 1;
15669
- else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
15670
- else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
15671
- }
15672
- return token.slice(0, end);
15673
- };
15674
- var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
15675
- var classTokens = (value) => value.split(/\s+/).filter(Boolean);
15676
-
15677
- // src/rules/prefer-semantic-colors.ts
15678
15786
  var PREFER_SEMANTIC_COLORS_DOCUMENTATION = {
15679
15787
  summary: "Enforce semantic color tokens over raw Tailwind palette classes, arbitrary color values, and inline color literals.",
15680
15788
  rationale: "Semantic tokens keep themes and product meaning consistent while raw colors couple components to a palette value.",
@@ -16728,7 +16836,7 @@ var TEST_MODIFIERS4 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "s
16728
16836
  var EXPECT_MODIFIERS = /* @__PURE__ */ new Set(["not", "rejects", "resolves"]);
16729
16837
  var SNAPSHOT_MATCHERS = /snapshot/iu;
16730
16838
  var MIN_CASES2 = 3;
16731
- function staticMemberName5(node) {
16839
+ function staticMemberName6(node) {
16732
16840
  if (!node.computed && node.property.type === import_utils88.AST_NODE_TYPES.Identifier) return node.property.name;
16733
16841
  if (node.computed && node.property.type === import_utils88.AST_NODE_TYPES.Literal && typeof node.property.value === "string") return node.property.value;
16734
16842
  return null;
@@ -16755,7 +16863,7 @@ function isDirectTestCallback2(node, context) {
16755
16863
  function testRoot2(callee) {
16756
16864
  if (callee.type === import_utils88.AST_NODE_TYPES.Identifier) return callee;
16757
16865
  if (callee.type !== import_utils88.AST_NODE_TYPES.MemberExpression) return null;
16758
- const modifier = staticMemberName5(callee);
16866
+ const modifier = staticMemberName6(callee);
16759
16867
  return modifier !== null && TEST_MODIFIERS4.has(modifier) ? testRoot2(callee.object) : null;
16760
16868
  }
16761
16869
  function isStatic(node) {
@@ -16819,7 +16927,7 @@ function expectCallFromMatcher(node) {
16819
16927
  const modifiers = [];
16820
16928
  let receiver = node.object;
16821
16929
  while (receiver.type === import_utils88.AST_NODE_TYPES.MemberExpression) {
16822
- const modifier = staticMemberName5(receiver);
16930
+ const modifier = staticMemberName6(receiver);
16823
16931
  if (modifier === null || !EXPECT_MODIFIERS.has(modifier)) return null;
16824
16932
  modifiers.unshift(modifier);
16825
16933
  receiver = receiver.object;
@@ -18407,8 +18515,8 @@ var FLUENT_BUILDER_NAME_RE = /Builder$/;
18407
18515
  var FLUENT_RESULT_TYPE_RE = /(?:Builder|Base|Query|Without)(?:\W|$)/;
18408
18516
  var ROUTER_FACTORY_NAME = "Router";
18409
18517
  var FRAMEWORK_HTTP_TYPES = /* @__PURE__ */ new Set(["Request", "Response", "NextFunction"]);
18410
- var STORAGE_ASSIGNMENT_OPERATORS = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
18411
- var staticMemberName6 = (member) => {
18518
+ var STORAGE_ASSIGNMENT_OPERATORS2 = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
18519
+ var staticMemberName7 = (member) => {
18412
18520
  if (member.property.type === import_utils94.AST_NODE_TYPES.PrivateIdentifier) return `#${member.property.name}`;
18413
18521
  if (!member.computed && member.property.type === import_utils94.AST_NODE_TYPES.Identifier) return member.property.name;
18414
18522
  return member.computed && member.property.type === import_utils94.AST_NODE_TYPES.Literal && typeof member.property.value === "string" ? member.property.value : null;
@@ -18500,8 +18608,8 @@ var readConstructor = (ctor, declared, typeParameters) => {
18500
18608
  if (current === void 0) break;
18501
18609
  if (current.type === import_utils94.AST_NODE_TYPES.ArrowFunctionExpression || current.type === import_utils94.AST_NODE_TYPES.FunctionExpression || current.type === import_utils94.AST_NODE_TYPES.FunctionDeclaration || current.type === import_utils94.AST_NODE_TYPES.ClassExpression || current.type === import_utils94.AST_NODE_TYPES.ClassDeclaration) continue;
18502
18610
  const expression = current.type === import_utils94.AST_NODE_TYPES.ExpressionStatement ? current.expression : null;
18503
- const storedField = expression?.type === import_utils94.AST_NODE_TYPES.AssignmentExpression && expression.left.type === import_utils94.AST_NODE_TYPES.MemberExpression && expression.left.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ? staticMemberName6(expression.left) : null;
18504
- if (expression?.type !== import_utils94.AST_NODE_TYPES.AssignmentExpression || !STORAGE_ASSIGNMENT_OPERATORS.has(expression.operator) || expression.left.type !== import_utils94.AST_NODE_TYPES.MemberExpression || expression.left.object.type !== import_utils94.AST_NODE_TYPES.ThisExpression || storedField === null) {
18611
+ const storedField = expression?.type === import_utils94.AST_NODE_TYPES.AssignmentExpression && expression.left.type === import_utils94.AST_NODE_TYPES.MemberExpression && expression.left.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ? staticMemberName7(expression.left) : null;
18612
+ if (expression?.type !== import_utils94.AST_NODE_TYPES.AssignmentExpression || !STORAGE_ASSIGNMENT_OPERATORS2.has(expression.operator) || expression.left.type !== import_utils94.AST_NODE_TYPES.MemberExpression || expression.left.object.type !== import_utils94.AST_NODE_TYPES.ThisExpression || storedField === null) {
18505
18613
  for (const key of Object.keys(current)) {
18506
18614
  if (key === "parent") continue;
18507
18615
  const value = current[key];
@@ -18525,7 +18633,7 @@ var readConstructor = (ctor, declared, typeParameters) => {
18525
18633
  const fields = storedFieldsFrom.get(source.object.name) ?? /* @__PURE__ */ new Set();
18526
18634
  fields.add(storedField);
18527
18635
  storedFieldsFrom.set(source.object.name, fields);
18528
- const member = staticMemberName6(source);
18636
+ const member = staticMemberName7(source);
18529
18637
  if (member !== null) {
18530
18638
  const members = storedMemberFieldsFrom.get(source.object.name) ?? /* @__PURE__ */ new Map();
18531
18639
  const memberFields = members.get(member) ?? /* @__PURE__ */ new Set();
@@ -18657,7 +18765,7 @@ var invokedInstanceField = (call) => {
18657
18765
  var instanceField = (candidate2) => {
18658
18766
  let node = candidate2;
18659
18767
  while (node.type === import_utils94.AST_NODE_TYPES.ChainExpression || node.type === import_utils94.AST_NODE_TYPES.TSAsExpression || node.type === import_utils94.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils94.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils94.AST_NODE_TYPES.TSTypeAssertion) node = node.expression;
18660
- return node.type === import_utils94.AST_NODE_TYPES.MemberExpression && node.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ? staticMemberName6(node) : null;
18768
+ return node.type === import_utils94.AST_NODE_TYPES.MemberExpression && node.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ? staticMemberName7(node) : null;
18661
18769
  };
18662
18770
  var behaviorallyInvokedFields = (body2) => {
18663
18771
  const invoked = /* @__PURE__ */ new Set();
@@ -19247,14 +19355,14 @@ var REQUIRE_STATIC_NEXT_MATCHER_DOCUMENTATION = {
19247
19355
  ]
19248
19356
  };
19249
19357
  var NEXT_ENTRY_FILE = /(?:^|[/\\])(?:middleware|proxy)\.[cm]?[jt]sx?$/u;
19250
- function unwrapExpression4(node) {
19358
+ function unwrapExpression5(node) {
19251
19359
  if (node.type === import_utils96.AST_NODE_TYPES.TSAsExpression || node.type === import_utils96.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils96.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils96.AST_NODE_TYPES.TSTypeAssertion) {
19252
- return unwrapExpression4(node.expression);
19360
+ return unwrapExpression5(node.expression);
19253
19361
  }
19254
19362
  return node;
19255
19363
  }
19256
19364
  function isStaticValue(node) {
19257
- const value = unwrapExpression4(node);
19365
+ const value = unwrapExpression5(node);
19258
19366
  if (value.type === import_utils96.AST_NODE_TYPES.Literal) {
19259
19367
  return true;
19260
19368
  }
@@ -19274,9 +19382,8 @@ function isStaticValue(node) {
19274
19382
  return false;
19275
19383
  }
19276
19384
  function propertyName5(property) {
19277
- if (property.computed) return null;
19278
- if (property.key.type === import_utils96.AST_NODE_TYPES.Identifier) return property.key.name;
19279
- return typeof property.key.value === "string" ? property.key.value : null;
19385
+ if (!property.computed && property.key.type === import_utils96.AST_NODE_TYPES.Identifier) return property.key.name;
19386
+ return property.key.type === import_utils96.AST_NODE_TYPES.Literal && typeof property.key.value === "string" ? property.key.value : null;
19280
19387
  }
19281
19388
  var require_static_next_matcher_default = createRule({
19282
19389
  name: "require-static-next-matcher",
@@ -19305,7 +19412,7 @@ var require_static_next_matcher_default = createRule({
19305
19412
  if (declaration.id.type !== import_utils96.AST_NODE_TYPES.Identifier || declaration.id.name !== "config" || declaration.init === null) {
19306
19413
  continue;
19307
19414
  }
19308
- const config = unwrapExpression4(declaration.init);
19415
+ const config = unwrapExpression5(declaration.init);
19309
19416
  if (config.type !== import_utils96.AST_NODE_TYPES.ObjectExpression) {
19310
19417
  continue;
19311
19418
  }
@@ -19326,38 +19433,44 @@ var require_static_next_matcher_default = createRule({
19326
19433
  // src/rules/require-use-form-default-values.ts
19327
19434
  var import_utils97 = require("@typescript-eslint/utils");
19328
19435
  var REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION = {
19329
- summary: "react-hook-form useForm call without explicit initial or reactive values",
19330
- rationale: "Without an explicit initial value, fields can change from uncontrolled to controlled as data arrives, reset behavior becomes ambiguous, and the form's initial shape no longer documents the values users can edit.",
19331
- remediation: "Provide defaultValues for initial state, or values when reactive external state owns initialization; choose schema-appropriate values for controlled fields.",
19436
+ summary: "Require explicit form-level initialization or a field default for directly bound Controller fields.",
19437
+ rationale: "React Hook Form exposes form-level defaultValues/values and field-level defaultValue as its explicit initialization mechanisms. A directly bound controlled field should not omit both mechanisms.",
19438
+ remediation: "Provide a non-undefined defaultValues or values option to useForm, or a non-undefined defaultValue on the associated Controller/useController field.",
19332
19439
  category: "correctness",
19333
19440
  limitations: [
19334
- "Only direct calls to a scope-resolved useForm value imported from react-hook-form are checked; reactive values are accepted, while wrapper hooks, spreads and computed option objects are intentionally not inferred."
19441
+ "The rule proves only that an explicit form-level initialization option or field default is present; it does not prove that a particular field path occurs inside a defaultValues/values object.",
19442
+ "The rule reports only scope-resolved Controller/useController fields explicitly bound to a directly created useForm control. FormProvider context, wrapper hooks, dynamic options, spreads, computed properties, and interprocedural flows are intentionally left unreported."
19335
19443
  ],
19336
19444
  examples: [
19337
19445
  {
19338
- id: "form-with-initial-values",
19339
- title: "Give the form an explicit initial shape",
19446
+ id: "controlled-field-with-form-defaults",
19447
+ title: "Initialize controlled fields at form level",
19340
19448
  outcome: "no-match",
19341
- files: [{ path: "profile-form.tsx", source: "'use client'; import { useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm({ defaultValues: { name: '' } }); return <input {...form.register('name')} />; }" }],
19449
+ files: [{ path: "profile-form.tsx", source: "'use client'; import { Controller, useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm({ defaultValues: { name: '' } }); return <Controller control={form.control} name='name' render={() => null} />; }" }],
19342
19450
  focusPath: "profile-form.tsx",
19343
19451
  expectedCount: 0,
19344
19452
  public: true
19345
19453
  },
19346
19454
  {
19347
- id: "form-without-initial-values",
19348
- title: "Do not leave form initialization implicit",
19455
+ id: "controlled-field-without-default",
19456
+ title: "Do not leave a controlled field uninitialized",
19349
19457
  outcome: "match",
19350
- files: [{ path: "profile-form.tsx", source: "'use client'; import { useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm({ mode: 'onChange' }); return <input {...form.register('name')} />; }" }],
19458
+ files: [{ path: "profile-form.tsx", source: "'use client'; import { Controller, useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm(); return <Controller control={form.control} name='name' render={() => null} />; }" }],
19351
19459
  focusPath: "profile-form.tsx",
19352
19460
  expectedCount: 1,
19353
19461
  public: true
19354
19462
  }
19355
19463
  ]
19356
19464
  };
19357
- function hasInitializationOrUnknownOptions(options) {
19358
- return options?.type === "ObjectExpression" && options.properties.some(
19359
- (property) => property.type === "SpreadElement" || property.computed || (property.key.type === "Identifier" && ["defaultValues", "values"].includes(property.key.name) || property.key.type === "Literal" && ["defaultValues", "values"].includes(String(property.key.value)))
19360
- );
19465
+ function staticPropertyName(property) {
19466
+ if (property.computed) return null;
19467
+ if (property.key.type === import_utils97.AST_NODE_TYPES.Identifier) return property.key.name;
19468
+ if (property.key.type === import_utils97.AST_NODE_TYPES.Literal && typeof property.key.value === "string") return property.key.value;
19469
+ return null;
19470
+ }
19471
+ function unwrapExpression6(node) {
19472
+ if (node.type === import_utils97.AST_NODE_TYPES.TSAsExpression || node.type === import_utils97.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils97.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils97.AST_NODE_TYPES.TSTypeAssertion) return unwrapExpression6(node.expression);
19473
+ return node;
19361
19474
  }
19362
19475
  var require_use_form_default_values_default = createRule({
19363
19476
  name: "require-use-form-default-values",
@@ -19366,27 +19479,127 @@ var require_use_form_default_values_default = createRule({
19366
19479
  type: "problem",
19367
19480
  docs: { description: REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION.summary },
19368
19481
  schema: [],
19369
- messages: {
19370
- requireUseFormDefaultValues: "Provide defaultValues or reactive values to useForm so controlled fields have an explicit initial shape."
19371
- }
19482
+ messages: { requireUseFormDefaultValues: "Add non-undefined defaultValues/values to the associated useForm call or non-undefined defaultValue to this field." }
19372
19483
  },
19373
19484
  defaultOptions: [],
19374
19485
  create(context) {
19375
- const importedHooks = /* @__PURE__ */ new Set();
19486
+ const imported = /* @__PURE__ */ new Map();
19487
+ const uninitializedForms = /* @__PURE__ */ new Set();
19488
+ const uninitializedControls = /* @__PURE__ */ new Set();
19489
+ const bindingNamed = (node, name) => import_utils97.ASTUtils.findVariable(context.sourceCode.getScope(node), name);
19490
+ const bindingOf = (node) => bindingNamed(node, node.name);
19491
+ const stable = (variable) => !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
19492
+ const importedKind = (node) => {
19493
+ const variable = bindingOf(node);
19494
+ return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
19495
+ };
19496
+ const importedJsxKind = (node) => {
19497
+ const variable = bindingNamed(node, node.name);
19498
+ return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
19499
+ };
19500
+ const isDefinitelyUndefined = (node) => {
19501
+ const value = unwrapExpression6(node);
19502
+ return value.type === import_utils97.AST_NODE_TYPES.UnaryExpression && value.operator === "void" || value.type === import_utils97.AST_NODE_TYPES.Identifier && value.name === "undefined" && (bindingOf(value)?.defs.length ?? 0) === 0;
19503
+ };
19504
+ const initializationState = (node) => {
19505
+ if (node === void 0) return "uninitialized";
19506
+ if (node.type !== import_utils97.AST_NODE_TYPES.ObjectExpression) return "unknown";
19507
+ const initialization = /* @__PURE__ */ new Map();
19508
+ for (const property of node.properties) {
19509
+ if (property.type === import_utils97.AST_NODE_TYPES.SpreadElement || property.computed) return "unknown";
19510
+ if (property.type !== import_utils97.AST_NODE_TYPES.Property) continue;
19511
+ const name = staticPropertyName(property);
19512
+ if (name === "defaultValues" || name === "values") initialization.set(name, !isDefinitelyUndefined(property.value));
19513
+ }
19514
+ return [...initialization.values()].some(Boolean) ? "initialized" : "uninitialized";
19515
+ };
19516
+ const uninitializedUseFormCall = (node) => {
19517
+ if (node.type !== import_utils97.AST_NODE_TYPES.CallExpression || node.callee.type !== import_utils97.AST_NODE_TYPES.Identifier || importedKind(node.callee) !== "useForm") return false;
19518
+ const options = node.arguments[0];
19519
+ return options?.type !== import_utils97.AST_NODE_TYPES.SpreadElement && initializationState(options) === "uninitialized";
19520
+ };
19521
+ const isUninitializedForm = (node) => {
19522
+ if (node.type !== import_utils97.AST_NODE_TYPES.Identifier) return false;
19523
+ const variable = bindingOf(node);
19524
+ return variable !== null && stable(variable) && uninitializedForms.has(variable);
19525
+ };
19526
+ const isUninitializedControl = (node) => {
19527
+ if (node.type === import_utils97.AST_NODE_TYPES.Identifier) {
19528
+ const variable = bindingOf(node);
19529
+ return variable !== null && stable(variable) && uninitializedControls.has(variable);
19530
+ }
19531
+ return node.type === import_utils97.AST_NODE_TYPES.MemberExpression && !node.computed && node.property.type === import_utils97.AST_NODE_TYPES.Identifier && node.property.name === "control" && isUninitializedForm(node.object);
19532
+ };
19533
+ const fieldOptionsNeedDefault = (node) => {
19534
+ if (node?.type !== import_utils97.AST_NODE_TYPES.ObjectExpression) return false;
19535
+ let control = null;
19536
+ let hasDefault = false;
19537
+ for (const property of node.properties) {
19538
+ if (property.type === import_utils97.AST_NODE_TYPES.SpreadElement || property.computed) return false;
19539
+ if (property.type !== import_utils97.AST_NODE_TYPES.Property) continue;
19540
+ const name = staticPropertyName(property);
19541
+ if (name === "control") control = property.value;
19542
+ if (name === "defaultValue") hasDefault = !isDefinitelyUndefined(property.value);
19543
+ }
19544
+ return control !== null && isUninitializedControl(control) && !hasDefault;
19545
+ };
19546
+ const jsxAttribute = (node, name) => {
19547
+ let result = null;
19548
+ for (const attribute of node.attributes) {
19549
+ if (attribute.type === import_utils97.AST_NODE_TYPES.JSXSpreadAttribute) return null;
19550
+ if (attribute.name.type === import_utils97.AST_NODE_TYPES.JSXIdentifier && attribute.name.name === name) result = attribute;
19551
+ }
19552
+ return result;
19553
+ };
19554
+ const trackDestructuredControl = (pattern) => {
19555
+ for (const property of pattern.properties) {
19556
+ if (property.type !== import_utils97.AST_NODE_TYPES.Property || staticPropertyName(property) !== "control" || property.value.type !== import_utils97.AST_NODE_TYPES.Identifier) continue;
19557
+ const variable = bindingOf(property.value);
19558
+ if (variable !== null) uninitializedControls.add(variable);
19559
+ }
19560
+ };
19376
19561
  return {
19377
19562
  ImportDeclaration(node) {
19378
19563
  if (node.source.value !== "react-hook-form") return;
19379
19564
  for (const specifier of node.specifiers) {
19380
- if (specifier.type !== "ImportSpecifier" || (specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value) !== "useForm") continue;
19381
- const variable = import_utils97.ASTUtils.findVariable(context.sourceCode.getScope(specifier.local), specifier.local.name);
19382
- if (variable) importedHooks.add(variable);
19565
+ if (specifier.type !== import_utils97.AST_NODE_TYPES.ImportSpecifier) continue;
19566
+ const name = specifier.imported.type === import_utils97.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
19567
+ if (name !== "Controller" && name !== "useController" && name !== "useForm") continue;
19568
+ const variable = bindingOf(specifier.local);
19569
+ if (variable !== null) imported.set(variable, name);
19570
+ }
19571
+ },
19572
+ VariableDeclarator(node) {
19573
+ if (node.init === null) return;
19574
+ if (uninitializedUseFormCall(node.init)) {
19575
+ if (node.id.type === import_utils97.AST_NODE_TYPES.Identifier) {
19576
+ const variable = bindingOf(node.id);
19577
+ if (variable !== null) uninitializedForms.add(variable);
19578
+ } else if (node.id.type === import_utils97.AST_NODE_TYPES.ObjectPattern) {
19579
+ trackDestructuredControl(node.id);
19580
+ }
19581
+ return;
19582
+ }
19583
+ if (node.id.type === import_utils97.AST_NODE_TYPES.ObjectPattern && isUninitializedForm(node.init)) {
19584
+ trackDestructuredControl(node.id);
19585
+ return;
19586
+ }
19587
+ if (node.id.type === import_utils97.AST_NODE_TYPES.Identifier && node.init.type === import_utils97.AST_NODE_TYPES.MemberExpression && !node.init.computed && node.init.property.type === import_utils97.AST_NODE_TYPES.Identifier && node.init.property.name === "control" && isUninitializedForm(node.init.object)) {
19588
+ const variable = bindingOf(node.id);
19589
+ if (variable !== null) uninitializedControls.add(variable);
19383
19590
  }
19384
19591
  },
19385
19592
  CallExpression(node) {
19386
- if (node.callee.type !== "Identifier") return;
19387
- const variable = import_utils97.ASTUtils.findVariable(context.sourceCode.getScope(node.callee), node.callee.name);
19388
- const options = node.arguments[0];
19389
- if (!variable || !importedHooks.has(variable) || options !== void 0 && options.type !== "ObjectExpression" || hasInitializationOrUnknownOptions(options)) return;
19593
+ if (node.callee.type !== import_utils97.AST_NODE_TYPES.Identifier || importedKind(node.callee) !== "useController" || !fieldOptionsNeedDefault(node.arguments[0]?.type === import_utils97.AST_NODE_TYPES.SpreadElement ? void 0 : node.arguments[0])) return;
19594
+ context.report({ node, messageId: "requireUseFormDefaultValues" });
19595
+ },
19596
+ JSXOpeningElement(node) {
19597
+ if (node.name.type !== import_utils97.AST_NODE_TYPES.JSXIdentifier || importedJsxKind(node.name) !== "Controller") return;
19598
+ if (node.attributes.some((attribute) => attribute.type === import_utils97.AST_NODE_TYPES.JSXSpreadAttribute)) return;
19599
+ const control = jsxAttribute(node, "control");
19600
+ if (control?.value?.type !== import_utils97.AST_NODE_TYPES.JSXExpressionContainer || control.value.expression.type === import_utils97.AST_NODE_TYPES.JSXEmptyExpression || !isUninitializedControl(control.value.expression)) return;
19601
+ const defaultValue = jsxAttribute(node, "defaultValue");
19602
+ if (defaultValue !== null && (defaultValue.value === null || defaultValue.value.type !== import_utils97.AST_NODE_TYPES.JSXExpressionContainer || defaultValue.value.expression.type !== import_utils97.AST_NODE_TYPES.JSXEmptyExpression && !isDefinitelyUndefined(defaultValue.value.expression))) return;
19390
19603
  context.report({ node, messageId: "requireUseFormDefaultValues" });
19391
19604
  }
19392
19605
  };
@@ -20260,7 +20473,7 @@ var stepdown_default = createRule({
20260
20473
 
20261
20474
  // src/rules/source-coupled-test.ts
20262
20475
  var import_utils102 = require("@typescript-eslint/utils");
20263
- var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc|py|[cm]?[jt]s)$/iu;
20476
+ var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc?|toml|py|[cm]?[jt]s)$/iu;
20264
20477
  var FS_MODULES = /* @__PURE__ */ new Set(["fs", "node:fs", "fs/promises", "node:fs/promises"]);
20265
20478
  var FS_READERS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
20266
20479
  var TEXT_TRANSFORMS = /* @__PURE__ */ new Set([
@@ -20297,6 +20510,8 @@ var EXPECT_MATCHERS = /* @__PURE__ */ new Set([
20297
20510
  ]);
20298
20511
  var EXPECT_MODIFIERS2 = /* @__PURE__ */ new Set(["not", "rejects", "resolves"]);
20299
20512
  var ASSERT_MATCHERS = /* @__PURE__ */ new Set(["deepEqual", "doesNotMatch", "equal", "match", "notDeepEqual", "notEqual", "notStrictEqual", "ok", "strictEqual"]);
20513
+ var EXPECT_MODULES = /* @__PURE__ */ new Set(["@jest/globals", "@playwright/test", "bun:test", "vitest"]);
20514
+ var ASSERT_MODULES = /* @__PURE__ */ new Set(["assert", "assert/strict", "node:assert", "node:assert/strict"]);
20300
20515
  var SOURCE_COUPLED_TEST_DOCUMENTATION = {
20301
20516
  summary: "Disallow raw repository source text as a test oracle; parse or execute the artifact instead.",
20302
20517
  rationale: "Substring and regex checks can pass on comments or unreachable configuration and fail after behavior-preserving formatting changes.",
@@ -20304,6 +20519,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
20304
20519
  category: "testing",
20305
20520
  limitations: [
20306
20521
  "The rule follows stable lexical bindings, static source paths, awaited reads, and common text operations. Reassigned bindings, dynamic paths, unknown path wrappers, iterator pipelines, and interprocedural flows remain unreported.",
20522
+ "Assertion roots are scope-resolved for supported test runners and Node assert imports; local or unknown helpers with assertion-like names are not inferred.",
20307
20523
  "When raw representation is genuinely the contract (for example a golden or compatibility sentinel), use an exact line suppression with the reason."
20308
20524
  ],
20309
20525
  examples: [
@@ -20327,7 +20543,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
20327
20543
  }
20328
20544
  ]
20329
20545
  };
20330
- function staticMemberName7(node) {
20546
+ function staticMemberName8(node) {
20331
20547
  if (!node.computed && node.property.type === import_utils102.AST_NODE_TYPES.Identifier) return node.property.name;
20332
20548
  if (node.computed && node.property.type === import_utils102.AST_NODE_TYPES.Literal && typeof node.property.value === "string") return node.property.value;
20333
20549
  return null;
@@ -20377,6 +20593,27 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
20377
20593
  const reportedOrigins = /* @__PURE__ */ new Set();
20378
20594
  const currentScope = () => scopes.at(-1) ?? scopes[0];
20379
20595
  const bindingOf = (node) => import_utils102.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
20596
+ const assertionKind = (node) => {
20597
+ const binding = bindingOf(node);
20598
+ if (binding === null || binding.defs.length === 0) {
20599
+ return node.name === "assert" || node.name === "expect" ? node.name : null;
20600
+ }
20601
+ for (const definition of binding.defs) {
20602
+ const specifier = definition.node;
20603
+ if (specifier.type !== import_utils102.AST_NODE_TYPES.ImportSpecifier && specifier.type !== import_utils102.AST_NODE_TYPES.ImportDefaultSpecifier && specifier.type !== import_utils102.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.parent.type !== import_utils102.AST_NODE_TYPES.ImportDeclaration) continue;
20604
+ const source = importSource(specifier.parent);
20605
+ if (source !== null && ASSERT_MODULES.has(source)) {
20606
+ if (specifier.type !== import_utils102.AST_NODE_TYPES.ImportSpecifier) return "assert";
20607
+ const imported2 = specifier.imported.type === import_utils102.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
20608
+ if (imported2 === "strict" || ASSERT_MATCHERS.has(imported2)) return "assert";
20609
+ continue;
20610
+ }
20611
+ if (source === null || !EXPECT_MODULES.has(source) || specifier.type !== import_utils102.AST_NODE_TYPES.ImportSpecifier) continue;
20612
+ const imported = specifier.imported.type === import_utils102.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
20613
+ if (imported === "assert" || imported === "expect") return imported;
20614
+ }
20615
+ return null;
20616
+ };
20380
20617
  const visible = (kind, node) => {
20381
20618
  const name2 = bindingOf(node);
20382
20619
  if (name2 === null || name2.references.some((reference) => reference.isWrite() && reference.init !== true)) return false;
@@ -20417,7 +20654,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
20417
20654
  return visible("fsReaders", callee) && sourcePath(current.arguments[0]);
20418
20655
  }
20419
20656
  if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return false;
20420
- const name2 = staticMemberName7(callee);
20657
+ const name2 = staticMemberName8(callee);
20421
20658
  const object = unwrap7(callee.object);
20422
20659
  return name2 !== null && FS_READERS.has(name2) && object.type === import_utils102.AST_NODE_TYPES.Identifier && visible("fsObjects", object) && sourcePath(current.arguments[0]);
20423
20660
  };
@@ -20426,11 +20663,11 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
20426
20663
  if (current.type === import_utils102.AST_NODE_TYPES.Identifier) return visibleRawOrigins(current);
20427
20664
  if (rawRead(current)) return /* @__PURE__ */ new Set([`${current.range[0]}:${current.range[1]}`]);
20428
20665
  if (current.type === import_utils102.AST_NODE_TYPES.BinaryExpression && current.operator === "+") return /* @__PURE__ */ new Set([...rawOrigins(current.left), ...rawOrigins(current.right)]);
20429
- if (current.type === import_utils102.AST_NODE_TYPES.MemberExpression && staticMemberName7(current) === "length") return rawOrigins(current.object);
20666
+ if (current.type === import_utils102.AST_NODE_TYPES.MemberExpression && staticMemberName8(current) === "length") return rawOrigins(current.object);
20430
20667
  if (current.type !== import_utils102.AST_NODE_TYPES.CallExpression) return /* @__PURE__ */ new Set();
20431
20668
  const callee = unwrap7(current.callee);
20432
20669
  if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return /* @__PURE__ */ new Set();
20433
- const name2 = staticMemberName7(callee);
20670
+ const name2 = staticMemberName8(callee);
20434
20671
  return name2 !== null && TEXT_TRANSFORMS.has(name2) ? rawOrigins(callee.object) : /* @__PURE__ */ new Set();
20435
20672
  };
20436
20673
  const evidenceOrigins = (node) => {
@@ -20442,26 +20679,26 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
20442
20679
  if (current.type !== import_utils102.AST_NODE_TYPES.CallExpression) return /* @__PURE__ */ new Set();
20443
20680
  const callee = unwrap7(current.callee);
20444
20681
  if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return /* @__PURE__ */ new Set();
20445
- const name2 = staticMemberName7(callee);
20682
+ const name2 = staticMemberName8(callee);
20446
20683
  if (name2 !== null && TEXT_PREDICATES.has(name2)) return rawOrigins(callee.object);
20447
20684
  if (name2 !== null && REGEXP_PREDICATES.has(name2)) return new Set(current.arguments.flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...rawOrigins(argument)]));
20448
20685
  return /* @__PURE__ */ new Set();
20449
20686
  };
20450
20687
  const rawAssertionOrigins = (node) => {
20451
20688
  const callee = unwrap7(node.callee);
20452
- if (callee.type === import_utils102.AST_NODE_TYPES.Identifier && callee.name === "assert") {
20689
+ if (callee.type === import_utils102.AST_NODE_TYPES.Identifier && assertionKind(callee) === "assert") {
20453
20690
  return new Set(node.arguments.flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
20454
20691
  }
20455
20692
  if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return /* @__PURE__ */ new Set();
20456
- const matcher = staticMemberName7(callee);
20693
+ const matcher = staticMemberName8(callee);
20457
20694
  if (matcher === null) return /* @__PURE__ */ new Set();
20458
20695
  let receiver = unwrap7(callee.object);
20459
- while (receiver.type === import_utils102.AST_NODE_TYPES.MemberExpression && EXPECT_MODIFIERS2.has(staticMemberName7(receiver) ?? "")) receiver = unwrap7(receiver.object);
20460
- if (receiver.type === import_utils102.AST_NODE_TYPES.CallExpression && receiver.callee.type === import_utils102.AST_NODE_TYPES.Identifier && receiver.callee.name === "expect") {
20696
+ while (receiver.type === import_utils102.AST_NODE_TYPES.MemberExpression && EXPECT_MODIFIERS2.has(staticMemberName8(receiver) ?? "")) receiver = unwrap7(receiver.object);
20697
+ if (receiver.type === import_utils102.AST_NODE_TYPES.CallExpression && receiver.callee.type === import_utils102.AST_NODE_TYPES.Identifier && assertionKind(receiver.callee) === "expect") {
20461
20698
  if (!EXPECT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
20462
20699
  return new Set([...receiver.arguments, ...node.arguments].flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
20463
20700
  }
20464
- if (receiver.type !== import_utils102.AST_NODE_TYPES.Identifier || receiver.name !== "assert" || !ASSERT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
20701
+ if (receiver.type !== import_utils102.AST_NODE_TYPES.Identifier || assertionKind(receiver) !== "assert" || !ASSERT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
20465
20702
  return new Set(node.arguments.flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
20466
20703
  };
20467
20704
  const declare = (node, state) => {
@@ -20555,13 +20792,13 @@ var source_coupled_test_default = createSourceCoupledRule(
20555
20792
  // src/rules/sole-export-matches-filename.ts
20556
20793
  var import_utils103 = require("@typescript-eslint/utils");
20557
20794
  var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
20558
- summary: "Match a module filename to its sole named public runtime export.",
20795
+ summary: "Make a module filename reflect its sole named public runtime export.",
20559
20796
  rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
20560
- remediation: "Rename the module stem to the kebab-case export name, or colocate genuinely related exports.",
20797
+ remediation: "Name the module for the exported responsibility, using either the full export name or a clear leading or trailing domain phrase; otherwise colocate genuinely related exports.",
20561
20798
  category: "maintainability",
20562
20799
  limitations: [
20563
20800
  "Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS, and re-exports are excluded.",
20564
- "The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker.",
20801
+ "The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker. A leading or trailing export-name phrase is accepted only at token boundaries; multi-token stems also tolerate established acronym spelling such as github versus GitHub.",
20565
20802
  "Exported destructuring patterns are excluded rather than undercounted as public exports."
20566
20803
  ],
20567
20804
  examples: [
@@ -20587,6 +20824,23 @@ var EXCLUDED_STEMS = /* @__PURE__ */ new Set([
20587
20824
  "util",
20588
20825
  "utils"
20589
20826
  ]);
20827
+ var WEAK_DOMAIN_TOKENS = /* @__PURE__ */ new Set([
20828
+ "adapter",
20829
+ "client",
20830
+ "config",
20831
+ "controller",
20832
+ "factory",
20833
+ "handler",
20834
+ "manager",
20835
+ "provider",
20836
+ "record",
20837
+ "repository",
20838
+ "router",
20839
+ "schema",
20840
+ "service",
20841
+ "store",
20842
+ "worker"
20843
+ ]);
20590
20844
  function stem3(filename) {
20591
20845
  const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
20592
20846
  return base.replace(/\.[cm]?[jt]sx?$/u, "").split(".")[0] ?? "";
@@ -20594,6 +20848,25 @@ function stem3(filename) {
20594
20848
  function kebabCase(name) {
20595
20849
  return name.replace(/([A-Z]{2,})([A-Z][a-z])/gu, "$1-$2").replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[^a-z0-9]+/giu, "-").replaceAll(/^-+|-+$/gu, "").toLowerCase();
20596
20850
  }
20851
+ function reflectsExportName(fileStem, exportedStem) {
20852
+ if (fileStem.startsWith("_")) return false;
20853
+ const visibleFileStem = fileStem.toLowerCase();
20854
+ const fileTokens = visibleFileStem.split("-").filter(Boolean);
20855
+ const exportTokens = exportedStem.split("-").filter(Boolean);
20856
+ if (fileTokens.length === 0 || exportTokens.length === 0) return false;
20857
+ if (fileTokens.length === 1) {
20858
+ const [token] = fileTokens;
20859
+ return token !== void 0 && token.length >= 4 && !WEAK_DOMAIN_TOKENS.has(token) && (exportTokens[0] === token || exportTokens.at(-1) === token);
20860
+ }
20861
+ const compactFile = fileTokens.join("");
20862
+ if (compactFile.length < 6) return false;
20863
+ const boundaryPhrases = /* @__PURE__ */ new Set();
20864
+ for (let index = 1; index <= exportTokens.length; index += 1) {
20865
+ boundaryPhrases.add(exportTokens.slice(0, index).join(""));
20866
+ boundaryPhrases.add(exportTokens.slice(-index).join(""));
20867
+ }
20868
+ return boundaryPhrases.has(compactFile);
20869
+ }
20597
20870
  function declarationExport(statement) {
20598
20871
  const declaration = statement.declaration;
20599
20872
  if (declaration === null || declaration.declare === true) return [];
@@ -20608,7 +20881,7 @@ var sole_export_matches_filename_default = createRule({
20608
20881
  documentation: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION,
20609
20882
  meta: {
20610
20883
  type: "suggestion",
20611
- docs: { description: "Match a module filename to its sole named public runtime export." },
20884
+ docs: { description: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION.summary },
20612
20885
  schema: [],
20613
20886
  messages: {
20614
20887
  matchSoleExport: "This module's sole runtime export is `{{exported}}`; rename the file stem to `{{expected}}`."
@@ -20665,7 +20938,7 @@ var sole_export_matches_filename_default = createRule({
20665
20938
  const exportedStem = kebabCase(only.name);
20666
20939
  if (exportedStem === "") return;
20667
20940
  const expected = `${fileStem.startsWith("_") ? "_" : ""}${exportedStem}`;
20668
- if (expected === fileStem.toLowerCase()) return;
20941
+ if (expected === fileStem.toLowerCase() || reflectsExportName(fileStem, exportedStem)) return;
20669
20942
  context.report({ node: only.node, messageId: "matchSoleExport", data: { exported: only.name, expected } });
20670
20943
  }
20671
20944
  };
@@ -20880,7 +21153,7 @@ var chainMemberNames2 = (node) => {
20880
21153
  names.reverse();
20881
21154
  return names;
20882
21155
  };
20883
- var unwrapExpression5 = (node) => {
21156
+ var unwrapExpression7 = (node) => {
20884
21157
  let current = node;
20885
21158
  while (current.type === import_utils104.AST_NODE_TYPES.TSAsExpression || current.type === import_utils104.AST_NODE_TYPES.TSSatisfiesExpression || current.type === import_utils104.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils104.AST_NODE_TYPES.TSTypeAssertion) {
20886
21159
  current = current.expression;
@@ -20931,7 +21204,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
20931
21204
  return binding !== null && schemaBindings.has(binding);
20932
21205
  }
20933
21206
  function isConfirmedSchema(expression) {
20934
- const init = unwrapExpression5(expression);
21207
+ const init = unwrapExpression7(expression);
20935
21208
  if (init.type === import_utils104.AST_NODE_TYPES.Identifier) return isSchemaBinding(init);
20936
21209
  if (init.type !== import_utils104.AST_NODE_TYPES.CallExpression || init.callee.type !== import_utils104.AST_NODE_TYPES.MemberExpression) {
20937
21210
  return false;
@@ -21161,7 +21434,7 @@ var RULES = {
21161
21434
  };
21162
21435
  var meta = {
21163
21436
  name: "@sarj/eslint-plugin",
21164
- version: "15.17.16"
21437
+ version: "15.17.17"
21165
21438
  };
21166
21439
  var APPLICATION_ONLY_RULES = [];
21167
21440
  var LIBRARY_IMPORT_POLICY = ["error", {