@noctcore/eslint-plugin-contracts 0.7.0 โ†’ 0.7.1

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/README.md CHANGED
@@ -1,36 +1,73 @@
1
1
  # @noctcore/eslint-plugin-contracts
2
2
 
3
- Rules for shared **contract** conventions โ€” zod schema naming, wire-message discriminants, error
4
- stringification, direct `process.env` access, and money precision. Flat-config only, ESLint 9+.
3
+ Rules for shared **contract** conventions: zod schema naming, wire-message discriminants, error
4
+ stringification, direct `process.env` access and money precision, where drift between producer and
5
+ consumer breaks at runtime.
6
+
7
+ **Docs:** [noctcore.github.io/eslint-plugins/packages/contracts](https://noctcore.github.io/eslint-plugins/packages/contracts/)
8
+
9
+ Not a good fit for a codebase that does not use zod, or whose error handling is intentionally
10
+ untyped.
11
+
12
+ ## Requirements
13
+
14
+ - ESLint 9 or newer, flat config (`eslint.config.js`) only.
15
+ - `configs.recommended` registers the plugin and sets rule severities, nothing else. It sets no
16
+ `files` and no parser, so it applies to whatever files the rest of your config lints. To lint
17
+ TypeScript, add a `files` pattern and `@typescript-eslint/parser`, as in the quick start.
5
18
 
6
19
  ## Install
7
20
 
8
21
  ```sh
9
- bun add -D @noctcore/eslint-plugin-contracts # or npm i -D / pnpm add -D
22
+ bun add -D @noctcore/eslint-plugin-contracts @typescript-eslint/parser # or npm i -D / pnpm add -D
10
23
  ```
11
24
 
12
- ## Use
25
+ ## Quick start
13
26
 
14
27
  ```js
15
28
  // eslint.config.js
29
+ import tsParser from '@typescript-eslint/parser';
16
30
  import contracts from '@noctcore/eslint-plugin-contracts';
17
31
 
18
32
  export default [
19
- contracts.configs.recommended,
33
+ {
34
+ ...contracts.configs.recommended,
35
+ files: ['**/*.{ts,tsx}'],
36
+ languageOptions: { parser: tsParser },
37
+ },
38
+ // Optional: tune a preset rule's options.
39
+ {
40
+ files: ['**/*.{ts,tsx}'],
41
+ rules: {
42
+ 'noctcore-contracts/zod-schema-naming': ['error', { roleSuffixes: ['Event', 'Command', 'Query'] }],
43
+ 'noctcore-contracts/no-direct-process-env': ['error', { configModule: '@acme/config' }],
44
+ },
45
+ },
20
46
  ];
21
47
  ```
22
48
 
23
- Or wire rules individually:
49
+ ## Opt-in rules
24
50
 
25
- ```js
26
- import contracts from '@noctcore/eslint-plugin-contracts';
51
+ Four rules ship `off`: `require-registered-keys`, `env-var-schema-parity` and
52
+ `translation-key-exists` do nothing until their `sinks` / `schema` / `catalogs` options are set, and
53
+ `require-schema-parse-at-boundary` is a conservative syntactic slice of a type-aware concern.
27
54
 
55
+ ```js
56
+ // eslint.config.js
28
57
  export default [
58
+ // ...the quick start's entries, then:
29
59
  {
30
- plugins: { 'noctcore-contracts': contracts },
60
+ files: ['**/*.{ts,tsx}'],
61
+ languageOptions: { parser: tsParser },
31
62
  rules: {
32
- 'noctcore-contracts/zod-schema-naming': ['error', { roleSuffixes: ['Event', 'Command', 'Query'] }],
33
- 'noctcore-contracts/no-direct-process-env': ['error', { configModule: '@acme/config' }],
63
+ 'noctcore-contracts/require-registered-keys': ['error', {
64
+ sinks: [{ callee: 'localStorage.getItem', argIndex: 0 }],
65
+ }],
66
+ 'noctcore-contracts/env-var-schema-parity': ['error', { schema: '.env.example' }],
67
+ 'noctcore-contracts/translation-key-exists': ['error', {
68
+ catalogs: [{ file: 'locales/en/common.json', namespace: 'common' }],
69
+ }],
70
+ 'noctcore-contracts/require-schema-parse-at-boundary': 'error',
34
71
  },
35
72
  },
36
73
  ];
@@ -38,24 +75,29 @@ export default [
38
75
 
39
76
  ## Rules
40
77
 
41
- Legend: ๐Ÿ”ง = autofixable ยท ๐Ÿ’ค = ships inert / `off` in `recommended` (enable + configure explicitly).
42
-
43
- | Rule | Description | ๐Ÿ”ง | ๐Ÿ’ค |
44
- | --- | --- | --- | --- |
45
- | [`zod-schema-naming`](./docs/rules/zod-schema-naming.md) | Exported zod schema must be a PascalCase `*Schema` const with a sibling `z.infer` type. | | |
46
- | [`wire-message-naming`](./docs/rules/wire-message-naming.md) | A role-suffixed schema's `type: z.literal(...)` must be kebab-case of its name minus the suffix. | ๐Ÿ”ง | |
47
- | [`no-error-stringify`](./docs/rules/no-error-stringify.md) | Ban `${error}` / `error.toString()` / `error + ""` โ€” they drop the cause chain. | | |
48
- | [`no-direct-process-env`](./docs/rules/no-direct-process-env.md) | Ban direct `process.env`; require a typed config accessor. | | |
49
- | [`money-must-be-decimal`](./docs/rules/money-must-be-decimal.md) | Money-named fields typed `: number` are banned; require a Decimal money type. | | |
50
- | [`require-error-cause`](./docs/rules/require-error-cause.md) | Re-throwing a new error inside `catch` must forward the caught error as `{ cause }`. | ๐Ÿ”ง | |
51
- | [`restrict-throw-to-taxonomy`](./docs/rules/restrict-throw-to-taxonomy.md) | `throw` only allowlisted error classes; ban throwing non-Error values. | | |
52
- | [`require-registered-keys`](./docs/rules/require-registered-keys.md) | Key/name argument of a configured sink API must be an imported constant, not a raw string. | | ๐Ÿ’ค |
53
- | [`env-var-schema-parity`](./docs/rules/env-var-schema-parity.md) | `process.env.FOO` / `import.meta.env.FOO` keys must be declared in a schema file. | | ๐Ÿ’ค |
54
- | [`require-schema-parse-at-boundary`](./docs/rules/require-schema-parse-at-boundary.md) | Ban `as T` on boundary reads (`JSON.parse`, `res.json()`, web storage, search params, message events, LLM tool input), directly or through a `const`; parse at runtime. | | ๐Ÿ’ค |
55
- | [`schema-enum-field-consistency`](./docs/rules/schema-enum-field-consistency.md) | A field that is an enum in one zod object schema must not be `z.string()` in another schema of the same module. | | |
56
- | [`fetch-must-check-ok`](./docs/rules/fetch-must-check-ok.md) | A fetch response must be checked with `.ok` or a status comparison before `.json()` parses its body. | | |
57
- | [`translation-key-exists`](./docs/rules/translation-key-exists.md) | A static i18next / react-i18next key (`t(...)`, `i18n.t(...)`, `<Trans i18nKey>`) must exist in the catalog of the namespace in scope. | | ๐Ÿ’ค |
58
-
59
- The ๐Ÿ’ค rules ship `off` in `recommended`: `require-registered-keys`, `env-var-schema-parity` and
60
- `translation-key-exists` do nothing until their `sinks` / `schema` / `catalogs` options are set, and `require-schema-parse-at-boundary` is a
61
- conservative syntactic slice of a type-aware concern. Turn them on explicitly once configured.
78
+ <!-- begin generated rules -->
79
+ <!-- Generated by `bun run docs:readmes` from each rule's meta. Do not edit by hand. -->
80
+
81
+ โœ… in `recommended` (error) ยท โš™๏ธ needs options ยท ๐Ÿ”ง `--fix` ยท ๐Ÿ’ก suggestions ยท ๐Ÿ’ญ needs type info ยท โŒ deprecated
82
+
83
+ | Rule | Description | โœ… | โš™๏ธ | ๐Ÿ”ง | ๐Ÿ’ก | ๐Ÿ’ญ |
84
+ | --- | --- | :-: | :-: | :-: | :-: | :-: |
85
+ | [`env-var-schema-parity`](https://noctcore.github.io/eslint-plugins/rules/contracts/env-var-schema-parity/) | Require every `process.env.FOO` / `import.meta.env.FOO` key to be declared in a schema file (`.env.example` or a zod-env module), so config access and config declaration cannot drift apart. | | โš™๏ธ | | | |
86
+ | [`fetch-must-check-ok`](https://noctcore.github.io/eslint-plugins/rules/contracts/fetch-must-check-ok/) | Require a fetch response to be checked with `.ok` or a status comparison before `.json()` parses its body. | โœ… | | | | |
87
+ | [`money-must-be-decimal`](https://noctcore.github.io/eslint-plugins/rules/contracts/money-must-be-decimal/) | Disallow monetary values typed as the JS primitive `number`. Money-named fields explicitly typed `: number` lose precision to float rounding; use a Decimal money type instead. | โœ… | | | | |
88
+ | [`no-direct-process-env`](https://noctcore.github.io/eslint-plugins/rules/contracts/no-direct-process-env/) | Disallow direct `process.env` access. Force every consumer through a typed, validated config accessor so a missing variable fails at boot, not at use. | โœ… | | | | |
89
+ | [`no-error-stringify`](https://noctcore.github.io/eslint-plugins/rules/contracts/no-error-stringify/) | Disallow stringifying an error with bare `${error}` interpolation, `error.toString()`, or `error + ""`. These drop the cause chain. Use `error instanceof Error ? error.message : String(error)` instead. | โœ… | | | | |
90
+ | [`require-error-cause`](https://noctcore.github.io/eslint-plugins/rules/contracts/require-error-cause/) | Require re-thrown errors inside a `catch` to forward the caught error as `{ cause }`. A `throw new SomeError(...)` that omits the cause severs the chain to the original failure. | โœ… | | ๐Ÿ”ง | | |
91
+ | [`require-registered-keys`](https://noctcore.github.io/eslint-plugins/rules/contracts/require-registered-keys/) | Require the key/name argument of configured sink APIs (storage, event channels, cache keys) to be an imported constant from a registry module, not a raw string literal. | | โš™๏ธ | | | |
92
+ | [`require-schema-parse-at-boundary`](https://noctcore.github.io/eslint-plugins/rules/contracts/require-schema-parse-at-boundary/) | Disallow asserting external boundary data with `as T` instead of parsing it at runtime. Flags casts of `JSON.parse`, `res.json()`, web storage, URL search params, message-event data and LLM tool input, directly or through a `const`; use a zod/valibot parse. | | | | | |
93
+ | [`restrict-throw-to-taxonomy`](https://noctcore.github.io/eslint-plugins/rules/contracts/restrict-throw-to-taxonomy/) | Restrict `throw` to an approved error taxonomy. Flags throwing a non-allowlisted error class and throwing a non-Error value (string, object, number, ...). | โœ… | | | | |
94
+ | [`schema-enum-field-consistency`](https://noctcore.github.io/eslint-plugins/rules/contracts/schema-enum-field-consistency/) | Disallow a zod field that is an enum in one object schema of a module from being `z.string()` in another, which widens the wire type every consumer then narrows by hand. | โœ… | | | | |
95
+ | [`translation-key-exists`](https://noctcore.github.io/eslint-plugins/rules/contracts/translation-key-exists/) | Require every static i18next / react-i18next translation key (`t(...)`, `i18n.t(...)`, `<Trans i18nKey>`) to exist in the catalog of the namespace in scope. | | โš™๏ธ | | | |
96
+ | [`wire-message-naming`](https://noctcore.github.io/eslint-plugins/rules/contracts/wire-message-naming/) | A message-schema const ending in a role suffix (default Event/Command/Query) whose zod object declares `type: z.literal(...)` must set that literal to kebab-case(const name minus its role suffix). | โœ… | | ๐Ÿ”ง | | |
97
+ | [`zod-schema-naming`](https://noctcore.github.io/eslint-plugins/rules/contracts/zod-schema-naming/) | Every exported zod schema is a PascalCase const suffixed `Schema`, paired with a same-named inferred type (`export type Foo = z.infer<typeof FooSchema>`). | โœ… | | | | |
98
+ <!-- end generated rules -->
99
+
100
+ ## Severity policy
101
+
102
+ Every rule is `error` or `off`, never `warn`: a warning is a rule nobody obeys. See the
103
+ [severity policy](https://noctcore.github.io/eslint-plugins/getting-started/#severity-policy).
package/dist/index.cjs CHANGED
@@ -120,7 +120,8 @@ var envVarSchemaParityRule = createRule({
120
120
  meta: {
121
121
  type: "suggestion",
122
122
  docs: {
123
- description: "Require every `process.env.FOO` / `import.meta.env.FOO` key to be declared in a schema file (`.env.example` or a zod-env module), so config access and config declaration cannot drift apart."
123
+ description: "Require every `process.env.FOO` / `import.meta.env.FOO` key to be declared in a schema file (`.env.example` or a zod-env module), so config access and config declaration cannot drift apart.",
124
+ requiresOptions: true
124
125
  },
125
126
  schema: [optionSchema],
126
127
  messages: {
@@ -1087,7 +1088,8 @@ var requireRegisteredKeysRule = createRule({
1087
1088
  meta: {
1088
1089
  type: "suggestion",
1089
1090
  docs: {
1090
- description: "Require the key/name argument of configured sink APIs (storage, event channels, cache keys) to be an imported constant from a registry module, not a raw string literal."
1091
+ description: "Require the key/name argument of configured sink APIs (storage, event channels, cache keys) to be an imported constant from a registry module, not a raw string literal.",
1092
+ requiresOptions: true
1091
1093
  },
1092
1094
  schema: [optionSchema6],
1093
1095
  messages: {
@@ -2312,7 +2314,8 @@ var translationKeyExistsRule = createRule({
2312
2314
  meta: {
2313
2315
  type: "problem",
2314
2316
  docs: {
2315
- description: "Require every static i18next / react-i18next translation key (`t(...)`, `i18n.t(...)`, `<Trans i18nKey>`) to exist in the catalog of the namespace in scope."
2317
+ description: "Require every static i18next / react-i18next translation key (`t(...)`, `i18n.t(...)`, `<Trans i18nKey>`) to exist in the catalog of the namespace in scope.",
2318
+ requiresOptions: true
2316
2319
  },
2317
2320
  schema: [optionSchema10],
2318
2321
  messages: {
@@ -2599,7 +2602,7 @@ var rules = {
2599
2602
 
2600
2603
  // src/index.ts
2601
2604
  var NAMESPACE = "noctcore-contracts";
2602
- var VERSION = "0.7.0";
2605
+ var VERSION = "0.7.1";
2603
2606
  var plugin = {
2604
2607
  meta: { name: "@noctcore/eslint-plugin-contracts", version: VERSION },
2605
2608
  rules,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
2
+ import * as _noctcore_eslint_utils from '@noctcore/eslint-utils';
2
3
  import { TSESTree, TSESLint } from '@typescript-eslint/utils';
3
4
 
4
5
  interface FetchMustCheckOkOptions {
@@ -256,43 +257,43 @@ declare function translationSettingsOf(options: TranslationKeyExistsOptions): Tr
256
257
 
257
258
  /** Every rule this plugin exposes, keyed by its (unprefixed) rule id. */
258
259
  declare const rules: {
259
- 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
260
+ 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
260
261
  name: string;
261
262
  };
262
- 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
263
+ 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
263
264
  name: string;
264
265
  };
265
- 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
266
+ 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
266
267
  name: string;
267
268
  };
268
- 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
269
+ 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
269
270
  name: string;
270
271
  };
271
- 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
272
+ 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
272
273
  name: string;
273
274
  };
274
- 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
275
+ 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
275
276
  name: string;
276
277
  };
277
- 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
278
+ 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
278
279
  name: string;
279
280
  };
280
- 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
281
+ 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
281
282
  name: string;
282
283
  };
283
- 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
284
+ 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
284
285
  name: string;
285
286
  };
286
- 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
287
+ 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
287
288
  name: string;
288
289
  };
289
- 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
290
+ 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
290
291
  name: string;
291
292
  };
292
- 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
293
+ 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
293
294
  name: string;
294
295
  };
295
- 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
296
+ 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
296
297
  name: string;
297
298
  };
298
299
  };
@@ -303,43 +304,43 @@ declare const plugin: {
303
304
  version: string;
304
305
  };
305
306
  rules: {
306
- 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
307
+ 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
307
308
  name: string;
308
309
  };
309
- 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
310
+ 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
310
311
  name: string;
311
312
  };
312
- 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
313
+ 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
313
314
  name: string;
314
315
  };
315
- 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
316
+ 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
316
317
  name: string;
317
318
  };
318
- 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
319
+ 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
319
320
  name: string;
320
321
  };
321
- 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
322
+ 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
322
323
  name: string;
323
324
  };
324
- 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
325
+ 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
325
326
  name: string;
326
327
  };
327
- 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
328
+ 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
328
329
  name: string;
329
330
  };
330
- 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
331
+ 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
331
332
  name: string;
332
333
  };
333
- 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
334
+ 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
334
335
  name: string;
335
336
  };
336
- 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
337
+ 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
337
338
  name: string;
338
339
  };
339
- 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
340
+ 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
340
341
  name: string;
341
342
  };
342
- 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
343
+ 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
343
344
  name: string;
344
345
  };
345
346
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
2
+ import * as _noctcore_eslint_utils from '@noctcore/eslint-utils';
2
3
  import { TSESTree, TSESLint } from '@typescript-eslint/utils';
3
4
 
4
5
  interface FetchMustCheckOkOptions {
@@ -256,43 +257,43 @@ declare function translationSettingsOf(options: TranslationKeyExistsOptions): Tr
256
257
 
257
258
  /** Every rule this plugin exposes, keyed by its (unprefixed) rule id. */
258
259
  declare const rules: {
259
- 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
260
+ 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
260
261
  name: string;
261
262
  };
262
- 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
263
+ 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
263
264
  name: string;
264
265
  };
265
- 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
266
+ 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
266
267
  name: string;
267
268
  };
268
- 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
269
+ 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
269
270
  name: string;
270
271
  };
271
- 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
272
+ 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
272
273
  name: string;
273
274
  };
274
- 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
275
+ 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
275
276
  name: string;
276
277
  };
277
- 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
278
+ 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
278
279
  name: string;
279
280
  };
280
- 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
281
+ 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
281
282
  name: string;
282
283
  };
