@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 +74 -32
- package/dist/index.cjs +7 -4
- package/dist/index.d.cts +27 -26
- package/dist/index.d.ts +27 -26
- package/dist/index.js +7 -4
- package/docs/rules/env-var-schema-parity.md +10 -2
- package/docs/rules/fetch-must-check-ok.md +18 -6
- package/docs/rules/money-must-be-decimal.md +14 -5
- package/docs/rules/no-direct-process-env.md +10 -2
- package/docs/rules/no-error-stringify.md +10 -0
- package/docs/rules/require-error-cause.md +17 -13
- package/docs/rules/require-registered-keys.md +13 -4
- package/docs/rules/require-schema-parse-at-boundary.md +12 -5
- package/docs/rules/restrict-throw-to-taxonomy.md +10 -4
- package/docs/rules/schema-enum-field-consistency.md +14 -0
- package/docs/rules/translation-key-exists.md +14 -10
- package/docs/rules/wire-message-naming.md +9 -1
- package/docs/rules/zod-schema-naming.md +10 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,36 +1,73 @@
|
|
|
1
1
|
# @noctcore/eslint-plugin-contracts
|
|
2
2
|
|
|
3
|
-
Rules for shared **contract** conventions
|
|
4
|
-
stringification, direct `process.env` access
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
## Opt-in rules
|
|
24
50
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
60
|
+
files: ['**/*.{ts,tsx}'],
|
|
61
|
+
languageOptions: { parser: tsParser },
|
|
31
62
|
rules: {
|
|
32
|
-
'noctcore-contracts/
|
|
33
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
| [`
|
|
49
|
-
| [`
|
|
50
|
-
| [`
|
|
51
|
-
| [`
|
|
52
|
-
| [`
|
|
53
|
-
| [`
|
|
54
|
-
| [`require-
|
|
55
|
-
| [`schema-
|
|
56
|
-
| [`
|
|
57
|
-
| [`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
`
|
|
61
|
-
|
|
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.
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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", [],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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", [],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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", [],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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", [],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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],
|
|
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.
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
111
|
+
## When not to use it
|
|
103
112
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
55
|
+
## What it does not flag
|
|
62
56
|
|
|
63
|
-
|
|
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
|
-
|
|
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'],
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
59
|
-
"@typescript-eslint/parser": "^8.
|
|
60
|
-
"@typescript-eslint/rule-tester": "^8.
|
|
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": "^
|
|
63
|
+
"vitest": "^5"
|
|
64
64
|
}
|
|
65
65
|
}
|