@sarj/eslint-plugin 15.17.7 → 15.17.8

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
@@ -10080,8 +10080,8 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
10080
10080
  rationale: "Wrapping a TypeScript enum preserves its emitted runtime object and duplicates the schema's value definition across two constructs.",
10081
10081
  remediation: "Pass string literals directly to `z.enum` and derive the TypeScript type with `z.infer`.",
10082
10082
  category: "maintainability",
10083
- autofix: "safe",
10084
- limitations: ["Automatic fixes are limited to inline object literals whose unique values are all string literals."],
10083
+ autofix: "none",
10084
+ limitations: ["Migration is manual: replacing an enum-like object with a value array changes the public schema.enum keys and can affect consumers."],
10085
10085
  examples: [
10086
10086
  {
10087
10087
  id: "zod-literal-enum",
@@ -10099,8 +10099,7 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
10099
10099
  files: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.nativeEnum({ Active: "active", Inactive: "inactive" });' }],
10100
10100
  focusPath: "src/status.ts",
10101
10101
  expectedCount: 1,
10102
- public: true,
10103
- fixedFiles: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.enum(["active", "inactive"]);' }]
10102
+ public: true
10104
10103
  }
10105
10104
  ]
10106
10105
  };
@@ -10125,26 +10124,6 @@ function unwrap2(node) {
10125
10124
  }
10126
10125
  return node;
10127
10126
  }