283
- 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
284
+ 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
284
285
  name: string;
285
286
  };
286
- 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
287
+ 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
287
288
  name: string;
288
289
  };
289
- 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
290
+ 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
290
291
  name: string;
291
292
  };
292
- 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
293
+ 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
293
294
  name: string;
294
295
  };
295
- 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
296
+ 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
296
297
  name: string;
297
298
  };
298
299
  };
@@ -303,43 +304,43 @@ declare const plugin: {
303
304
  version: string;
304
305
  };
305
306
  rules: {
306
- 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
307
+ 'zod-schema-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"schemaNaming" | "missingType", [ZodSchemaNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
307
308
  name: string;
308
309
  };
309
- 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
310
+ 'wire-message-naming': _typescript_eslint_utils_ts_eslint.RuleModule<"typeMismatch", [WireMessageNamingOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
310
311
  name: string;
311
312
  };
312
- 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
313
+ 'no-error-stringify': _typescript_eslint_utils_ts_eslint.RuleModule<"noErrorStringify", [NoErrorStringifyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
313
314
  name: string;
314
315
  };
315
- 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
316
+ 'no-direct-process-env': _typescript_eslint_utils_ts_eslint.RuleModule<"directProcessEnv", [NoDirectProcessEnvOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
316
317
  name: string;
317
318
  };
