@sarj/eslint-plugin 15.13.2 → 15.13.3

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
@@ -3720,8 +3720,6 @@ var no_json_stringify_error_default = createRule({
3720
3720
  var import_utils18 = require("@typescript-eslint/utils");
3721
3721
 
3722
3722
  // src/rules/_zod.ts
3723
- var ZOD_PREFIX_RE = /^Z[A-Z]/;
3724
- var ZOD_SUFFIX_RE = /Schema$/;
3725
3723
  var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
3726
3724
  function isZodModule(source) {
3727
3725
  return /(^|[/@-])zod([/-]|$)/.test(source);
@@ -16683,24 +16681,26 @@ var iac_source_coupled_test_default = createSourceCoupledRule(
16683
16681
  IAC_SOURCE_SUFFIX_RE
16684
16682
  );
16685
16683
 
16686
- // src/rules/zod-naming-convention.ts
16684
+ // src/rules/require-pascal-case-zod-schema-name.ts
16687
16685
  var import_utils85 = require("@typescript-eslint/utils");
16688
- var ZOD_NAMING_CONVENTION_DOCUMENTATION = {
16689
- summary: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.",
16690
- rationale: "A recognizable schema name distinguishes runtime validators from ordinary values at each use site.",
16691
- remediation: "Rename the schema with a `Z` prefix or `Schema` suffix, according to the configured convention.",
16686
+ var REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION = {
16687
+ summary: "Require confirmed module-level Zod schema contracts to use PascalCase with a `Schema` suffix.",
16688
+ rationale: "A reusable Zod schema is a runtime type contract. PascalCase mirrors that role; SCREAMING_SNAKE_CASE should remain reserved for scalar values and lookup tables.",
16689
+ remediation: "Rename the binding to PascalCase ending in `Schema` (for example, `MutationRouteBaseSchema`).",
16692
16690
  category: "style",
16691
+ aliases: ["zod-naming-convention"],
16692
+ autofix: "none",
16693
+ limitations: [
16694
+ "Only module-level bindings proven from a Zod import or a same-file proven schema are checked.",
16695
+ "Tests, benchmarks, generated files, imported schemas, re-export aliases, and arbitrary wrapper-factory results are excluded.",
16696
+ "Cross-file and exported renames are not safely file-local, so the rule has no autofix."
16697
+ ],
16693
16698
  examples: [
16694
- { id: "recognizable-schema-name", title: "Mark the value as a schema", outcome: "no-match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nconst userSchema = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 0, public: true },
16695
- { id: "unmarked-schema-name", title: "Do not hide the schema behind a value name", outcome: "match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nconst user = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 1, public: true }
16699
+ { id: "runtime-type-schema-name", title: "Name a reusable schema like a runtime type contract", outcome: "no-match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nexport const UserSchema = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 0, public: true },
16700
+ { id: "screaming-schema-name", title: "Do not name a schema like a scalar constant", outcome: "match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nexport const USER_SCHEMA = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 1, public: true }
16696
16701
  ]
16697
16702
  };
16698
- var CONVENTIONS = {
16699
- prefix: { test: ZOD_PREFIX_RE, messageId: "zPrefix" },
16700
- suffix: { test: ZOD_SUFFIX_RE, messageId: "schemaSuffix" },
16701
- either: { test: ZOD_SCHEMA_NAME_RE, messageId: "zodSchemaName" }
16702
- };
16703
- var CONTAINS_SCHEMA_RE = /schema/i;
16703
+ var PASCAL_SCHEMA_NAME_RE = /^[A-Z][A-Za-z0-9]*Schema$/;
16704
16704
  var BENCHMARK_PATH_RE = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
16705
16705
  var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
16706
16706
  "parse",
@@ -16725,6 +16725,95 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
16725
16725
  "prettifyError",
16726
16726
  "treeifyError"
16727
16727
  ]);
16728
+ var ZOD_SCHEMA_FACTORIES = /* @__PURE__ */ new Set([
16729
+ "any",
16730
+ "array",
16731
+ "base64",
16732
+ "base64url",
16733
+ "bigint",
16734
+ "boolean",
16735
+ "cidrv4",
16736
+ "cidrv6",
16737
+ "codec",
16738
+ "custom",
16739
+ "date",
16740
+ "discriminatedUnion",
16741
+ "email",
16742
+ "emoji",
16743
+ "enum",
16744
+ "file",
16745
+ "function",
16746
+ "hash",
16747
+ "hex",
16748
+ "hostname",
16749
+ "instanceof",
16750
+ "intersection",
16751
+ "ipv4",
16752
+ "ipv6",
16753
+ "json",
16754
+ "jwt",
16755
+ "lazy",
16756
+ "literal",
16757
+ "looseObject",
16758
+ "map",
16759
+ "nan",
16760
+ "nativeEnum",
16761
+ "never",
16762
+ "null",
16763
+ "nullable",
16764
+ "nullish",
16765
+ "number",
16766
+ "object",
16767
+ "optional",
16768
+ "partialRecord",
16769
+ "preprocess",
16770
+ "promise",
16771
+ "record",
16772
+ "set",
16773
+ "strictObject",
16774
+ "string",
16775
+ "stringbool",
16776
+ "symbol",
16777
+ "templateLiteral",
16778
+ "tuple",
16779
+ "undefined",
16780
+ "union",
16781
+ "unknown",
16782
+ "url",
16783
+ "uuid",
16784
+ "void"
16785
+ ]);
16786
+ var ZOD_FACTORY_NAMESPACES = /* @__PURE__ */ new Set(["coerce", "iso"]);
16787
+ var SCHEMA_RETURNING_METHODS = /* @__PURE__ */ new Set([
16788
+ "and",
16789
+ "array",
16790
+ "brand",
16791
+ "catch",
16792
+ "check",
16793
+ "clone",
16794
+ "default",
16795
+ "describe",
16796
+ "extend",
16797
+ "keyof",
16798
+ "meta",
16799
+ "nullable",
16800
+ "nullish",
16801
+ "omit",
16802
+ "optional",
16803
+ "or",
16804
+ "overwrite",
16805
+ "partial",
16806
+ "pick",
16807
+ "pipe",
16808
+ "prefault",
16809
+ "readonly",
16810
+ "refine",
16811
+ "register",
16812
+ "required",
16813
+ "safeExtend",
16814
+ "superRefine",
16815
+ "transform"
16816
+ ]);
16728
16817
  var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils85.AST_NODE_TYPES.Identifier ? callee.property.name : null;
16729
16818
  var calleeChainRoot = (node) => {
16730
16819
  let current = node;
@@ -16743,38 +16832,55 @@ var calleeChainRoot = (node) => {
16743
16832
  return null;
16744
16833
  }
16745
16834
  };
16746
- var zod_naming_convention_default = createRule({
16747
- name: "zod-naming-convention",
16748
- documentation: ZOD_NAMING_CONVENTION_DOCUMENTATION,
16835
+ var chainMemberNames = (node) => {
16836
+ const names = [];
16837
+ let current = node;
16838
+ for (; ; ) {
16839
+ if (current.type === import_utils85.AST_NODE_TYPES.MemberExpression) {
16840
+ if (current.computed || current.property.type !== import_utils85.AST_NODE_TYPES.Identifier) return [];
16841
+ names.push(current.property.name);
16842
+ current = current.object;
16843
+ continue;
16844
+ }
16845
+ if (current.type === import_utils85.AST_NODE_TYPES.CallExpression) {
16846
+ current = current.callee;
16847
+ continue;
16848
+ }
16849
+ break;
16850
+ }
16851
+ names.reverse();
16852
+ return names;
16853
+ };
16854
+ var unwrapExpression4 = (node) => {
16855
+ let current = node;
16856
+ while (current.type === import_utils85.AST_NODE_TYPES.TSAsExpression || current.type === import_utils85.AST_NODE_TYPES.TSSatisfiesExpression || current.type === import_utils85.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils85.AST_NODE_TYPES.TSTypeAssertion) {
16857
+ current = current.expression;
16858
+ }
16859
+ return current;
16860
+ };
16861
+ var isModuleDeclarator = (node) => {
16862
+ const declaration = node.parent;
16863
+ if (declaration.type !== import_utils85.AST_NODE_TYPES.VariableDeclaration) return false;
16864
+ const owner = declaration.parent;
16865
+ return owner.type === import_utils85.AST_NODE_TYPES.Program || owner.type === import_utils85.AST_NODE_TYPES.ExportNamedDeclaration && owner.parent.type === import_utils85.AST_NODE_TYPES.Program;
16866
+ };
16867
+ var require_pascal_case_zod_schema_name_default = createRule({
16868
+ name: "require-pascal-case-zod-schema-name",
16869
+ documentation: REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION,
16749
16870
  meta: {
16750
16871
  type: "suggestion",
16751
16872
  docs: {
16752
- description: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default."
16873
+ description: "Require confirmed module-level Zod schema contracts to use PascalCase with a `Schema` suffix."
16753
16874
  },
16754
- schema: [
16755
- {
16756
- type: "object",
16757
- additionalProperties: false,
16758
- properties: {
16759
- convention: {
16760
- type: "string",
16761
- enum: ["prefix", "suffix", "either"]
16762
- }
16763
- }
16764
- }
16765
- ],
16875
+ schema: [],
16766
16876
  messages: {
16767
- zPrefix: "Zod schema names should start with Z (e.g. `ZUser`)",
16768
- schemaSuffix: "Zod schema names should end with Schema (e.g. `userSchema`)",
16769
- zodSchemaName: "Zod schema names should start with Z (`ZUser`) or end with Schema (`userSchema`)"
16877
+ requirePascalSchema: "Zod schema contracts must use PascalCase ending in Schema (for example, `MutationRouteBaseSchema`); reserve SCREAMING_SNAKE_CASE for scalar/table constants."
16770
16878
  }
16771
16879
  },
16772
- defaultOptions: [{}],
16773
- create(context, [optionsArg]) {
16774
- const convention = optionsArg?.convention ?? "either";
16775
- const { test, messageId } = CONVENTIONS[convention];
16776
- const acceptsSchemaWord = convention !== "prefix";
16880
+ defaultOptions: [],
16881
+ create(context) {
16777
16882
  const zodBindings = /* @__PURE__ */ new Set();
16883
+ const schemaBindings = /* @__PURE__ */ new Set();
16778
16884
  function resolvedBinding(identifier) {
16779
16885
  return import_utils85.ASTUtils.findVariable(
16780
16886
  context.sourceCode.getScope(identifier),
@@ -16791,6 +16897,26 @@ var zod_naming_convention_default = createRule({
16791
16897
  const binding = resolvedBinding(root);
16792
16898
  return binding !== null && zodBindings.has(binding);
16793
16899
  }
16900
+ function isSchemaBinding(identifier) {
16901
+ const binding = resolvedBinding(identifier);
16902
+ return binding !== null && schemaBindings.has(binding);
16903
+ }
16904
+ function isConfirmedSchema(expression) {
16905
+ const init = unwrapExpression4(expression);
16906
+ if (init.type === import_utils85.AST_NODE_TYPES.Identifier) return isSchemaBinding(init);
16907
+ if (init.type !== import_utils85.AST_NODE_TYPES.CallExpression || init.callee.type !== import_utils85.AST_NODE_TYPES.MemberExpression) {
16908
+ return false;
16909
+ }
16910
+ const terminal = terminalMethodName(init.callee);
16911
+ if (terminal === null || NON_SCHEMA_TERMINALS.has(terminal)) return false;
16912
+ const names = chainMemberNames(init.callee);
16913
+ if (names.length === 0) return false;
16914
+ if (isZodChain(init.callee)) {
16915
+ return ZOD_SCHEMA_FACTORIES.has(names[0] ?? "") || ZOD_FACTORY_NAMESPACES.has(names[0] ?? "") && ZOD_SCHEMA_FACTORIES.has(names[1] ?? "");
16916
+ }
16917
+ const root = calleeChainRoot(init.callee);
16918
+ return root !== null && isSchemaBinding(root) && SCHEMA_RETURNING_METHODS.has(terminal);
16919
+ }
16794
16920
  if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
16795
16921
  return {};
16796
16922
  }
@@ -16804,20 +16930,17 @@ var zod_naming_convention_default = createRule({
16804
16930
  }
16805
16931
  },
16806
16932
  VariableDeclarator(node) {
16933
+ if (!isModuleDeclarator(node)) return;
16807
16934
  const init = node.init;
16808
16935
  if (init === null || init === void 0) return;
16809
- if (init.type !== import_utils85.AST_NODE_TYPES.CallExpression) return;
16810
- const callee = init.callee;
16811
- if (callee.type !== import_utils85.AST_NODE_TYPES.MemberExpression) return;
16812
- if (!isZodChain(callee)) return;
16813
- const terminal = terminalMethodName(callee);
16814
- if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;
16815
16936
  if (node.id.type !== import_utils85.AST_NODE_TYPES.Identifier) return;
16816
- if (test.test(node.id.name)) return;
16817
- if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;
16937
+ if (!isConfirmedSchema(init)) return;
16938
+ const binding = resolvedBinding(node.id);
16939
+ if (binding !== null) schemaBindings.add(binding);
16940
+ if (PASCAL_SCHEMA_NAME_RE.test(node.id.name)) return;
16818
16941
  context.report({
16819
16942
  node: node.id,
16820
- messageId
16943
+ messageId: "requirePascalSchema"
16821
16944
  });
16822
16945
  }
16823
16946
  };
@@ -16827,6 +16950,7 @@ var zod_naming_convention_default = createRule({
16827
16950
  // src/rules/_renames.ts
16828
16951
  var RENAMED_RULES = {
16829
16952
  "jsdoc-restates-signature": "no-restated-jsdoc",
16953
+ "zod-naming-convention": "require-pascal-case-zod-schema-name",
16830
16954
  "require-interface-for-injected-service": "require-port-for-service",
16831
16955
  "strict-test-assertions": "prefer-whole-object-assertion",
16832
16956
  "trailing-value-narration": "no-trailing-value-narration"
@@ -16984,18 +17108,18 @@ var RULES = {
16984
17108
  "store-insert-requires-on-conflict": store_insert_requires_on_conflict_default,
16985
17109
  "stepdown": stepdown_default,
16986
17110
  "source-coupled-test": source_coupled_test_default,
16987
- "zod-naming-convention": zod_naming_convention_default
17111
+ "require-pascal-case-zod-schema-name": require_pascal_case_zod_schema_name_default
16988
17112
  };
16989
17113
  var meta = {
16990
17114
  name: "@sarj/eslint-plugin",
16991
- version: "15.13.2"
17115
+ version: "15.13.3"
16992
17116
  };
16993
17117
  var APPLICATION_ONLY_RULES = [
16994
17118
  "no-restricted-library-load",
16995
17119
  "prefer-native-random-uuid",
16996
17120
  "prefer-shadcn-primitives"
16997
17121
  ];
16998
- var ADVISORY_RULES = [];
17122
+ var ADVISORY_RULES = ["@sarj/require-pascal-case-zod-schema-name"];
16999
17123
  var RECOMMENDED_RULES = {
17000
17124
  "@sarj/interface-contract-members-private": "error",
17001
17125
  "@sarj/iac-source-coupled-test": "error",
@@ -17068,7 +17192,7 @@ var RECOMMENDED_RULES = {
17068
17192
  "@sarj/stepdown": "error",
17069
17193
  "@sarj/source-coupled-test": "error",
17070
17194
  "@sarj/test-phase-label-comment": "error",
17071
- "@sarj/zod-naming-convention": "error"
17195
+ "@sarj/require-pascal-case-zod-schema-name": "warn"
17072
17196
  };
17073
17197
  var STRICT_RULES = {
17074
17198
  "@sarj/interface-contract-members-private": "error",
@@ -17146,7 +17270,7 @@ var STRICT_RULES = {
17146
17270
  "@sarj/stepdown": "error",
17147
17271
  "@sarj/source-coupled-test": "error",
17148
17272
  "@sarj/test-phase-label-comment": "error",
17149
- "@sarj/zod-naming-convention": "error"
17273
+ "@sarj/require-pascal-case-zod-schema-name": "warn"
17150
17274
  };
17151
17275
  var PLUGIN = {
17152
17276
  meta,
package/dist/index.d.cts CHANGED
@@ -182,6 +182,7 @@ interface RuleOptions {
182
182
  */
183
183
  declare const RENAMED_RULES: {
184
184
  readonly "jsdoc-restates-signature": "no-restated-jsdoc";
185
+ readonly "zod-naming-convention": "require-pascal-case-zod-schema-name";
185
186
  readonly "require-interface-for-injected-service": "require-port-for-service";
186
187
  readonly "strict-test-assertions": "prefer-whole-object-assertion";
187
188
  readonly "trailing-value-narration": "no-trailing-value-narration";
@@ -289,14 +290,12 @@ declare const RULES: {
289
290
  readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
290
291
  readonly stepdown: DocumentedRule<[], "helperAboveOnlyCaller">;
291
292
  readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
292
- readonly "zod-naming-convention": DocumentedRule<readonly [{
293
- convention?: "prefix" | "suffix" | "either";
294
- }?], "zPrefix" | "schemaSuffix" | "zodSchemaName">;
293
+ readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
295
294
  };
296
295
  /** Rules registered for application-profile configs but intentionally absent from general presets. */
297
296
  declare const APPLICATION_ONLY_RULES: readonly ["no-restricted-library-load", "prefer-native-random-uuid", "prefer-shadcn-primitives"];
298
297
  /** No active rule remains advisory; retained as a public compatibility export. */
299
- declare const ADVISORY_RULES: readonly [];
298
+ declare const ADVISORY_RULES: readonly ["@sarj/require-pascal-case-zod-schema-name"];
300
299
  declare const RECOMMENDED_RULES: {
301
300
  readonly "@sarj/interface-contract-members-private": "error";
302
301
  readonly "@sarj/iac-source-coupled-test": "error";
@@ -373,7 +372,7 @@ declare const RECOMMENDED_RULES: {
373
372
  readonly "@sarj/stepdown": "error";
374
373
  readonly "@sarj/source-coupled-test": "error";
375
374
  readonly "@sarj/test-phase-label-comment": "error";
376
- readonly "@sarj/zod-naming-convention": "error";
375
+ readonly "@sarj/require-pascal-case-zod-schema-name": "warn";
377
376
  };
378
377
  declare const STRICT_RULES: {
379
378
  readonly "@sarj/interface-contract-members-private": "error";
@@ -455,7 +454,7 @@ declare const STRICT_RULES: {
455
454
  readonly "@sarj/stepdown": "error";
456
455
  readonly "@sarj/source-coupled-test": "error";
457
456
  readonly "@sarj/test-phase-label-comment": "error";
458
- readonly "@sarj/zod-naming-convention": "error";
457
+ readonly "@sarj/require-pascal-case-zod-schema-name": "warn";
459
458
  };
460
459
  type FlatPreset = {
461
460
  readonly name: string;
@@ -465,7 +464,7 @@ type FlatPreset = {
465
464
  declare const PLUGIN: {
466
465
  readonly meta: {
467
466
  readonly name: "@sarj/eslint-plugin";
468
- readonly version: "15.13.2";
467
+ readonly version: "15.13.3";
469
468
  };
470
469
  readonly rules: {
471
470
  readonly "iac-source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
@@ -569,9 +568,7 @@ declare const PLUGIN: {
569
568
  readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
570
569
  readonly stepdown: DocumentedRule<[], "helperAboveOnlyCaller">;
571
570
  readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
572
- readonly "zod-naming-convention": DocumentedRule<readonly [{
573
- convention?: "prefix" | "suffix" | "either";
574
- }?], "zPrefix" | "schemaSuffix" | "zodSchemaName">;
571
+ readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
575
572
  };
576
573
  readonly retiredRules: Readonly<Record<string, RetiredRule>>;
577
574
  readonly configs: {
package/dist/index.d.ts CHANGED
@@ -182,6 +182,7 @@ interface RuleOptions {
182
182
  */
183
183
  declare const RENAMED_RULES: {
184
184
  readonly "jsdoc-restates-signature": "no-restated-jsdoc";
185
+ readonly "zod-naming-convention": "require-pascal-case-zod-schema-name";
185
186
  readonly "require-interface-for-injected-service": "require-port-for-service";
186
187
  readonly "strict-test-assertions": "prefer-whole-object-assertion";
187
188
  readonly "trailing-value-narration": "no-trailing-value-narration";
@@ -289,14 +290,12 @@ declare const RULES: {
289
290
  readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
290
291
  readonly stepdown: DocumentedRule<[], "helperAboveOnlyCaller">;
291
292
  readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
292
- readonly "zod-naming-convention": DocumentedRule<readonly [{
293
- convention?: "prefix" | "suffix" | "either";
294
- }?], "zPrefix" | "schemaSuffix" | "zodSchemaName">;
293
+ readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
295
294
  };
296
295
  /** Rules registered for application-profile configs but intentionally absent from general presets. */
297
296
  declare const APPLICATION_ONLY_RULES: readonly ["no-restricted-library-load", "prefer-native-random-uuid", "prefer-shadcn-primitives"];
298
297
  /** No active rule remains advisory; retained as a public compatibility export. */
299
- declare const ADVISORY_RULES: readonly [];
298
+ declare const ADVISORY_RULES: readonly ["@sarj/require-pascal-case-zod-schema-name"];
300
299
  declare const RECOMMENDED_RULES: {
301
300
  readonly "@sarj/interface-contract-members-private": "error";
302
301
  readonly "@sarj/iac-source-coupled-test": "error";
@@ -373,7 +372,7 @@ declare const RECOMMENDED_RULES: {
373
372
  readonly "@sarj/stepdown": "error";
374
373
  readonly "@sarj/source-coupled-test": "error";
375
374
  readonly "@sarj/test-phase-label-comment": "error";
376
- readonly "@sarj/zod-naming-convention": "error";
375
+ readonly "@sarj/require-pascal-case-zod-schema-name": "warn";
377
376
  };
378
377
  declare const STRICT_RULES: {
379
378
  readonly "@sarj/interface-contract-members-private": "error";
@@ -455,7 +454,7 @@ declare const STRICT_RULES: {
455
454
  readonly "@sarj/stepdown": "error";
456
455
  readonly "@sarj/source-coupled-test": "error";
457
456
  readonly "@sarj/test-phase-label-comment": "error";
458
- readonly "@sarj/zod-naming-convention": "error";
457
+ readonly "@sarj/require-pascal-case-zod-schema-name": "warn";
459
458
  };
460
459
  type FlatPreset = {
461
460
  readonly name: string;
@@ -465,7 +464,7 @@ type FlatPreset = {
465
464
  declare const PLUGIN: {
466
465
  readonly meta: {
467
466
  readonly name: "@sarj/eslint-plugin";
468
- readonly version: "15.13.2";
467
+ readonly version: "15.13.3";
469
468
  };
470
469
  readonly rules: {
471
470
  readonly "iac-source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
@@ -569,9 +568,7 @@ declare const PLUGIN: {
569
568
  readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
570
569
  readonly stepdown: DocumentedRule<[], "helperAboveOnlyCaller">;
571
570
  readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
572
- readonly "zod-naming-convention": DocumentedRule<readonly [{
573
- convention?: "prefix" | "suffix" | "either";
574
- }?], "zPrefix" | "schemaSuffix" | "zodSchemaName">;
571
+ readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
575
572
  };
576
573
  readonly retiredRules: Readonly<Record<string, RetiredRule>>;
577
574
  readonly configs: {
package/dist/index.js CHANGED
@@ -3674,8 +3674,6 @@ import {
3674
3674
  } from "@typescript-eslint/utils";
3675
3675
 
3676
3676
  // src/rules/_zod.ts
3677
- var ZOD_PREFIX_RE = /^Z[A-Z]/;
3678
- var ZOD_SUFFIX_RE = /Schema$/;
3679
3677
  var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
3680
3678
  function isZodModule(source) {
3681
3679
  return /(^|[/@-])zod([/-]|$)/.test(source);
@@ -16662,27 +16660,29 @@ var iac_source_coupled_test_default = createSourceCoupledRule(
16662
16660
  IAC_SOURCE_SUFFIX_RE
16663
16661
  );
16664
16662
 
16665
- // src/rules/zod-naming-convention.ts
16663
+ // src/rules/require-pascal-case-zod-schema-name.ts
16666
16664
  import {
16667
16665
  AST_NODE_TYPES as AST_NODE_TYPES65,
16668
16666
  ASTUtils as ASTUtils22
16669
16667
  } from "@typescript-eslint/utils";
16670
- var ZOD_NAMING_CONVENTION_DOCUMENTATION = {
16671
- summary: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.",
16672
- rationale: "A recognizable schema name distinguishes runtime validators from ordinary values at each use site.",
16673
- remediation: "Rename the schema with a `Z` prefix or `Schema` suffix, according to the configured convention.",
16668
+ var REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION = {
16669
+ summary: "Require confirmed module-level Zod schema contracts to use PascalCase with a `Schema` suffix.",
16670
+ rationale: "A reusable Zod schema is a runtime type contract. PascalCase mirrors that role; SCREAMING_SNAKE_CASE should remain reserved for scalar values and lookup tables.",
16671
+ remediation: "Rename the binding to PascalCase ending in `Schema` (for example, `MutationRouteBaseSchema`).",
16674
16672
  category: "style",
16673
+ aliases: ["zod-naming-convention"],
16674
+ autofix: "none",
16675
+ limitations: [
16676
+ "Only module-level bindings proven from a Zod import or a same-file proven schema are checked.",
16677
+ "Tests, benchmarks, generated files, imported schemas, re-export aliases, and arbitrary wrapper-factory results are excluded.",
16678
+ "Cross-file and exported renames are not safely file-local, so the rule has no autofix."
16679
+ ],
16675
16680
  examples: [
16676
- { id: "recognizable-schema-name", title: "Mark the value as a schema", outcome: "no-match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nconst userSchema = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 0, public: true },
16677
- { id: "unmarked-schema-name", title: "Do not hide the schema behind a value name", outcome: "match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nconst user = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 1, public: true }
16681
+ { id: "runtime-type-schema-name", title: "Name a reusable schema like a runtime type contract", outcome: "no-match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nexport const UserSchema = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 0, public: true },
16682
+ { id: "screaming-schema-name", title: "Do not name a schema like a scalar constant", outcome: "match", files: [{ path: "src/user.ts", source: "import { z } from 'zod';\nexport const USER_SCHEMA = z.object({ id: z.string() });" }], focusPath: "src/user.ts", expectedCount: 1, public: true }
16678
16683
  ]
16679
16684
  };
16680
- var CONVENTIONS = {
16681
- prefix: { test: ZOD_PREFIX_RE, messageId: "zPrefix" },
16682
- suffix: { test: ZOD_SUFFIX_RE, messageId: "schemaSuffix" },
16683
- either: { test: ZOD_SCHEMA_NAME_RE, messageId: "zodSchemaName" }
16684
- };
16685
- var CONTAINS_SCHEMA_RE = /schema/i;
16685
+ var PASCAL_SCHEMA_NAME_RE = /^[A-Z][A-Za-z0-9]*Schema$/;
16686
16686
  var BENCHMARK_PATH_RE = /(^|[\\/])(?:benchmarks?|bench)[\\/]/;
16687
16687
  var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
16688
16688
  "parse",
@@ -16707,6 +16707,95 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
16707
16707
  "prettifyError",
16708
16708
  "treeifyError"
16709
16709
  ]);
16710
+ var ZOD_SCHEMA_FACTORIES = /* @__PURE__ */ new Set([
16711
+ "any",
16712
+ "array",
16713
+ "base64",
16714
+ "base64url",
16715
+ "bigint",
16716
+ "boolean",
16717
+ "cidrv4",
16718
+ "cidrv6",
16719
+ "codec",
16720
+ "custom",
16721
+ "date",
16722
+ "discriminatedUnion",
16723
+ "email",
16724
+ "emoji",
16725
+ "enum",
16726
+ "file",
16727
+ "function",
16728
+ "hash",
16729
+ "hex",
16730
+ "hostname",
16731
+ "instanceof",
16732
+ "intersection",
16733
+ "ipv4",
16734
+ "ipv6",
16735
+ "json",
16736
+ "jwt",
16737
+ "lazy",
16738
+ "literal",
16739
+ "looseObject",
16740
+ "map",
16741
+ "nan",
16742
+ "nativeEnum",
16743
+ "never",
16744
+ "null",
16745
+ "nullable",
16746
+ "nullish",
16747
+ "number",
16748
+ "object",
16749
+ "optional",
16750
+ "partialRecord",
16751
+ "preprocess",
16752
+ "promise",
16753
+ "record",
16754
+ "set",
16755
+ "strictObject",
16756
+ "string",
16757
+ "stringbool",
16758
+ "symbol",
16759
+ "templateLiteral",
16760
+ "tuple",
16761
+ "undefined",
16762
+ "union",
16763
+ "unknown",
16764
+ "url",
16765
+ "uuid",
16766
+ "void"
16767
+ ]);
16768
+ var ZOD_FACTORY_NAMESPACES = /* @__PURE__ */ new Set(["coerce", "iso"]);
16769
+ var SCHEMA_RETURNING_METHODS = /* @__PURE__ */ new Set([
16770
+ "and",
16771
+ "array",
16772
+ "brand",
16773
+ "catch",
16774
+ "check",
16775
+ "clone",
16776
+ "default",
16777
+ "describe",
16778
+ "extend",
16779
+ "keyof",
16780
+ "meta",
16781
+ "nullable",
16782
+ "nullish",
16783
+ "omit",
16784
+ "optional",
16785
+ "or",
16786
+ "overwrite",
16787
+ "partial",
16788
+ "pick",
16789
+ "pipe",
16790
+ "prefault",
16791
+ "readonly",
16792
+ "refine",
16793
+ "register",
16794
+ "required",
16795
+ "safeExtend",
16796
+ "superRefine",
16797
+ "transform"
16798
+ ]);
16710
16799
  var terminalMethodName = (callee) => !callee.computed && callee.property.type === AST_NODE_TYPES65.Identifier ? callee.property.name : null;
16711
16800
  var calleeChainRoot = (node) => {
16712
16801
  let current = node;
@@ -16725,38 +16814,55 @@ var calleeChainRoot = (node) => {
16725
16814
  return null;
16726
16815
  }
16727
16816
  };
16728
- var zod_naming_convention_default = createRule({
16729
- name: "zod-naming-convention",
16730
- documentation: ZOD_NAMING_CONVENTION_DOCUMENTATION,
16817
+ var chainMemberNames = (node) => {
16818
+ const names = [];
16819
+ let current = node;
16820
+ for (; ; ) {
16821
+ if (current.type === AST_NODE_TYPES65.MemberExpression) {
16822
+ if (current.computed || current.property.type !== AST_NODE_TYPES65.Identifier) return [];
16823
+ names.push(current.property.name);
16824
+ current = current.object;
16825
+ continue;
16826
+ }
16827
+ if (current.type === AST_NODE_TYPES65.CallExpression) {
16828
+ current = current.callee;
16829
+ continue;
16830
+ }
16831
+ break;
16832
+ }
16833
+ names.reverse();
16834
+ return names;
16835
+ };
16836
+ var unwrapExpression4 = (node) => {
16837
+ let current = node;
16838
+ while (current.type === AST_NODE_TYPES65.TSAsExpression || current.type === AST_NODE_TYPES65.TSSatisfiesExpression || current.type === AST_NODE_TYPES65.TSNonNullExpression || current.type === AST_NODE_TYPES65.TSTypeAssertion) {
16839
+ current = current.expression;
16840
+ }
16841
+ return current;
16842
+ };
16843
+ var isModuleDeclarator = (node) => {
16844
+ const declaration = node.parent;
16845
+ if (declaration.type !== AST_NODE_TYPES65.VariableDeclaration) return false;
16846
+ const owner = declaration.parent;
16847
+ return owner.type === AST_NODE_TYPES65.Program || owner.type === AST_NODE_TYPES65.ExportNamedDeclaration && owner.parent.type === AST_NODE_TYPES65.Program;
16848
+ };
16849
+ var require_pascal_case_zod_schema_name_default = createRule({
16850
+ name: "require-pascal-case-zod-schema-name",
16851
+ documentation: REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION,
16731
16852
  meta: {
16732
16853
  type: "suggestion",
16733
16854
  docs: {
16734
- description: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default."
16855
+ description: "Require confirmed module-level Zod schema contracts to use PascalCase with a `Schema` suffix."
16735
16856
  },
16736
- schema: [
16737
- {
16738
- type: "object",
16739
- additionalProperties: false,
16740
- properties: {
16741
- convention: {
16742
- type: "string",
16743
- enum: ["prefix", "suffix", "either"]
16744
- }
16745
- }
16746
- }
16747
- ],
16857
+ schema: [],
16748
16858
  messages: {
16749
- zPrefix: "Zod schema names should start with Z (e.g. `ZUser`)",
16750
- schemaSuffix: "Zod schema names should end with Schema (e.g. `userSchema`)",
16751
- zodSchemaName: "Zod schema names should start with Z (`ZUser`) or end with Schema (`userSchema`)"
16859
+ requirePascalSchema: "Zod schema contracts must use PascalCase ending in Schema (for example, `MutationRouteBaseSchema`); reserve SCREAMING_SNAKE_CASE for scalar/table constants."
16752
16860
  }
16753
16861
  },
16754
- defaultOptions: [{}],
16755
- create(context, [optionsArg]) {
16756
- const convention = optionsArg?.convention ?? "either";
16757
- const { test, messageId } = CONVENTIONS[convention];
16758
- const acceptsSchemaWord = convention !== "prefix";
16862
+ defaultOptions: [],
16863
+ create(context) {
16759
16864
  const zodBindings = /* @__PURE__ */ new Set();
16865
+ const schemaBindings = /* @__PURE__ */ new Set();
16760
16866
  function resolvedBinding(identifier) {
16761
16867
  return ASTUtils22.findVariable(
16762
16868
  context.sourceCode.getScope(identifier),
@@ -16773,6 +16879,26 @@ var zod_naming_convention_default = createRule({
16773
16879
  const binding = resolvedBinding(root);
16774
16880
  return binding !== null && zodBindings.has(binding);
16775
16881
  }
16882
+ function isSchemaBinding(identifier) {
16883
+ const binding = resolvedBinding(identifier);
16884
+ return binding !== null && schemaBindings.has(binding);
16885
+ }
16886
+ function isConfirmedSchema(expression) {
16887
+ const init = unwrapExpression4(expression);
16888
+ if (init.type === AST_NODE_TYPES65.Identifier) return isSchemaBinding(init);
16889
+ if (init.type !== AST_NODE_TYPES65.CallExpression || init.callee.type !== AST_NODE_TYPES65.MemberExpression) {
16890
+ return false;
16891
+ }
16892
+ const terminal = terminalMethodName(init.callee);
16893
+ if (terminal === null || NON_SCHEMA_TERMINALS.has(terminal)) return false;
16894
+ const names = chainMemberNames(init.callee);
16895
+ if (names.length === 0) return false;
16896
+ if (isZodChain(init.callee)) {
16897
+ return ZOD_SCHEMA_FACTORIES.has(names[0] ?? "") || ZOD_FACTORY_NAMESPACES.has(names[0] ?? "") && ZOD_SCHEMA_FACTORIES.has(names[1] ?? "");
16898
+ }
16899
+ const root = calleeChainRoot(init.callee);
16900
+ return root !== null && isSchemaBinding(root) && SCHEMA_RETURNING_METHODS.has(terminal);
16901
+ }
16776
16902
  if (isTestFile(context.filename) || BENCHMARK_PATH_RE.test(context.filename.replaceAll("\\", "/")) || isGeneratedFile(context.filename, context.sourceCode.text)) {
16777
16903
  return {};
16778
16904
  }
@@ -16786,20 +16912,17 @@ var zod_naming_convention_default = createRule({
16786
16912
  }
16787
16913
  },
16788
16914
  VariableDeclarator(node) {
16915
+ if (!isModuleDeclarator(node)) return;
16789
16916
  const init = node.init;
16790
16917
  if (init === null || init === void 0) return;
16791
- if (init.type !== AST_NODE_TYPES65.CallExpression) return;
16792
- const callee = init.callee;
16793
- if (callee.type !== AST_NODE_TYPES65.MemberExpression) return;
16794
- if (!isZodChain(callee)) return;
16795
- const terminal = terminalMethodName(callee);
16796
- if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;
16797
16918
  if (node.id.type !== AST_NODE_TYPES65.Identifier) return;
16798
- if (test.test(node.id.name)) return;
16799
- if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;
16919
+ if (!isConfirmedSchema(init)) return;
16920
+ const binding = resolvedBinding(node.id);
16921
+ if (binding !== null) schemaBindings.add(binding);
16922
+ if (PASCAL_SCHEMA_NAME_RE.test(node.id.name)) return;
16800
16923
  context.report({
16801
16924
  node: node.id,
16802
- messageId
16925
+ messageId: "requirePascalSchema"
16803
16926
  });
16804
16927
  }
16805
16928
  };
@@ -16809,6 +16932,7 @@ var zod_naming_convention_default = createRule({
16809
16932
  // src/rules/_renames.ts
16810
16933
  var RENAMED_RULES = {
16811
16934
  "jsdoc-restates-signature": "no-restated-jsdoc",
16935
+ "zod-naming-convention": "require-pascal-case-zod-schema-name",
16812
16936
  "require-interface-for-injected-service": "require-port-for-service",
16813
16937
  "strict-test-assertions": "prefer-whole-object-assertion",
16814
16938
  "trailing-value-narration": "no-trailing-value-narration"
@@ -16966,18 +17090,18 @@ var RULES = {
16966
17090
  "store-insert-requires-on-conflict": store_insert_requires_on_conflict_default,
16967
17091
  "stepdown": stepdown_default,
16968
17092
  "source-coupled-test": source_coupled_test_default,
16969
- "zod-naming-convention": zod_naming_convention_default
17093
+ "require-pascal-case-zod-schema-name": require_pascal_case_zod_schema_name_default
16970
17094
  };
16971
17095
  var meta = {
16972
17096
  name: "@sarj/eslint-plugin",
16973
- version: "15.13.2"
17097
+ version: "15.13.3"
16974
17098
  };
16975
17099
  var APPLICATION_ONLY_RULES = [
16976
17100
  "no-restricted-library-load",
16977
17101
  "prefer-native-random-uuid",
16978
17102
  "prefer-shadcn-primitives"
16979
17103
  ];
16980
- var ADVISORY_RULES = [];
17104
+ var ADVISORY_RULES = ["@sarj/require-pascal-case-zod-schema-name"];
16981
17105
  var RECOMMENDED_RULES = {
16982
17106
  "@sarj/interface-contract-members-private": "error",
16983
17107
  "@sarj/iac-source-coupled-test": "error",
@@ -17050,7 +17174,7 @@ var RECOMMENDED_RULES = {
17050
17174
  "@sarj/stepdown": "error",
17051
17175
  "@sarj/source-coupled-test": "error",
17052
17176
  "@sarj/test-phase-label-comment": "error",
17053
- "@sarj/zod-naming-convention": "error"
17177
+ "@sarj/require-pascal-case-zod-schema-name": "warn"
17054
17178
  };
17055
17179
  var STRICT_RULES = {
17056
17180
  "@sarj/interface-contract-members-private": "error",
@@ -17128,7 +17252,7 @@ var STRICT_RULES = {
17128
17252
  "@sarj/stepdown": "error",
17129
17253
  "@sarj/source-coupled-test": "error",
17130
17254
  "@sarj/test-phase-label-comment": "error",
17131
- "@sarj/zod-naming-convention": "error"
17255
+ "@sarj/require-pascal-case-zod-schema-name": "warn"
17132
17256
  };
17133
17257
  var PLUGIN = {
17134
17258
  meta,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "15.13.2",
3
+ "version": "15.13.3",
4
4
  "packageManager": "npm@12.0.2",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",