10128
- function stringValueTexts(node, sourceCode) {
10129
- const texts = [];
10130
- for (const prop of node.properties) {
10131
- if (prop.type !== import_utils56.AST_NODE_TYPES.Property) {
10132
- return null;
10133
- }
10134
- if (prop.computed || prop.shorthand || prop.method || prop.kind !== "init") {
10135
- return null;
10136
- }
10137
- const value = prop.value;
10138
- if (value.type !== import_utils56.AST_NODE_TYPES.Literal || typeof value.value !== "string") {
10139
- return null;
10140
- }
10141
- const text = sourceCode.getText(value);
10142
- if (!texts.includes(text)) {
10143
- texts.push(text);
10144
- }
10145
- }
10146
- return texts.length > 0 ? texts : null;
10147
- }
10148
10127
  function resolvesToLocalEnum(node, scope) {
10149
10128
  let current = scope;
10150
10129
  while (current !== null) {
@@ -10176,7 +10155,6 @@ var no_zod_native_enum_default = createRule({
10176
10155
  documentation: NO_ZOD_NATIVE_ENUM_DOCUMENTATION,
10177
10156
  meta: {
10178
10157
  type: "suggestion",
10179
- fixable: "code",
10180
10158
  docs: {
10181
10159
  description: 'Disallow `z.nativeEnum()` (and `z.enum()` over a TypeScript enum); use `z.enum(["a", "b"])` with a string-literal union instead.'
10182
10160
  },
@@ -10221,30 +10199,6 @@ var no_zod_native_enum_default = createRule({
10221
10199
  }
10222
10200
  return false;
10223
10201
  }
10224
- function buildFix(node) {
10225
- const callee = node.callee;
10226
- if (callee.type !== import_utils56.AST_NODE_TYPES.MemberExpression || callee.property.type !== import_utils56.AST_NODE_TYPES.Identifier) {
10227
- return null;
10228
- }
10229
- const arg = node.arguments[0];
10230
- if (arg === void 0 || node.arguments.length !== 1 || arg.type === import_utils56.AST_NODE_TYPES.SpreadElement) {
10231
- return null;
10232
- }
10233
- const inner = unwrap2(arg);
10234
- if (inner.type !== import_utils56.AST_NODE_TYPES.ObjectExpression) {
10235
- return null;
10236
- }
10237
- const values = stringValueTexts(inner, sourceCode);
10238
- if (values === null) {
10239
- return null;
10240
- }
10241
- const property = callee.property;
10242
- const replacementArg = `[${values.join(", ")}]`;
10243
- return (fixer) => [
10244
- fixer.replaceText(property, "enum"),
10245
- fixer.replaceText(arg, replacementArg)
10246
- ];
10247
- }
10248
10202
  return {
10249
10203
  ImportDeclaration(node) {
10250
10204
  if (!isZodModule2(node.source.value)) {
@@ -10265,11 +10219,9 @@ var no_zod_native_enum_default = createRule({
10265
10219
  },
10266
10220
  CallExpression(node) {
10267
10221
  if (isZodMemberCall(node, "nativeEnum")) {
10268
- const fix = buildFix(node);
10269
10222
  context.report({
10270
10223
  node,
10271
- messageId: "nativeEnum",
10272
- ...fix === null ? {} : { fix }
10224
+ messageId: "nativeEnum"
10273
10225
  });
10274
10226
  return;
10275
10227
  }
@@ -12876,11 +12828,11 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12876
12828
  rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
12877
12829
  remediation: "Replace the union with z.literal([value1, value2, ...]).",
12878
12830
  category: "maintainability",
12879
- autofix: "safe",
12831
+ autofix: "none",
12880
12832
  limitations: [
12881
12833
  "Bare zod imports are analyzed only when the rule option explicitly declares zodMajorVersion: 4; explicit zod/v4 entrypoints are self-declaring.",
12882
12834
  "All-string domains are left to zod/prefer-enum-over-literal-union.",
12883
- "A finding containing comments is not autofixed because moving its trivia is ambiguous."
12835
+ "Migration is manual: ZodLiteral and ZodUnion expose different introspection APIs and validation error shapes even when they accept the same values."
12884
12836
  ],
12885
12837
  examples: [
12886
12838
  {
@@ -12903,10 +12855,6 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12903
12855
  path: "src/schema.ts",
12904
12856
  source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
12905
12857
  }],
12906
- fixedFiles: [{
12907
- path: "src/schema.ts",
12908
- source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12909
- }],
12910
12858
  focusPath: "src/schema.ts",
12911
12859
  expectedCount: 1,
12912
12860
  public: true
@@ -12936,7 +12884,6 @@ var prefer_multi_value_zod_literal_default = createRule({
12936
12884
  documentation: PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION,
12937
12885
  meta: {
12938
12886
  type: "suggestion",
12939
- fixable: "code",
12940
12887
  docs: {
12941
12888
  description: "Use the Zod 4 multi-value literal API instead of a union of literal schemas."
12942
12889
  },
@@ -13000,15 +12947,10 @@ var prefer_multi_value_zod_literal_default = createRule({
13000
12947
  }
13001
12948
  if (values.every(isStaticString)) return;
13002
12949
  const namespace = node.callee.object.name;
13003
- const hasComments = context.sourceCode.getCommentsInside(node).length > 0;
13004
12950
  context.report({
13005
12951
  node,
13006
12952
  messageId: "useMultiValueLiteral",
13007
- data: { zod: namespace },
13008
- fix: hasComments ? null : (fixer) => fixer.replaceText(
13009
- node,
13010
- `${namespace}.literal([${values.map((value) => context.sourceCode.getText(value)).join(", ")}])`
13011
- )
12953
+ data: { zod: namespace }
13012
12954
  });
13013
12955
  }
13014
12956
  };
@@ -15439,13 +15381,14 @@ var MIN_RUN_LENGTH = 2;
15439
15381
  var PREFER_WHOLE_OBJECT_ASSERTION_DOCUMENTATION = {
15440
15382
  summary: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together.",
15441
15383
  rationale: "One whole-object assertion presents related expectations together and produces a complete structural diff.",
15442
- remediation: "Replace consecutive member assertions with one `toMatchObject` assertion.",
15384
+ remediation: "Consider one `toMatchObject` assertion for ordinary data objects. Preserve missing-property checks, identity, and getter or proxy behavior when deciding whether to combine assertions.",
15443
15385
  category: "testing",
15444
15386
  aliases: ["strict-test-assertions"],
15445
- autofix: "safe",
15387
+ autofix: "none",
15388
+ limitations: ["No automatic rewrite: whole-object matching can require previously absent properties and change observable getter or proxy reads."],
15446
15389
  examples: [
15447
15390
  { id: "whole-object", title: "Assert the object once", outcome: "no-match", files: [{ path: "src/user.test.ts", source: "expect(user).toMatchObject({ id: 1, name: 'Ada' });" }], focusPath: "src/user.test.ts", expectedCount: 0, public: true },
15448
- { id: "member-run", title: "Do not split one object across assertions", outcome: "match", files: [{ path: "src/user.test.ts", source: "expect(user.id).toBe(1);\nexpect(user.name).toBe('Ada');" }], focusPath: "src/user.test.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/user.test.ts", source: "expect(user).toMatchObject({ id: 1, name: 'Ada' });\n" }] }
15391
+ { id: "member-run", title: "Consider grouping related data properties", outcome: "match", files: [{ path: "src/user.test.ts", source: "expect(user.id).toBe(1);\nexpect(user.name).toBe('Ada');" }], focusPath: "src/user.test.ts", expectedCount: 1, public: true }
15449
15392
  ]
15450
15393
  };
15451
15394
  function literalText(node, getText) {
@@ -15501,9 +15444,8 @@ var prefer_whole_object_assertion_default = createRule({
15501
15444
  docs: {
15502
15445
  description: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together."
15503
15446
  },
15504
- fixable: "code",
15505
15447
  messages: {
15506
- combineAssertions: "These {{count}} assertions each check one property of `{{receiver}}` against a literal, so the first mismatch hides the rest. Assert the object once: `expect({{receiver}}).toMatchObject({ \u2026 })`.",
15448
+ combineAssertions: "Consider combining these {{count}} assertions on `{{receiver}}` with `toMatchObject` when structural matching preserves property presence and getter or proxy behavior.",
15507
15449
  assertArrayOnce: "These {{count}} assertions check `{{receiver}}[0]`\u2026`{{receiver}}[{{last}}]` one at a time, which never checks how long `{{receiver}}` is \u2014 extra elements pass unnoticed. Assert the array once: `expect({{receiver}}).{{matcher}}([ \u2026 ])`."
15508
15450
  },
15509
15451
  schema: []
@@ -15574,11 +15516,6 @@ var prefer_whole_object_assertion_default = createRule({
15574
15516
  expectedIsLiteral: literal !== null
15575
15517
  };
15576
15518
  }
15577
- function hasInterveningComment(run) {
15578
- return run.some(
15579
- (assertion, index) => sourceCode.getCommentsInside(assertion.statement).length > 0 || index > 0 && sourceCode.getCommentsBefore(assertion.statement).length > 0
15580
- );
15581
- }
15582
15519
  function reportPropertyRun(run) {
15583
15520
  const tree = /* @__PURE__ */ new Map();
15584
15521
  const paths = [];
@@ -15625,16 +15562,10 @@ var prefer_whole_object_assertion_default = createRule({
15625
15562
  return;
15626
15563
  }
15627
15564
  const receiverText = `${sourceCode.getText(first.receiver)}${commonPrefix.map((name) => `.${name}`).join("")}`;
15628
- const renderTree = (value) => [...value.entries()].map(([name, child]) => `${name}: ${child instanceof Map ? `{ ${renderTree(child)} }` : child}`).join(", ");
15629
- const properties = renderTree(tree);
15630
15565
  context.report({
15631
15566
  node: first.statement,
15632
15567
  messageId: "combineAssertions",
15633
- data: { count: String(run.length), receiver: receiverText },
15634
- fix: hasInterveningComment(run) ? null : (fixer) => [
15635
- fixer.replaceText(first.statement, `expect(${receiverText}).toMatchObject({ ${properties} });`),
15636
- ...run.slice(1).map((assertion) => fixer.remove(assertion.statement))
15637
- ]
15568
+ data: { count: String(run.length), receiver: receiverText }
15638
15569
  });
15639
15570
  }
15640
15571
  function reportIndexRun(run) {
@@ -19758,7 +19689,7 @@ var RULES = {
19758
19689
  };
19759
19690
  var meta = {
19760
19691
  name: "@sarj/eslint-plugin",
19761
- version: "15.17.7"
19692
+ version: "15.17.8"
19762
19693
  };
19763
19694
  var APPLICATION_ONLY_RULES = [];
19764
19695
  var LIBRARY_IMPORT_POLICY = ["error", {
@@ -19780,6 +19711,7 @@ var ADVISORY_RULES = [
19780
19711
  "@sarj/prefer-nullish-filter-predicate",
19781
19712
  "@sarj/prefer-shared-zod-enum",
19782
19713
  "@sarj/prefer-switch-for-repeated-equality",
19714
+ "@sarj/prefer-whole-object-assertion",
19783
19715
  "@sarj/require-interface-for-exported-class",
19784
19716
  "@sarj/require-sql-access-class",
19785
19717
  "@sarj/sole-export-matches-filename"
@@ -19858,7 +19790,7 @@ var RECOMMENDED_RULES = {
19858
19790
  "@sarj/prefer-switch-for-repeated-equality": "warn",
19859
19791
  "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
19860
19792
  "@sarj/prefer-server-actions": "error",
19861
- "@sarj/prefer-whole-object-assertion": "error",
19793
+ "@sarj/prefer-whole-object-assertion": "warn",
19862
19794
  "@sarj/repeated-static-call-cases": "error",
19863
19795
  "@sarj/prefer-zod-infer": "error",
19864
19796
  "@sarj/require-assert-never": "error",
@@ -19955,7 +19887,7 @@ var STRICT_RULES = {
19955
19887
  "@sarj/prefer-switch-for-repeated-equality": "warn",
19956
19888
  "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
19957
19889
  "@sarj/prefer-server-actions": "error",
19958
- "@sarj/prefer-whole-object-assertion": "error",
19890
+ "@sarj/prefer-whole-object-assertion": "warn",
19959
19891
  "@sarj/repeated-static-call-cases": "error",
19960
19892
  "@sarj/prefer-zod-infer": "error",
19961
19893
  "@sarj/require-assert-never": "error",
package/dist/index.d.cts CHANGED
@@ -320,7 +320,7 @@ declare const RULES: {
320
320
  /** @deprecated All repositories use one policy; retained for import compatibility. */
321
321
  declare const APPLICATION_ONLY_RULES: readonly [];
322
322
  /** Rules staged as non-blocking warnings while corpus adoption evidence accumulates. */
323
- declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
323
+ declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/prefer-whole-object-assertion", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
324
324
  declare const RECOMMENDED_RULES: {
325
325
  readonly "no-restricted-imports": readonly ["error", {
326
326
  readonly paths: {
@@ -582,7 +582,7 @@ declare const RECOMMENDED_RULES: {
582
582
  readonly requireSemanticTokens: true;
583
583
  }];
584
584
  readonly "@sarj/prefer-server-actions": "error";
585
- readonly "@sarj/prefer-whole-object-assertion": "error";
585
+ readonly "@sarj/prefer-whole-object-assertion": "warn";
586
586
  readonly "@sarj/repeated-static-call-cases": "error";
587
587
  readonly "@sarj/prefer-zod-infer": "error";
588
588
  readonly "@sarj/require-assert-never": "error";
@@ -866,7 +866,7 @@ declare const STRICT_RULES: {
866
866
  readonly requireSemanticTokens: true;
867
867
  }];
868
868
  readonly "@sarj/prefer-server-actions": "error";
869
- readonly "@sarj/prefer-whole-object-assertion": "error";
869
+ readonly "@sarj/prefer-whole-object-assertion": "warn";
870
870
  readonly "@sarj/repeated-static-call-cases": "error";
871
871
  readonly "@sarj/prefer-zod-infer": "error";
872
872
  readonly "@sarj/require-assert-never": "error";
@@ -893,7 +893,7 @@ type FlatPreset = {
893
893
  declare const PLUGIN: {
894
894
  readonly meta: {
895
895
  readonly name: "@sarj/eslint-plugin";
896
- readonly version: "15.17.7";
896
+ readonly version: "15.17.8";
897
897
  };
898
898
  readonly rules: {
899
899
  readonly "excessive-commentary": DocumentedRule<readonly [], "excessive">;
package/dist/index.d.ts CHANGED
@@ -320,7 +320,7 @@ declare const RULES: {
320
320
  /** @deprecated All repositories use one policy; retained for import compatibility. */
321
321
  declare const APPLICATION_ONLY_RULES: readonly [];
322
322
  /** Rules staged as non-blocking warnings while corpus adoption evidence accumulates. */
323
- declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
323
+ declare const ADVISORY_RULES: readonly ["@sarj/excessive-commentary", "@sarj/no-bespoke-api-case-conversion", "@sarj/no-restated-comment", "@sarj/no-restated-jsdoc", "@sarj/prefer-millisecond-control-duration-schema", "@sarj/prefer-module-level-refined-schema", "@sarj/prefer-multi-value-zod-literal", "@sarj/prefer-named-callback-domain", "@sarj/prefer-named-complex-return-type", "@sarj/prefer-node-crypto-hash", "@sarj/prefer-node-fs-promises", "@sarj/prefer-nullish-filter-predicate", "@sarj/prefer-shared-zod-enum", "@sarj/prefer-switch-for-repeated-equality", "@sarj/prefer-whole-object-assertion", "@sarj/require-interface-for-exported-class", "@sarj/require-sql-access-class", "@sarj/sole-export-matches-filename"];
324
324
  declare const RECOMMENDED_RULES: {
325
325
  readonly "no-restricted-imports": readonly ["error", {
326
326
  readonly paths: {
@@ -582,7 +582,7 @@ declare const RECOMMENDED_RULES: {
582
582
  readonly requireSemanticTokens: true;
583
583
  }];
584
584
  readonly "@sarj/prefer-server-actions": "error";
585
- readonly "@sarj/prefer-whole-object-assertion": "error";
585
+ readonly "@sarj/prefer-whole-object-assertion": "warn";
586
586
  readonly "@sarj/repeated-static-call-cases": "error";
587
587
  readonly "@sarj/prefer-zod-infer": "error";
588
588
  readonly "@sarj/require-assert-never": "error";
@@ -866,7 +866,7 @@ declare const STRICT_RULES: {
866
866
  readonly requireSemanticTokens: true;
867
867
  }];
868
868
  readonly "@sarj/prefer-server-actions": "error";
869
- readonly "@sarj/prefer-whole-object-assertion": "error";
869
+ readonly "@sarj/prefer-whole-object-assertion": "warn";
870
870
  readonly "@sarj/repeated-static-call-cases": "error";
871
871
  readonly "@sarj/prefer-zod-infer": "error";
872
872
  readonly "@sarj/require-assert-never": "error";
@@ -893,7 +893,7 @@ type FlatPreset = {
893
893
  declare const PLUGIN: {
894
894
  readonly meta: {
895
895
  readonly name: "@sarj/eslint-plugin";
896
- readonly version: "15.17.7";
896
+ readonly version: "15.17.8";
897
897
  };
898
898
  readonly rules: {
899
899
  readonly "excessive-commentary": DocumentedRule<readonly [], "excessive">;
package/dist/index.js CHANGED
@@ -10046,8 +10046,8 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
10046
10046
  rationale: "Wrapping a TypeScript enum preserves its emitted runtime object and duplicates the schema's value definition across two constructs.",
10047
10047
  remediation: "Pass string literals directly to `z.enum` and derive the TypeScript type with `z.infer`.",
10048
10048
  category: "maintainability",
10049
- autofix: "safe",
10050
- limitations: ["Automatic fixes are limited to inline object literals whose unique values are all string literals."],
10049
+ autofix: "none",
10050
+ limitations: ["Migration is manual: replacing an enum-like object with a value array changes the public schema.enum keys and can affect consumers."],
10051
10051
  examples: [
10052
10052
  {
10053
10053
  id: "zod-literal-enum",
@@ -10065,8 +10065,7 @@ var NO_ZOD_NATIVE_ENUM_DOCUMENTATION = {
10065
10065
  files: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.nativeEnum({ Active: "active", Inactive: "inactive" });' }],
10066
10066
  focusPath: "src/status.ts",
10067
10067
  expectedCount: 1,
10068
- public: true,
10069
- fixedFiles: [{ path: "src/status.ts", source: 'import { z } from "zod"; const S = z.enum(["active", "inactive"]);' }]
10068
+ public: true
10070
10069
  }
10071
10070
  ]
10072
10071
  };
@@ -10091,26 +10090,6 @@ function unwrap2(node) {
10091
10090
  }
10092
10091
  return node;
10093
10092
  }
10094
- function stringValueTexts(node, sourceCode) {
10095
- const texts = [];
10096
- for (const prop of node.properties) {
10097
- if (prop.type !== AST_NODE_TYPES41.Property) {
10098
- return null;
10099
- }
10100
- if (prop.computed || prop.shorthand || prop.method || prop.kind !== "init") {
10101
- return null;
10102
- }
10103
- const value = prop.value;
10104
- if (value.type !== AST_NODE_TYPES41.Literal || typeof value.value !== "string") {
10105
- return null;
10106
- }
10107
- const text = sourceCode.getText(value);
10108
- if (!texts.includes(text)) {
10109
- texts.push(text);
10110
- }
10111
- }
10112
- return texts.length > 0 ? texts : null;
10113
- }
10114
10093
  function resolvesToLocalEnum(node, scope) {
10115
10094
  let current = scope;
10116
10095
  while (current !== null) {
@@ -10142,7 +10121,6 @@ var no_zod_native_enum_default = createRule({
10142
10121
  documentation: NO_ZOD_NATIVE_ENUM_DOCUMENTATION,
10143
10122
  meta: {
10144
10123
  type: "suggestion",
10145
- fixable: "code",
10146
10124
  docs: {
10147
10125
  description: 'Disallow `z.nativeEnum()` (and `z.enum()` over a TypeScript enum); use `z.enum(["a", "b"])` with a string-literal union instead.'
10148
10126
  },
@@ -10187,30 +10165,6 @@ var no_zod_native_enum_default = createRule({
10187
10165
  }
10188
10166
  return false;
10189
10167
  }
10190
- function buildFix(node) {
10191
- const callee = node.callee;
10192
- if (callee.type !== AST_NODE_TYPES41.MemberExpression || callee.property.type !== AST_NODE_TYPES41.Identifier) {
10193
- return null;
10194
- }
10195
- const arg = node.arguments[0];
10196
- if (arg === void 0 || node.arguments.length !== 1 || arg.type === AST_NODE_TYPES41.SpreadElement) {
10197
- return null;
10198
- }
10199
- const inner = unwrap2(arg);
10200
- if (inner.type !== AST_NODE_TYPES41.ObjectExpression) {
10201
- return null;
10202
- }
10203
- const values = stringValueTexts(inner, sourceCode);
10204
- if (values === null) {
10205
- return null;
10206
- }
10207
- const property = callee.property;
10208
- const replacementArg = `[${values.join(", ")}]`;
10209
- return (fixer) => [
10210
- fixer.replaceText(property, "enum"),
10211
- fixer.replaceText(arg, replacementArg)
10212
- ];
10213
- }
10214
10168
  return {
10215
10169
  ImportDeclaration(node) {
10216
10170
  if (!isZodModule2(node.source.value)) {
@@ -10231,11 +10185,9 @@ var no_zod_native_enum_default = createRule({
10231
10185
  },
10232
10186
  CallExpression(node) {
10233
10187
  if (isZodMemberCall(node, "nativeEnum")) {
10234
- const fix = buildFix(node);
10235
10188
  context.report({
10236
10189
  node,
10237
- messageId: "nativeEnum",
10238
- ...fix === null ? {} : { fix }
10190
+ messageId: "nativeEnum"
10239
10191
  });
10240
10192
  return;
10241
10193
  }
@@ -12854,11 +12806,11 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12854
12806
  rationale: "One multi-value literal expresses the same closed value domain without repeated schema wrappers.",
12855
12807
  remediation: "Replace the union with z.literal([value1, value2, ...]).",
12856
12808
  category: "maintainability",
12857
- autofix: "safe",
12809
+ autofix: "none",
12858
12810
  limitations: [
12859
12811
  "Bare zod imports are analyzed only when the rule option explicitly declares zodMajorVersion: 4; explicit zod/v4 entrypoints are self-declaring.",
12860
12812
  "All-string domains are left to zod/prefer-enum-over-literal-union.",
12861
- "A finding containing comments is not autofixed because moving its trivia is ambiguous."
12813
+ "Migration is manual: ZodLiteral and ZodUnion expose different introspection APIs and validation error shapes even when they accept the same values."
12862
12814
  ],
12863
12815
  examples: [
12864
12816
  {
@@ -12881,10 +12833,6 @@ var PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION = {
12881
12833
  path: "src/schema.ts",
12882
12834
  source: "import { z } from 'zod'; export const Version = z.union([z.literal(1), z.literal(2), z.literal(3)]);"
12883
12835
  }],
12884
- fixedFiles: [{
12885
- path: "src/schema.ts",
12886
- source: "import { z } from 'zod'; export const Version = z.literal([1, 2, 3]);"
12887
- }],
12888
12836
  focusPath: "src/schema.ts",
12889
12837
  expectedCount: 1,
12890
12838
  public: true
@@ -12914,7 +12862,6 @@ var prefer_multi_value_zod_literal_default = createRule({
12914
12862
  documentation: PREFER_MULTI_VALUE_ZOD_LITERAL_DOCUMENTATION,
12915
12863
  meta: {
12916
12864
  type: "suggestion",
12917
- fixable: "code",
12918
12865
  docs: {
12919
12866
  description: "Use the Zod 4 multi-value literal API instead of a union of literal schemas."
12920
12867
  },
@@ -12978,15 +12925,10 @@ var prefer_multi_value_zod_literal_default = createRule({
12978
12925
  }
12979
12926
  if (values.every(isStaticString)) return;
12980
12927
  const namespace = node.callee.object.name;
12981
- const hasComments = context.sourceCode.getCommentsInside(node).length > 0;
12982
12928
  context.report({
12983
12929
  node,
12984
12930
  messageId: "useMultiValueLiteral",
12985
- data: { zod: namespace },
12986
- fix: hasComments ? null : (fixer) => fixer.replaceText(
12987
- node,
12988
- `${namespace}.literal([${values.map((value) => context.sourceCode.getText(value)).join(", ")}])`
12989
- )
12931
+ data: { zod: namespace }
12990
12932
  });
12991
12933
  }
12992
12934
  };
@@ -15425,13 +15367,14 @@ var MIN_RUN_LENGTH = 2;
15425
15367
  var PREFER_WHOLE_OBJECT_ASSERTION_DOCUMENTATION = {
15426
15368
  summary: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together.",
15427
15369
  rationale: "One whole-object assertion presents related expectations together and produces a complete structural diff.",
15428
- remediation: "Replace consecutive member assertions with one `toMatchObject` assertion.",
15370
+ remediation: "Consider one `toMatchObject` assertion for ordinary data objects. Preserve missing-property checks, identity, and getter or proxy behavior when deciding whether to combine assertions.",
15429
15371
  category: "testing",
15430
15372
  aliases: ["strict-test-assertions"],
15431
- autofix: "safe",
15373
+ autofix: "none",
15374
+ limitations: ["No automatic rewrite: whole-object matching can require previously absent properties and change observable getter or proxy reads."],
15432
15375
  examples: [
15433
15376
  { id: "whole-object", title: "Assert the object once", outcome: "no-match", files: [{ path: "src/user.test.ts", source: "expect(user).toMatchObject({ id: 1, name: 'Ada' });" }], focusPath: "src/user.test.ts", expectedCount: 0, public: true },
15434
- { id: "member-run", title: "Do not split one object across assertions", outcome: "match", files: [{ path: "src/user.test.ts", source: "expect(user.id).toBe(1);\nexpect(user.name).toBe('Ada');" }], focusPath: "src/user.test.ts", expectedCount: 1, public: true, fixedFiles: [{ path: "src/user.test.ts", source: "expect(user).toMatchObject({ id: 1, name: 'Ada' });\n" }] }
15377
+ { id: "member-run", title: "Consider grouping related data properties", outcome: "match", files: [{ path: "src/user.test.ts", source: "expect(user.id).toBe(1);\nexpect(user.name).toBe('Ada');" }], focusPath: "src/user.test.ts", expectedCount: 1, public: true }
15435
15378
  ]
15436
15379
  };
15437
15380
  function literalText(node, getText) {
@@ -15487,9 +15430,8 @@ var prefer_whole_object_assertion_default = createRule({
15487
15430
  docs: {
15488
15431
  description: "Collapse consecutive assertions on one object into a whole-object assertion so related mismatches are reported together."
15489
15432
  },
15490
- fixable: "code",
15491
15433
  messages: {
15492
- combineAssertions: "These {{count}} assertions each check one property of `{{receiver}}` against a literal, so the first mismatch hides the rest. Assert the object once: `expect({{receiver}}).toMatchObject({ \u2026 })`.",
15434
+ combineAssertions: "Consider combining these {{count}} assertions on `{{receiver}}` with `toMatchObject` when structural matching preserves property presence and getter or proxy behavior.",
15493
15435
  assertArrayOnce: "These {{count}} assertions check `{{receiver}}[0]`\u2026`{{receiver}}[{{last}}]` one at a time, which never checks how long `{{receiver}}` is \u2014 extra elements pass unnoticed. Assert the array once: `expect({{receiver}}).{{matcher}}([ \u2026 ])`."
15494
15436
  },
15495
15437
  schema: []
@@ -15560,11 +15502,6 @@ var prefer_whole_object_assertion_default = createRule({
15560
15502
  expectedIsLiteral: literal !== null
15561
15503
  };
15562
15504
  }
15563
- function hasInterveningComment(run) {
15564
- return run.some(
15565
- (assertion, index) => sourceCode.getCommentsInside(assertion.statement).length > 0 || index > 0 && sourceCode.getCommentsBefore(assertion.statement).length > 0
15566
- );
15567
- }
15568
15505
  function reportPropertyRun(run) {
15569
15506
  const tree = /* @__PURE__ */ new Map();
15570
15507
  const paths = [];
@@ -15611,16 +15548,10 @@ var prefer_whole_object_assertion_default = createRule({
15611
15548
  return;
15612
15549
  }
15613
15550
  const receiverText = `${sourceCode.getText(first.receiver)}${commonPrefix.map((name) => `.${name}`).join("")}`;
15614
- const renderTree = (value) => [...value.entries()].map(([name, child]) => `${name}: ${child instanceof Map ? `{ ${renderTree(child)} }` : child}`).join(", ");
15615
- const properties = renderTree(tree);
15616
15551
  context.report({
15617
15552
  node: first.statement,
15618
15553
  messageId: "combineAssertions",
15619
- data: { count: String(run.length), receiver: receiverText },
15620
- fix: hasInterveningComment(run) ? null : (fixer) => [
15621
- fixer.replaceText(first.statement, `expect(${receiverText}).toMatchObject({ ${properties} });`),
15622
- ...run.slice(1).map((assertion) => fixer.remove(assertion.statement))
15623
- ]
15554
+ data: { count: String(run.length), receiver: receiverText }
15624
15555
  });
15625
15556
  }
15626
15557
  function reportIndexRun(run) {
@@ -19753,7 +19684,7 @@ var RULES = {
19753
19684
  };
19754
19685
  var meta = {
19755
19686
  name: "@sarj/eslint-plugin",
19756
- version: "15.17.7"
19687
+ version: "15.17.8"
19757
19688
  };
19758
19689
  var APPLICATION_ONLY_RULES = [];
19759
19690
  var LIBRARY_IMPORT_POLICY = ["error", {
@@ -19775,6 +19706,7 @@ var ADVISORY_RULES = [
19775
19706
  "@sarj/prefer-nullish-filter-predicate",
19776
19707
  "@sarj/prefer-shared-zod-enum",
19777
19708
  "@sarj/prefer-switch-for-repeated-equality",
19709
+ "@sarj/prefer-whole-object-assertion",
19778
19710
  "@sarj/require-interface-for-exported-class",
19779
19711
  "@sarj/require-sql-access-class",
19780
19712
  "@sarj/sole-export-matches-filename"
@@ -19853,7 +19785,7 @@ var RECOMMENDED_RULES = {
19853
19785
  "@sarj/prefer-switch-for-repeated-equality": "warn",
19854
19786
  "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
19855
19787
  "@sarj/prefer-server-actions": "error",
19856
- "@sarj/prefer-whole-object-assertion": "error",
19788
+ "@sarj/prefer-whole-object-assertion": "warn",
19857
19789
  "@sarj/repeated-static-call-cases": "error",
19858
19790
  "@sarj/prefer-zod-infer": "error",
19859
19791
  "@sarj/require-assert-never": "error",
@@ -19950,7 +19882,7 @@ var STRICT_RULES = {
19950
19882
  "@sarj/prefer-switch-for-repeated-equality": "warn",
19951
19883
  "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
19952
19884
  "@sarj/prefer-server-actions": "error",
19953
- "@sarj/prefer-whole-object-assertion": "error",
19885
+ "@sarj/prefer-whole-object-assertion": "warn",
19954
19886
  "@sarj/repeated-static-call-cases": "error",
19955
19887
  "@sarj/prefer-zod-infer": "error",
19956
19888
  "@sarj/require-assert-never": "error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "15.17.7",
3
+ "version": "15.17.8",
4
4
  "packageManager": "npm@12.0.2",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",