318
- 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
319
+ 'money-must-be-decimal': _typescript_eslint_utils_ts_eslint.RuleModule<"moneyMustBeDecimal", [MoneyMustBeDecimalOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
319
320
  name: string;
320
321
  };
321
- 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
322
+ 'require-error-cause': _typescript_eslint_utils_ts_eslint.RuleModule<"missingCause", [], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
322
323
  name: string;
323
324
  };
324
- 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
325
+ 'restrict-throw-to-taxonomy': _typescript_eslint_utils_ts_eslint.RuleModule<"disallowedErrorClass" | "nonErrorThrow", [RestrictThrowToTaxonomyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
325
326
  name: string;
326
327
  };
327
- 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
328
+ 'require-registered-keys': _typescript_eslint_utils_ts_eslint.RuleModule<"unregisteredKey", [RequireRegisteredKeysOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
328
329
  name: string;
329
330
  };
330
- 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
331
+ 'env-var-schema-parity': _typescript_eslint_utils_ts_eslint.RuleModule<"undeclaredEnvVar", [EnvVarSchemaParityOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
331
332
  name: string;
332
333
  };
333
- 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
334
+ 'require-schema-parse-at-boundary': _typescript_eslint_utils_ts_eslint.RuleModule<"castedBoundaryData", [RequireSchemaParseAtBoundaryOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
334
335
  name: string;
335
336
  };
336
- 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
337
+ 'schema-enum-field-consistency': _typescript_eslint_utils_ts_eslint.RuleModule<"widenedEnumField", [SchemaEnumFieldConsistencyOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
337
338
  name: string;
338
339
  };
339
- 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
340
+ 'fetch-must-check-ok': _typescript_eslint_utils_ts_eslint.RuleModule<"missingOkCheck", [FetchMustCheckOkOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
340
341
  name: string;
341
342
  };
342
- 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
343
+ 'translation-key-exists': _typescript_eslint_utils_ts_eslint.RuleModule<"missingKey" | "missingKeyPrefix" | "unknownNamespace" | "catalogUnreadable", [TranslationKeyExistsOptions], _noctcore_eslint_utils.NoctcoreRuleDocs, _typescript_eslint_utils_ts_eslint.RuleListener> & {
343
344
  name: string;
344
345
  };
345
346
  };
package/dist/index.js CHANGED
@@ -76,7 +76,8 @@ var envVarSchemaParityRule = createRule({
76
76
  meta: {
77
77
  type: "suggestion",
78
78
  docs: {
79
- description: "Require every `process.env.FOO` / `import.meta.env.FOO` key to be declared in a schema file (`.env.example` or a zod-env module), so config access and config declaration cannot drift apart."
79
+ description: "Require every `process.env.FOO` / `import.meta.env.FOO` key to be declared in a schema file (`.env.example` or a zod-env module), so config access and config declaration cannot drift apart.",
80
+ requiresOptions: true
80
81
  },
81
82
  schema: [optionSchema],
82
83
  messages: {
@@ -1043,7 +1044,8 @@ var requireRegisteredKeysRule = createRule({
1043
1044
  meta: {
1044
1045
  type: "suggestion",
1045
1046
  docs: {
1046
- description: "Require the key/name argument of configured sink APIs (storage, event channels, cache keys) to be an imported constant from a registry module, not a raw string literal."
1047
+ description: "Require the key/name argument of configured sink APIs (storage, event channels, cache keys) to be an imported constant from a registry module, not a raw string literal.",
1048
+ requiresOptions: true
1047
1049
  },
1048
1050
  schema: [optionSchema6],
1049
1051
  messages: {
@@ -2268,7 +2270,8 @@ var translationKeyExistsRule = createRule({
2268
2270
  meta: {
2269
2271
  type: "problem",
2270
2272
  docs: {
2271
- description: "Require every static i18next / react-i18next translation key (`t(...)`, `i18n.t(...)`, `<Trans i18nKey>`) to exist in the catalog of the namespace in scope."
2273
+ description: "Require every static i18next / react-i18next translation key (`t(...)`, `i18n.t(...)`, `<Trans i18nKey>`) to exist in the catalog of the namespace in scope.",
2274
+ requiresOptions: true
2272
2275
  },
2273
2276
  schema: [optionSchema10],
2274
2277
  messages: {
@@ -2555,7 +2558,7 @@ var rules = {
2555
2558
 
2556
2559
  // src/index.ts
2557
2560
  var NAMESPACE = "noctcore-contracts";
2558
- var VERSION = "0.7.0";
2561
+ var VERSION = "0.7.1";
2559
2562
  var plugin = {
2560
2563
  meta: { name: "@noctcore/eslint-plugin-contracts", version: VERSION },
2561
2564
  rules,
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Every `process.env.FOO` / `import.meta.env.FOO` key must be declared in a schema file.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โš™๏ธ Opt-in: `off` in `recommended`; needs options (see Options) ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  An env var that is read in code but declared nowhere is config drift waiting to fail in production:
@@ -27,8 +31,12 @@ const url = process.env.DATABASE_URL;
27
31
  const port = import.meta.env.PORT;
28
32
  ```
29
33
 
30
- Only static accesses are policed. Computed (`process.env[dynamic]`) and destructured reads are left
31
- alone.
34
+ ## What it does not flag
35
+
36
+ - Computed reads such as `process.env[dynamicKey]`: only static `.FOO` accesses are policed.
37
+ - Destructured reads (`const { FOO } = process.env`).
38
+ - Anything at all when `schema` is unset, or when the schema file cannot be read: the rule goes inert
39
+ rather than flagging every access.
32
40
 
33
41
  ## Options
34
42
 
@@ -2,7 +2,9 @@
2
2
 
3
3
  > A fetch response must be checked with `.ok` or a status comparison before `.json()` parses its body.
4
4
 
5
- Ported from tsforge's `typescript-core/fetch-must-check-ok` (MIT).
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
6
8
 
7
9
  ## Why
8
10
 
@@ -85,6 +87,13 @@ Three response shapes are tracked: `const res = await fetch(...)` then `res.json
85
87
  Aliases work (`const ok = res.ok; if (!ok) throw ...`). A bare `if (res.status)` or
86
88
  `typeof res.status === 'number'` is not a check: both are true for a 500.
87
89
 
90
+ ## What it does not flag
91
+
92
+ Purely syntactic, no type information. A response assigned later (`let res; res = await fetch(...)`),
93
+ passed to another function, or returned from a wrapper that is not in `fetchFunctions` is not tracked.
94
+ A nested function that reuses the response's name is treated as the same binding. Clients whose
95
+ `.json()` already throws on a bad status (ky, for example) do not belong in `fetchFunctions`.
96
+
88
97
  ## Options
89
98
 
90
99
  | Option | Type | Default | Meaning |
@@ -99,9 +108,12 @@ List every name you want tracked, including `fetch` itself:
99
108
  }]
100
109
  ```
101
110
 
102
- ## Limits
111
+ ## When not to use it
103
112
 
104
- Purely syntactic, no type information. A response assigned later (`let res; res = await fetch(...)`),
105
- passed to another function, or returned from a wrapper that is not in `fetchFunctions` is not tracked.
106
- A nested function that reuses the response's name is treated as the same binding. Clients whose
107
- `.json()` already throws on a bad status (ky, for example) do not belong in `fetchFunctions`.
113
+ Leave it off if your code never reads a raw `Response`, for example when every request goes through a
114
+ client whose `.json()` already throws on a bad status.
115
+
116
+ ## Credits
117
+
118
+ Based on the `typescript-core/fetch-must-check-ok` rule from
119
+ [tsforge](https://github.com/boringstack-xyz/tsforge) (MIT).
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Monetary fields typed as the JS `number` primitive lose precision to float rounding โ€” use a Decimal money type.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  Money stored as a JS `number` accumulates IEEE-754 rounding errors (`0.1 + 0.2 !== 0.3`), which is
@@ -16,10 +20,6 @@ positions that carry real precision risk:
16
20
  - class properties โ€” `class Invoice { total: number }`
17
21
  - annotated variable declarators โ€” `const amount: number = โ€ฆ`
18
22
 
19
- Conservative on purpose. Untyped declarations and numeric-literal initializers (`let total = 0`) are
20
- **not** flagged โ€” those are usually counters/accumulators. Interface and type-literal members
21
- (`{ amount: number }`) are **out of scope** so non-money type members do not regress.
22
-
23
23
  ```ts bad reports=2
24
24
  class Invoice { total: number; }
25
25
  const amount: number = 5;
@@ -31,6 +31,15 @@ const count: number = 3; // not a money name
31
31
  interface Payment { amount: number; } // type member, out of scope
32
32
  ```
33
33
 
34
+ ## What it does not flag
35
+
36
+ Conservative on purpose. Untyped declarations and numeric-literal initializers (`let total = 0`) are
37
+ **not** flagged โ€” those are usually counters/accumulators. Interface and type-literal members
38
+ (`{ amount: number }`) are **out of scope** so non-money type members do not regress.
39
+
40
+ - Object-literal properties (`const x = { total: 5 }`).
41
+ - Fields matching `minorUnitPatterns`, and every field in a file listed in `allowedFiles`.
42
+
34
43
  ## Options
35
44
 
36
45
  | Option | Type | Default | Meaning |
@@ -47,7 +56,7 @@ interface Payment { amount: number; } // type member, out of scope
47
56
  }]
48
57
  ```
49
58
 
50
- ## Talking to a payment API
59
+ ### Talking to a payment API
51
60
 
52
61
  Stripe and most payment APIs deal in integer minor units: `amount` is a number of cents, and that
53
62
  is correct at that boundary. Without `minorUnitPatterns` this rule flags every one of those
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Read environment variables through a typed, validated config accessor โ€” never `process.env` directly.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  `process.env.X` is `string | undefined`, unvalidated, and reachable from anywhere. A typo or a missing
@@ -27,8 +31,12 @@ import { config } from '@/config';
27
31
  const isProd = config.isProduction;
28
32
  ```
29
33
 
30
- Files matched by the `allowedFiles` glob allowlist are skipped entirely, so bootstrap entrypoints,
31
- config files, and tests may still read `process.env` directly.
34
+ ## What it does not flag
35
+
36
+ - Files matched by the `allowedFiles` glob allowlist are skipped entirely, so bootstrap entrypoints,
37
+ config files, and tests may still read `process.env` directly.
38
+ - `import.meta.env` reads and other `process` properties (`process.environment`, `process.argv`):
39
+ only `process.env` itself is policed.
32
40
 
33
41
  ```ts good filename=vite.config.ts relocation
34
42
  const isProd = process.env.NODE_ENV === 'production';
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Stringifying an error with `${error}`, `error.toString()`, or `error + ""` drops its cause chain.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  `` `${error}` ``, `error.toString()`, and `error + ""` all coerce an `Error` to its `message` alone,
@@ -31,6 +35,12 @@ const msg = error instanceof Error ? error.message : String(error);
31
35
  const m = `${error.message}`;
32
36
  ```
33
37
 
38
+ ## What it does not flag
39
+
40
+ - Bare `String(error)`, which the guarded extractor idiom relies on.
41
+ - Member reads such as `` `${error.message}` `` or `error.stack`.
42
+ - `x + ""` where `x` is not one of `errorIdentifierNames` (`count + ""`).
43
+
34
44
  ## Options
35
45
 
36
46
  | Option | Type | Default | Meaning |
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Re-throwing inside a `catch` without `{ cause }` severs the chain to the original error. ๐Ÿ”ง
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ”ง Fixable with `--fix` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  When you catch an error and throw a new one, the new error is what reaches your logger. If you do not
@@ -20,16 +24,6 @@ try {
20
24
  ## What it flags
21
25
 
22
26
  A `throw new SomeError(...)` inside a `catch` block when **no** argument carries a `cause` property.
23
- Deliberately conservative:
24
-
25
- - Only constructors whose simple name ends in `Error` or `Exception` are treated as errors
26
- (`Error`, `TypeError`, `ValidationError`, `HttpException`). Throwing `new Response(...)` for
27
- control flow is not policed.
28
- - Fires only when the enclosing catch binds a plain identifier (`catch (err)`). A parameterless
29
- `catch {}` or a destructured binding (`catch ({ message })`) has no single name to attach, so the
30
- throw is left alone.
31
- - Any `cause` property (whatever its value), and any spread the rule cannot see through, counts as
32
- "has a cause" and suppresses the report โ€” a hand-written cause is never second-guessed.
33
27
 
34
28
  A `throw` nested in a closure declared inside the catch is still flagged: the binding is genuinely in
35
29
  scope there. Nested `try/catch` uses the nearest binding.
@@ -47,7 +41,7 @@ try { work(); } catch (err) { throw new Error('failed', { cause: err }); }
47
41
  try { work(); } catch (err) { throw err; }
48
42
  ```
49
43
 
50
- ## Fix
44
+ ### Fix
51
45
 
52
46
  Autofix attaches `{ cause: <binding> }`:
53
47
 
@@ -58,9 +52,19 @@ Autofix attaches `{ cause: <binding> }`:
58
52
  A zero-argument `new SomeError()` is **reported but not autofixed** โ€” inserting an options object as
59
53
  the first argument could clobber a positional message the rule cannot see.
60
54
 
61
- ## Options
55
+ ## What it does not flag
62
56
 
63
- None.
57
+ Deliberately conservative:
58
+
59
+ - Only constructors whose simple name ends in `Error` or `Exception` are treated as errors
60
+ (`Error`, `TypeError`, `ValidationError`, `HttpException`). Throwing `new Response(...)` for
61
+ control flow is not policed.
62
+ - Fires only when the enclosing catch binds a plain identifier (`catch (err)`). A parameterless
63
+ `catch {}` or a destructured binding (`catch ({ message })`) has no single name to attach, so the
64
+ throw is left alone.
65
+ - Any `cause` property (whatever its value), and any spread the rule cannot see through, counts as
66
+ "has a cause" and suppresses the report โ€” a hand-written cause is never second-guessed.
67
+ - A bare re-throw of the caught error (`throw err`).
64
68
 
65
69
  ## When not to use it
66
70
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  > The key/name argument of a configured sink API must be an imported constant, not a raw string.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โš™๏ธ Opt-in: `off` in `recommended`; needs options (see Options) ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  String keys threaded into sink APIs โ€” storage slots, event channels, feature flags, query-cache keys
@@ -26,9 +30,18 @@ localStorage.getItem(USER_PROFILE_KEY);
26
30
  emitter.on(TASK_DONE, handler);
27
31
  ```
28
32
 
33
+ ## What it does not flag
34
+
29
35
  Only string literals are flagged. An already-imported identifier, a template literal, or any computed
30
36
  expression is left alone โ€” those are not the raw-string smell.
31
37
 
38
+ `callee` is matched against the call's dotted identifier path (`localStorage.getItem`, `emitter.on`).
39
+ A callee that is computed or not a plain identifier chain (`this.emitter.on`, `obj[k].on`, `a().b`)
40
+ cannot be matched and is skipped.
41
+
42
+ Calls to callees not listed in `sinks` (`sessionStorage.getItem` when only `localStorage.getItem` is
43
+ configured) are not policed.
44
+
32
45
  ## Options
33
46
 
34
47
  | Option | Type | Default | Meaning |
@@ -51,10 +64,6 @@ key sinks to hard-code, so you declare which callees matter for your project.
51
64
  }]
52
65
  ```
53
66
 
54
- `callee` is matched against the call's dotted identifier path (`localStorage.getItem`, `emitter.on`).
55
- A callee that is computed or not a plain identifier chain (`this.emitter.on`, `obj[k].on`, `a().b`)
56
- cannot be matched and is skipped.
57
-
58
67
  ## When not to use it
59
68
 
60
69
  If your keys are already constants, or you have no registry module to import from, leave `sinks` empty
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Parse external boundary data at runtime โ€” don't assert its shape with `as T`.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ Opt-in: `off` in `recommended` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  External data โ€” a fetch body, a `JSON.parse` result, a message-event payload โ€” has whatever shape the
@@ -98,7 +102,7 @@ async function loadUser(res: Response) {
98
102
  }
99
103
  ```
100
104
 
101
- ### What it leaves alone
105
+ ## What it does not flag
102
106
 
103
107
  - Casts to `unknown`, `any`, `const` or a primitive keyword (`as string | null`): the safe or
104
108
  neutral forms.
@@ -111,12 +115,15 @@ async function loadUser(res: Response) {
111
115
 
112
116
  ## Options
113
117
 
118
+ | Option | Type | Default | Meaning |
119
+ | --- | --- | --- | --- |
120
+ | `boundaries` | `string[]` | `[]` | Extra callees whose result is boundary data: a bare name or a dotted path. |
121
+
114
122
  ```js
115
- {
116
- // Extra callees whose result is boundary data: a bare name or a dotted path.
123
+ 'noctcore-contracts/require-schema-parse-at-boundary': ['error', {
117
124
  // `readBody(event) as T` and `(await readBody(event)) as T` are then flagged.
118
- boundaries: ['readBody', 'ipcRenderer.invoke'], // default []
119
- }
125
+ boundaries: ['readBody', 'ipcRenderer.invoke'],
126
+ }]
120
127
  ```
121
128
 
122
129
  ```ts bad options={"boundaries":["readBody"]}
@@ -2,6 +2,10 @@
2
2
 
3
3
  > `throw` only members of your error taxonomy โ€” never an ad hoc built-in nor a bare value.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  A codebase that throws a curated set of error types can handle them exhaustively at the boundary:
@@ -15,10 +19,6 @@ breaks that: a non-Error carries no stack and no cause, and an unclassified erro
15
19
  - `throw new SomethingError(...)` whose constructor is **not** in the `allow` list.
16
20
  - `throw <non-Error value>` โ€” a string, number, boolean, template literal, object, or array literal.
17
21
 
18
- Conservative on the ambiguous forms. A bare identifier (`throw err` โ€” the re-throw), a member
19
- (`throw ctx.error`), and a call (`throw makeError()`) are all left alone: a syntactic rule cannot know
20
- whether they resolve to an Error, and re-throwing a caught error is the most common `throw` there is.
21
-
22
22
  ```ts bad reports=3
23
23
  // built-in not in the taxonomy
24
24
  throw new TypeError('bad');
@@ -34,6 +34,12 @@ throw new Error('boom');
34
34
  try { work(); } catch (err) { throw err; }
35
35
  ```
36
36
 
37
+ ## What it does not flag
38
+
39
+ Conservative on the ambiguous forms. A bare identifier (`throw err` โ€” the re-throw), a member
40
+ (`throw ctx.error`), and a call (`throw makeError()`) are all left alone: a syntactic rule cannot know
41
+ whether they resolve to an Error, and re-throwing a caught error is the most common `throw` there is.
42
+
37
43
  ## Options
38
44
 
39
45
  | Option | Type | Default | Meaning |
@@ -3,6 +3,10 @@
3
3
  > A field that is an enum in one zod object schema must not be `z.string()` in another schema of the
4
4
  > same module.
5
5
 
6
+ <!-- begin generated rule header -->
7
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
8
+ <!-- end generated rule header -->
9
+
6
10
  ## Why
7
11
 
8
12
  When schemas double as wire types (tRPC procedures, a shared contract package), the output schema is
@@ -50,6 +54,16 @@ export const ticketOutput = z.object({
50
54
  });
51
55
  ```
52
56
 
57
+ ## What it does not flag
58
+
59
+ - `z.union([z.string(), z.null()])` and any `z.string()` chain with `.pipe()` or `.transform()`:
60
+ neither is a plain string.
61
+ - A single `z.literal('X')`: a constant, not an enum.
62
+ - An imported identifier, unless its name matches `enumIdentifierPattern` (see Options).
63
+ - An enum in another file: the scope is one module.
64
+ - Keys in `ignoreFields`, computed keys, and schemas built from a namespace not listed in
65
+ `zodIdentifiers`.
66
+
53
67
  ## Options
54
68
 
55
69
  | Option | Type | Default | Meaning |
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Every static translation key must exist in the catalog of the namespace in scope.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โš™๏ธ Opt-in: `off` in `recommended`; needs options (see Options) ยท ๐Ÿ’ญ Type information: used when available
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  i18next does not fail on a missing key. It renders the key itself (`admin.portalAccounts.revokeTitle`)
@@ -56,7 +60,7 @@ Plural forms (`key_one`, `key_few`, `key_ordinal_other`) answer for `key` only w
56
60
  `count`, and context variants (`key_male`) only when it passes `context`, because without them
57
61
  i18next looks up the bare key and misses. A subtree or array answers only with `returnObjects`.
58
62
 
59
- ### What it never reports
63
+ ## What it does not flag
60
64
 
61
65
  Anything it cannot resolve statically: a variable key (`t(someKey)`), a template key
62
66
  (`` t(`status.${s}`) ``), a key computed by a helper, a namespace held in an identifier it cannot
@@ -64,6 +68,15 @@ resolve (an import not listed in `namespaceIdentifiers`), an options bag it cann
64
68
  (`t('k', opts)`, `{ ...opts }`, `{ ns: someNs }`), and a `t` parameter with no `TFunction` type. The
65
69
  rule stays silent on those rather than guessing.
66
70
 
71
+ ### Dead keys are out of scope
72
+
73
+ The reverse check, "a catalog key nothing uses", is not something a per-file ESLint rule can do
74
+ soundly: it needs every source file at once, and ESLint may lint one file (editor), a subset
75
+ (`--cache`, lint-staged), or shard files across workers. Worse, keys routinely flow as data
76
+ (navigation tables, key-builder helpers, `` `errors:${code}` ``), which no call-site analysis
77
+ sees. Run it as a whole-tree check instead, one that unions static keys, template-key prefixes
78
+ and string literals that equal a key.
79
+
67
80
  ## Options
68
81
 
69
82
  | Option | Type | Default | Meaning |
@@ -140,15 +153,6 @@ found one real bug (`t('common.cancel')` in the default namespace, which renders
140
153
  namespace registered at runtime only inside a test (`registerFeatureNamespace('late-arrival', ...)`)
141
154
  is reported as unknown; exclude test files or add a catalog entry for it.
142
155
 
143
- ## Dead keys are out of scope
144
-
145
- The reverse check, "a catalog key nothing uses", is not something a per-file ESLint rule can do
146
- soundly: it needs every source file at once, and ESLint may lint one file (editor), a subset
147
- (`--cache`, lint-staged), or shard files across workers. Worse, keys routinely flow as data
148
- (navigation tables, key-builder helpers, `` `errors:${code}` ``), which no call-site analysis
149
- sees. Run it as a whole-tree check instead, one that unions static keys, template-key prefixes
150
- and string literals that equal a key.
151
-
152
156
  ## When not to use it
153
157
 
154
158
  If your keys are mostly computed, or your catalogs are not JSON files on disk (fetched from a TMS at
@@ -2,6 +2,10 @@
2
2
 
3
3
  > A message-schema's `type` discriminant must be the kebab-case of its const name minus its role suffix. ๐Ÿ”ง
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ”ง Fixable with `--fix` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  When wire messages are modelled as zod objects with a `type: z.literal('โ€ฆ')` discriminant, the const
@@ -29,7 +33,11 @@ export const TaskCompletedEvent = z.object({ type: z.literal('task-completed') }
29
33
  export const RunTaskCommand = z.object({ type: z.literal('run-task') });
30
34
  ```
31
35
 
32
- Consts without a role suffix, and role-suffixed consts without a `type` literal, are ignored.
36
+ ## What it does not flag
37
+
38
+ - Consts without a role suffix (`TaskSchema`), whatever their `type` literal says.
39
+ - Role-suffixed consts without a `type: z.literal(...)` property.
40
+ - Consts that are not exported: only `export const` declarations are checked.
33
41
 
34
42
  ## Options
35
43
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  > Every exported zod schema is a PascalCase const suffixed `Schema`, paired with a same-named inferred type.
4
4
 
5
+ <!-- begin generated rule header -->
6
+ โœ… In `recommended` at `error` ยท ๐Ÿ’ญ Type information: not needed
7
+ <!-- end generated rule header -->
8
+
5
9
  ## Why
6
10
 
7
11
  A contracts package is a shared spine. A uniform `FooSchema` + `Foo` pairing keeps the schema and
@@ -30,6 +34,12 @@ export const TaskSchema = z.object({ id: z.string() });
30
34
  export type Task = z.infer<typeof TaskSchema>;
31
35
  ```
32
36
 
37
+ ## What it does not flag
38
+
39
+ - Exported consts whose initializer is not rooted at `z` (`export const MAX = 10`).
40
+ - Zod schemas that are not exported.
41
+ - Consts ending in one of the configured `roleSuffixes` (see Options).
42
+
33
43
  ## Options
34
44
 
35
45
  | Option | Type | Default | Meaning |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noctcore/eslint-plugin-contracts",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "ESLint rules for shared contract, config, error-handling, and money-precision conventions (zod schema naming, wire discriminants, no-direct-process-env, decimal money).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  "url": "git+https://github.com/noctcore/eslint-plugins.git",
39
39
  "directory": "packages/eslint-plugin-contracts"
40
40
  },
41
- "homepage": "https://github.com/noctcore/eslint-plugins/tree/main/packages/eslint-plugin-contracts",
41
+ "homepage": "https://noctcore.github.io/eslint-plugins/packages/contracts/",
42
42
  "bugs": "https://github.com/noctcore/eslint-plugins/issues",
43
43
  "scripts": {
44
44
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
@@ -46,7 +46,7 @@
46
46
  "test": "vitest run"
47
47
  },
48
48
  "dependencies": {
49
- "@noctcore/eslint-utils": "^0.1.1",
49
+ "@noctcore/eslint-utils": "^0.1.2",
50
50
  "@typescript-eslint/utils": "^8.61.1"
51
51
  },
52
52
  "peerDependencies": {
@@ -55,11 +55,11 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "@noctcore/eslint-test-utils": "workspace:*",
58
- "@types/node": "^22.0.0",
59
- "@typescript-eslint/parser": "^8.61.1",
60
- "@typescript-eslint/rule-tester": "^8.61.1",
58
+ "@types/node": "^22.20.3",
59
+ "@typescript-eslint/parser": "^8.70.0",
60
+ "@typescript-eslint/rule-tester": "^8.70.0",
61
61
  "tsup": "^8.5.1",
62
62
  "typescript": "^5.6.0",
63
- "vitest": "^4"
63
+ "vitest": "^5"
64
64
  }
65
65
  }