@sarj/eslint-plugin 10.0.0 → 11.0.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,375 +1,49 @@
1
1
  # @sarj/eslint-plugin
2
2
 
3
- ## New in 9.11.0 disabled test assertions
3
+ Deterministic ESLint rules and presets for TypeScript, React, and modern Node.js
4
+ applications. The plugin complements ESLint's core and TypeScript ESLint rules;
5
+ it does not duplicate them.
4
6
 
5
- `no-comment-cruft` now recognizes commented-out `expect(...)` chains and
6
- `assert(...)` calls. A disabled assertion leaves a collected test passing while
7
- verifying nothing. JSDoc and assertion examples introduced by prose remain
8
- exempt, as do active assertions.
7
+ ## Use
9
8
 
10
- ## New in 9.9.0 static Next.js matchers
11
-
12
- `require-static-next-matcher` rejects runtime expressions in exported
13
- `middleware.ts` and `proxy.ts` matcher configuration. Next.js reads these values
14
- during static analysis, so identifiers, calls, concatenation, interpolated
15
- templates, and spreads can pass type checking but fail the production build.
16
-
17
- Custom ESLint rules for hypermodern TypeScript / React / Next.js projects.
18
-
19
- ```bash
20
- pnpm add -D @sarj/eslint-plugin
21
- ```
22
-
23
- ```js
24
- // eslint.config.mjs
25
- import sarj from "@sarj/eslint-plugin";
26
- export default [sarj.configs.recommended];
27
- ```
28
-
29
- 66 rules. Each source under `src/rules/` states one concise claim and links to
30
- its paired tests. The definition and named test cases are the complete rule
31
- specification, and `meta.docs.url` points directly to those executable examples.
32
-
33
- Presets: `recommended` (warn-first), `strict` (every general-profile rule at its declared strict severity), `style-guide` (formatting/naming subset). Application-only rules are exported through `applicationOnlyRules` and configured by the application profile. The two-sentence comment rule warns; its three-sentence companion errors.
34
-
35
- ## New in 9.8.0 — application library policy adapters
36
-
37
- `no-restricted-library-load` extends a configured dependency policy to runtime
38
- loading forms that ESLint's `no-restricted-imports` does not cover: literal
39
- `import()`, unshadowed `require()` / `require.resolve()`, and TypeScript
40
- `import x = require(...)`. It matches a package and all of its subpaths, but
41
- deliberately ignores computed names and shadowed local `require` functions.
42
-
43
- `prefer-native-random-uuid` reports resolved, zero-argument UUID v4 calls from
44
- the `uuid` package and suggests `globalThis.crypto.randomUUID()`. The suggestion
45
- changes only the call; import cleanup remains explicit. Other UUID versions,
46
- custom-randomness arguments, re-exports, and function references are untouched.
47
-
48
- Neither rule is in the general presets. The application profile configures them
49
- for its Node 22.13+ runtime contract and supplies catalog entries to the loader
50
- rule:
51
-
52
- ```js
53
- rules: {
54
- "@sarj/no-restricted-library-load": [
55
- "error",
56
- {
57
- libraries: [
58
- {
59
- id: "LIB101",
60
- module: "axios",
61
- replacement: "Ky",
62
- note: "The APIs are not drop-in equivalents.",
63
- },
64
- ],
65
- },
66
- ],
67
- "@sarj/prefer-native-random-uuid": "error",
68
- }
69
- ```
70
-
71
- ## Renamed in 7.0.0, aliases deleted in 9.0.0 (breaking)
72
-
73
- | Old name | New name |
74
- | --- | --- |
75
- | `@sarj/jsdoc-restates-signature` | `@sarj/no-restated-jsdoc` |
76
- | `@sarj/no-async-callback-in-waitfor` | `@sarj/no-async-callback-in-wait-for` |
77
- | `@sarj/strict-test-assertions` | `@sarj/prefer-whole-object-assertion` |
78
- | `@sarj/trailing-value-narration` | `@sarj/no-trailing-value-narration` |
79
-
80
- 7.0.0 kept each old name registered as a deprecated alias. 9.0.0 deletes them, so
81
- the new names are the only names — and an old name left in a config, an
82
- `eslint-disable` comment or a suppressions baseline now makes ESLint exit 2 with
83
- `Could not find "@sarj/<rule>" in plugin "@sarj"` before it reads a file.
84
-
85
- Run `sarj-lint-configs doctor` before upgrading: it reads the shipped rule ledger
86
- and names every file holding an old name, with the replacement. The map is also
87
- exported for codemods:
88
-
89
- ```js
90
- import { renamedRules } from "@sarj/eslint-plugin";
91
- ```
92
-
93
- The rename map and migration behavior live in
94
- [`src/rules/_renames.ts`](src/rules/_renames.ts) and its tests.
95
-
96
- ## New in 4.1.0 — `no-hand-rolled-sleep`
97
-
98
- `new Promise((resolve) => setTimeout(resolve, ms))` is `node:timers/promises`'s
99
- `setTimeout` rewritten by hand, minus the `AbortSignal` — so it is a capability
100
- loss, not verbosity. The hand-rolled sleep holds a live timer that nothing can
101
- clear, and its mirror image, the `Promise.race([work, rejectAfter(ms)])` timeout
102
- arm, leaks the timer the other way: when `work` wins, nothing clears it and it
103
- keeps the event loop alive until it fires.
104
-
105
- Shipped only after confirming nothing already enabled reports this position. The
106
- enabled set was resolved with `ESLint#calculateConfigForFile` against the shipped
107
- `eslint.strict.mjs` (204 rules before this one) and a file containing every
108
- shape was linted through it: no report. `eslint-plugin-unicorn` 72 has no
109
- promisified-timer rule among its 341; `unicorn/prefer-abort-signal-timeout` covers the
110
- `AbortController` + `setTimeout` idiom and not the race arm; core
111
- `no-promise-executor-return` fires on the concise-arrow spelling only and its
112
- remedy ("add braces") entrenches the hand-rolled sleep. The polling-loop variant
113
- (`while (!done) await sleep(ms)`) is deliberately absent — core `no-await-in-loop`
114
- is already enabled and reports that exact position.
115
-
116
- Measured over 1,471 files containing `setTimeout` across 15 OSS repos (hono,
117
- tRPC, drizzle-orm, undici, vitest, got, cal.com, documenso, dub, formbricks,
118
- midday, openstatus, papermark, unkey, zod) and seven internal ones: 85 + 11
119
- sleeps and 2 + 1 leaky race arms at the default settings, **0 false positives**.
120
-
121
- `checkClientModules` is the option that matters. A browser or React Native
122
- bundle cannot import `node:timers/promises` and the web platform ships no
123
- equivalent, so client modules are skipped by default — 79% of the internal
124
- corpus's occurrences live in `.tsx` components where the fix cannot be applied.
125
- Turn it on only in a tree where every file resolves `node:` builtins. The race
126
- message is reported everywhere regardless: `AbortSignal.timeout` is on the web
127
- platform too.
128
-
129
- | Rule | What it catches | Preset |
130
- |---|---|---|
131
- | `no-hand-rolled-spinner` | Intrinsic elements styled as Tailwind border-ring loading spinners outside the design system. | error / error |
132
- | `prefer-input-group-search` | Search icons and shared Input controls composed without the shared InputGroup primitive. | error / error |
133
- | `prefer-shadcn-primitives` | Deterministically visible raw JSX controls used instead of shared shadcn primitives. Unknown and hidden input types are excluded. | application profile: warn |
134
- | `require-static-next-matcher` | Dynamic values in exported Next.js middleware and proxy matcher configuration. | error / error |
135
- | `no-hand-rolled-sleep` | `new Promise((r) => setTimeout(r, ms))` in any spelling, and an uncleared `Promise.race`/`Promise.any` timeout arm. Options: `checkClientModules` (default `false`), `allowIn`. | warn / error |
136
-
137
- `prefer-shadcn-primitives` is application-only because generic plugin consumers
138
- may use another design system or intentionally expose native controls. It starts
139
- at warning with 53 measured adoption findings across four application-profile
140
- consumers; two additional consumers are clean. Promote it only after those
141
- findings are removed, and keep it disabled inside `components/ui` and
142
- `components/design-system`, where shared primitives must wrap native elements.
143
-
144
- ## New in 2.14.0 — `no-tautological-expect`
145
-
146
- The TS half of SARJ057. An `expect(...)` whose operands are all literals has
147
- already decided its outcome before the test runs — `expect(true).toBe(true)`
148
- passes if you delete the module under test.
149
-
150
- Python has caught the assertion-*free* test since 0.15.0 (`SARJ043
151
- zero-assertion-test`) and had no TypeScript counterpart, which is exactly how
152
- `expect(true).toBe(true); // placeholder` survived in an internal suite named for
153
- the behaviour it was supposed to check: the file *has* an assertion, so nothing
154
- was looking at it.
155
-
156
- | Rule | What it catches | Preset |
157
- |---|---|---|
158
- | `no-tautological-expect` | `expect(<literal>).toBe/toEqual/toStrictEqual(<textually identical literal>)`, and `expect(<literal>).toBeDefined()/toBeTruthy()/toBeNull()/…` — a zero-argument matcher on a literal receiver. | warn / error |
159
-
160
- **The narrowness is the rule.** The obvious generalisation — "flag a comparison
161
- of a thing with itself" — measures ~95% false positives:
162
- `expect(hash([o])).toEqual(hash([o]))` is a *determinism* test,
163
- `expect(memo(x)).toBe(memo(x))` a *memoization* test, `expect(a).toEqual(a)` on a
164
- value with custom equality a *reflexivity* test. All three can genuinely fail. So
165
- an identifier, member-expression or call operand is never enough: both sides must
166
- be literals, and textually identical ones. A modified chain (`.not`, `.resolves`,
167
- `.rejects`), a spread, an interpolated template literal, and two *different*
168
- literals are all left alone.
169
-
170
- Measured before shipping: **3 hits across 5,819 `.ts`/`.tsx` files** (1,003 of
171
- them test files, where the rule is active) — six internal repos plus `got`,
172
- `hono`, `swr` and `trpc`. 3 true positives, **0 false positives**; every hit is
173
- an abandoned placeholder.
174
-
175
- ## New in 2.13.0 — the anti-comment-verbosity family
176
-
177
- From a 37,918-comment, nine-repo measurement study. All three are
178
- deletion-class, so each was validated against zod / swr / zustand / TanStack
179
- Query as well as the maintained repos. Each paired test suite records the
180
- false-positive shapes guarded by the implementation.
181
-
182
- | Rule | What it catches | Preset |
183
- |---|---|---|
184
- | `no-restated-comment` | A single-line comment whose every content word already appears on the statement below it. Defers to `no-comment-cruft` for the verb-led shape, so a comment is never reported twice. | warn / error |
185
- | `no-restated-jsdoc` | A JSDoc block whose description and `@param`/`@returns` only re-spell the signature. Offers a delete SUGGESTION, never an auto-`--fix`. | warn / error |
186
- | `no-trailing-value-narration` | `staleTime: 5 * 60 * 1000, // 5 minutes` — the unit belongs in the name, where it cannot drift. | warn / error |
187
- | `no-declaration-comment-wall` | The same judgement for an ENUM BODY or a CLASS BODY, which the rule below structurally cannot see. | warn / error |
188
- | `no-union-in-comment` | `kind: string; // 'pending' \| 'approved'` — the comment IS the union; write it as one so the compiler enforces it. | warn / error |
189
- | `no-type-member-comment-wall` | An object type whose member comments mostly re-spell the members' own names and types — the VOLUME arm of the family, reported once for the type. | warn / error |
190
-
191
- ## New in 2.9.0
192
-
193
- Both distilled from two years of PR-review comments across ~1,065 PRs.
194
-
195
- | Rule | What it catches | Preset |
196
- |---|---|---|
197
- | `no-zod-native-enum` | `z.nativeEnum(...)` and `z.enum(SomeTsEnum)` — the schema-layer back door around `no-enum`. Autofixes an inline string-literal object to `z.enum([...])`. | warn / error |
198
- | `prefer-zod-enum` | `z.union([z.literal("a"), z.literal("b")])` — autofixes closed string choices to the shorter, equivalent `z.enum(["a", "b"])`. | warn / error |
199
- | `prefer-zod-infer` | An `interface`/`type` that restates a Zod schema declared in the same module instead of deriving it with `z.infer`. Options: `ignoreTypeNames`, `requireIdenticalShape` (default `true`). | warn / error |
200
- | `prefer-module-level-constant` | A literal-only `const` collection (array, object, `Set`, `Map`, `Object.freeze`) or non-global regex declared inside a function body, never mutated and never escaping — hoist it to module scope. Options: `minElements` (default 3), `checkRegex`, `ignoreTestFiles`. | warn / error |
201
- | `prefer-module-level-schema` | A Zod schema built inside a function body that closes over nothing the function owns — hoist it to module scope instead of rebuilding it per call, per request, per render. Silent when it references a parameter, local, type parameter, local type, or `this` (that is a schema FACTORY), when it is already memoized, and inside `z.lazy`. Options: `factories` (default: the object-like composites), `minProperties` (default 1), `ignoreTestFiles`. | warn / error |
202
- | `prefer-non-nullable-collection` | A required array property or direct alias explicitly combined with `null`/`undefined`, creating two equivalent empty states. Optional API fields are excluded because omission can be meaningful. | warn / error |
203
-
204
- ## Options
205
-
206
- ### Declare your logger (`loggerNames` / `logFunctions`)
207
-
208
- Three rules decide whether a call writes to a log sink: `no-log-only-catch`,
209
- `no-sentinel-return-on-catch`, `no-secret-in-log`. Out of the box they recognise
210
- a log method on a logger *receiver* (`console.error`, `logger.warn`,
211
- `this.logger.info`). A structured logger is usually a free *function* taking a
212
- meta object, which has no receiver — declare it once and all of them see it:
9
+ Sarj repositories should let `sarj-standards setup` install the tested peer set
10
+ and generate the integration. Direct ESLint consumers can install the package
11
+ and select a preset:
213
12
 
214
13
  ```js
215
- const logging = { logFunctions: ["logEvent"], loggerNames: ["obs"] };
216
-
217
- rules: {
218
- "@sarj/no-log-only-catch": ["error", logging],
219
- "@sarj/no-sentinel-return-on-catch": ["error", logging],
220
- "@sarj/no-secret-in-log": ["error", logging],
221
- }
222
- ```
223
-
224
- - `logFunctions` — free functions (or methods) that log: `logEvent("x", { err })`.
225
- - `loggerNames` — extra logger *receiver* names, added to the built-in set.
226
-
227
- This suppresses "swallows the error without logging it" on a correctly-logged
228
- degraded return, and — importantly — makes `no-secret-in-log` inspect those
229
- calls, which it could not do before: `logEvent("slack.auth", { botToken })` was
230
- previously never examined.
231
-
232
- ### `zod-naming-convention`: `convention`
233
-
234
- `"either"` (default) accepts both the `Z`-prefix (`ZUser`) and the `Schema`
235
- suffix (`userSchema`) — the two conventions `require-zod-form-validation`
236
- already recognises. Set `"prefix"` or `"suffix"` to pin one:
237
-
238
- ```js
239
- "@sarj/zod-naming-convention": ["error", { convention: "suffix" }]
240
- ```
241
-
242
- ### `prefer-zod-infer`: `requireIdenticalShape` / `ignoreTypeNames`
243
-
244
- By default the rule only reports a type whose members match its schema's keys
245
- one for one — same names, same optionality, same nullability, no `.transform()`
246
- anywhere in the pair. On a 30,759-file, 17-repo sweep that is 5 reports and 5
247
- true positives. Drop the shape comparison to report on name correlation alone
248
- (8 reports on the same corpus, 1 of them noise, and it catches twins that have
249
- already drifted):
250
-
251
- ```js
252
- "@sarj/prefer-zod-infer": ["error", { requireIdenticalShape: false }]
253
- ```
254
-
255
- `ignoreTypeNames` takes regex sources matched against the declared type name,
256
- for the pair that is genuinely meant to be maintained by hand:
257
-
258
- ```js
259
- "@sarj/prefer-zod-infer": ["error", { ignoreTypeNames: ["^LegacyUser$"] }]
260
- ```
261
-
262
- ### `prefer-string-literal-union`: `ignoreFields`
263
-
264
- Field names whose value set is owned by a vendor and genuinely open (a Slack
265
- `event.subtype`, a Resend `bounceType`). Narrowing to a union you don't control
266
- would be wrong, not better:
267
-
268
- ```js
269
- "@sarj/prefer-string-literal-union": ["warn", { ignoreFields: ["subtype", "bounceType"] }]
270
- ```
271
-
272
- ### `no-enum`: `ignoreFiles`
273
-
274
- Glob patterns whose files opt out (generated code already opts out by default).
275
-
276
- ## Configurable rules
277
-
278
- Most rules take no options. These do, because they encode a codebase's
279
- architecture rather than a language fact — the defaults describe one convention
280
- and every repo gets to name its own.
281
-
282
- | Rule | Option | Default | Effect |
283
- |---|---|---|---|
284
- | `no-raw-fetch-outside-clients` | `allow` | client / vendor-wrapper path patterns | Extra files exempt from the "no bare `fetch`" rule (test files are exempt unconditionally) |
285
- | `no-dynamic-sql` | `methods` | `["prepare", "exec", "query"]` | Statement-taking methods to inspect |
286
- | `no-storage-in-stateless-modules` | `modules` | `[]` (rule off) | Directories declared stateless |
287
- | `no-storage-in-stateless-modules` | `methods` | `["prepare", "put", "getWithMetadata"]` | Storage methods to flag |
288
- | `no-hand-rolled-sleep` | `checkClientModules` | `false` | Also report the sleep form in browser/React Native modules |
289
- | `no-hand-rolled-sleep` | `allowIn` | `[]` | Glob patterns for a sanctioned sleep wrapper module |
290
-
291
- The path options on the first three rules are **regular-expression sources
292
- matched against the absolute filename**, not globs — so they can express both
293
- path separators. Test files no longer need to appear there: the rule delegates
294
- to the shared `isTestFile` predicate, so supplying `allow` replaces only the
295
- production exemptions and cannot accidentally un-exempt a test tree.
296
- `allowIn` is the exception, on `no-hand-rolled-sleep` as on
297
- `require-fetch-timeout`: it takes minimatch-ish **globs**, also matched against
298
- the absolute path, so anchor them with a `**/` prefix. Supplying an option
299
- **replaces** the default rather than extending it.
300
-
301
- `no-storage-in-stateless-modules` is a **no-op until `modules` is set**. The
302
- method names alone (`put`, `prepare`) carry no type information, so the rule is
303
- only meaningful once it is pointed at the directories a team has actually
304
- declared stateless.
305
-
306
- ```js
307
- // eslint.config.mjs
308
14
  import sarj from "@sarj/eslint-plugin";
309
15
 
310
16
  export default [
311
17
  sarj.configs.strict,
312
- {
313
- rules: {
314
- // This repo keeps its HTTP layer in `lib/api/`, not `clients/`.
315
- "@sarj/no-raw-fetch-outside-clients": [
316
- "error",
317
- { allow: ["[\\\\/]lib[\\\\/]api[\\\\/]"] },
318
- ],
319
- // Declare which modules must stay stateless.
320
- "@sarj/no-storage-in-stateless-modules": [
321
- "error",
322
- { modules: ["[\\\\/]engineer-digest[\\\\/]"] },
323
- ],
324
- },
325
- },
326
18
  ];
327
- ```
328
19
 
329
- Tiering: `no-dynamic-sql` is in both presets (an injection guard with a low
330
- false-positive rate, relevant to any repo touching SQL). The two architectural
331
- rules are **`strict`-only**, since they need per-repo configuration to say
332
- anything useful.
333
-
334
- `no-generic-single-export-module` warns when a `utils`, `helpers`, `common`,
335
- `constants`, or similarly generic module has one runtime export that already
336
- provides a precise filename. Ambiguous re-exports, type-only modules,
337
- conventional framework filenames, tests, generated files, and CommonJS modules
338
- are excluded. It is warning-first while corpus evidence accumulates.
20
+ // For staged adoption:
21
+ // export default [sarj.configs.recommended];
22
+ ```
339
23
 
340
- `stepdown` is likewise warning-first: measured adoption is intentionally broad,
341
- so it only owns ordering within a private-method band. The strict preset gives
342
- public/protected/private band ordering to `@typescript-eslint/member-ordering`
343
- and disables `perfectionist/sort-classes`, avoiding contradictory owners. The
344
- rule resolves module helpers, private methods, recursion and cycles, but leaves
345
- function-valued private fields and mutable receiver aliases alone.
24
+ Available presets are `recommended` for staged adoption and `strict` for the
25
+ full general policy. Application-only rules are exported separately because
26
+ they depend on an explicit runtime and library policy.
346
27
 
347
- `no-positional-tuple-return` now rejects every fixed multi-field tuple on an
348
- exported TypeScript boundary—including homogeneous, labelled, tagged, readonly,
349
- hook-style, interface, function-type, declaration and abstract-method returns.
350
- The runtime alternative is a named object. Private/local implementation helpers
351
- and sequence-shaped arrays remain outside the public-boundary rule.
28
+ Use `sarj-standards show peers` for the tested ESLint and parser dependency set.
29
+ Do not combine independent `latest` versions and assume they are compatible.
352
30
 
353
- ## Ported from `sarj_python_lint`
31
+ ## Rules and suppressions
354
32
 
355
- Several rules are ports of the Python linter's SARJ rules, retuned for TypeScript. The false-positive tuning documented in the Python docstrings is ported with them — that tuning is the valuable part.
33
+ Every rule is registered in `src/index.ts`; its source under `src/rules/` and
34
+ paired test under `tests/rules/` are the authoritative specification. Run
35
+ `sarj-standards show rules` for the current machine-readable catalog.
356
36
 
357
- | TypeScript rule | Python | What it prevents |
358
- | --- | --- | --- |
359
- | `prefer-constant-time-secret-compare` | SARJ011 | Byte-by-byte secret recovery through the timing of a short-circuiting `===` on a token / signature / HMAC. On Workers the fix is `crypto.subtle.timingSafeEqual` over equal-length digests. |
360
- | `no-secret-in-log` | SARJ012 | Credentials persisted into log sinks. |
361
- | `store-insert-requires-on-conflict` | SARJ018 | Duplicate rows — or unique-constraint failures that re-trigger the handler — when a cron re-runs or a queue message is redelivered. |
362
- | `no-select-star` | SARJ021 | An implicit row contract that changes silently when a column is added or reordered. |
363
- | `no-offset-pagination` | SARJ025 | O(N)-per-page scans, and rows repeated or skipped when the offset window shifts under concurrent inserts. |
364
- | `no-repeated-string-literal` | SARJ024 | Copies of a structured literal (SQL, column lists, prompt templates) drifting apart when only one is edited. |
365
- | `no-positional-tuple-return` | SARJ026 | Public APIs exposing multi-field tuples—even labelled or hook-style tuples—instead of named objects. |
366
- | `no-sleep-in-test-body` | SARJ031 | Tests that assert on wall-clock time and flake under CI load. |
367
- | `no-fat-try-blocks` | SARJ007 | Over-broad `catch` handlers swallowing unrelated failures. |
368
- | `no-cors-wildcard-with-credentials` | SARJ008 | Credentialed cross-origin requests from any origin. |
369
- | `single-public-export` | SARJ022 | Modules with no single obvious entry point. |
370
- | `stepdown` | SARJ023 | A private helper placed above its only direct same-scope caller. Indirect references, callbacks, cycles, overloads, and multi-caller helpers are excluded. |
371
- | `prefer-string-literal-union` | SARJ006 | An open `string` where a closed set is intended. |
37
+ Prefer the smallest line-scoped `eslint-disable-next-line @sarj/rule-name`
38
+ suppression and explain why the exceptional code is intentional. Unknown or
39
+ retired rule names are configuration errors.
372
40
 
373
- Shared helpers live in `src/rules/_*.ts` (`_secret-names.ts`, `_sql.ts`, `_logging.ts`, `_paths.ts`, `_tailwind.ts`) so related rules cannot diverge on what counts as a secret, a SQL statement, a logging call, or a test file.
41
+ New rules must prove that upstream ESLint and TypeScript ESLint cannot express
42
+ the policy and must pass focused tests plus the repository corpus evaluation
43
+ described in the root contribution guide.
374
44
 
375
- Deliberately **not** ported: `no-unreachable-after-terminal` (SARJ010) is already covered by `allowUnreachableCode: false` in `@sarj/tsconfig` plus ESLint core `no-unreachable`; `no-aggregation-in-store-query` (SARJ020) assumes a Postgres-OLTP / columnar-mirror split that D1 does not have; `no-query-with-many-joins` (SARJ019), `prefer-class-row`, `prefer-struct-over-namedtuple`, `prefer-timedelta-for-durations`, and `no-fstring-in-log` have no TypeScript defect class or target API; `prefer-str-enum` is covered by `prefer-string-literal-union` + `no-enum`.
45
+ Renamed rules fail closed. Replace `jsdoc-restates-signature` with
46
+ `no-restated-jsdoc`, `no-async-callback-in-waitfor` with
47
+ `no-async-callback-in-wait-for`, `strict-test-assertions` with
48
+ `prefer-whole-object-assertion`, and `trailing-value-narration` with
49
+ `no-trailing-value-narration`.
package/dist/index.cjs CHANGED
@@ -12382,7 +12382,7 @@ var rules = {
12382
12382
  };
12383
12383
  var meta = {
12384
12384
  name: "@sarj/eslint-plugin",
12385
- version: "10.0.0"
12385
+ version: "11.0.1"
12386
12386
  };
12387
12387
  var applicationOnlyRules = [
12388
12388
  "no-restricted-library-load",
@@ -12390,68 +12390,68 @@ var applicationOnlyRules = [
12390
12390
  "prefer-shadcn-primitives"
12391
12391
  ];
12392
12392
  var recommendedRules = {
12393
- "@sarj/duplicate-test-body": "warn",
12394
- "@sarj/enforce-file-structure": "warn",
12395
- "@sarj/no-async-callback-in-wait-for": "warn",
12396
- "@sarj/no-client-side-data-fetching": "warn",
12397
- "@sarj/no-comment-cruft": "warn",
12398
- "@sarj/no-conditional-in-test": "warn",
12399
- "@sarj/no-cors-wildcard-with-credentials": "warn",
12400
- "@sarj/no-dynamic-sql": "warn",
12401
- "@sarj/no-fat-try-blocks": "warn",
12402
- "@sarj/no-hand-rolled-sleep": "warn",
12393
+ "@sarj/duplicate-test-body": "error",
12394
+ "@sarj/enforce-file-structure": "error",
12395
+ "@sarj/no-async-callback-in-wait-for": "error",
12396
+ "@sarj/no-client-side-data-fetching": "error",
12397
+ "@sarj/no-comment-cruft": "error",
12398
+ "@sarj/no-conditional-in-test": "error",
12399
+ "@sarj/no-cors-wildcard-with-credentials": "error",
12400
+ "@sarj/no-dynamic-sql": "error",
12401
+ "@sarj/no-fat-try-blocks": "error",
12402
+ "@sarj/no-hand-rolled-sleep": "error",
12403
12403
  "@sarj/no-hand-rolled-spinner": "error",
12404
- "@sarj/no-insecure-random-id": "warn",
12405
- "@sarj/no-json-stringify-error": "warn",
12406
- "@sarj/no-log-only-catch": "warn",
12407
- "@sarj/no-long-comment": "warn",
12408
- "@sarj/no-generic-single-export-module": "warn",
12409
- "@sarj/no-offset-pagination": "warn",
12410
- "@sarj/no-positional-tuple-return": "warn",
12411
- "@sarj/no-repeated-string-literal": "warn",
12412
- "@sarj/no-restated-comment": "warn",
12413
- "@sarj/no-restated-jsdoc": "warn",
12414
- "@sarj/no-secret-in-log": "warn",
12415
- "@sarj/no-select-star": "warn",
12416
- "@sarj/no-sentinel-return-on-catch": "warn",
12417
- "@sarj/no-silent-promise-catch": "warn",
12418
- "@sarj/no-sleep-in-test-body": "warn",
12419
- "@sarj/no-string-concat-in-loop": "warn",
12420
- "@sarj/no-tautological-expect": "warn",
12421
- "@sarj/no-typed-doc-sections": "warn",
12422
- "@sarj/no-trailing-value-narration": "warn",
12423
- "@sarj/no-declaration-comment-wall": "warn",
12424
- "@sarj/no-union-in-comment": "warn",
12425
- "@sarj/no-type-member-comment-wall": "warn",
12426
- "@sarj/no-unnecessary-use-client": "warn",
12427
- "@sarj/no-unsafe-mock-casting": "warn",
12428
- "@sarj/no-zod-native-enum": "warn",
12429
- "@sarj/test-loops-over-literal-cases": "warn",
12404
+ "@sarj/no-insecure-random-id": "error",
12405
+ "@sarj/no-json-stringify-error": "error",
12406
+ "@sarj/no-log-only-catch": "error",
12407
+ "@sarj/no-long-comment": "error",
12408
+ "@sarj/no-generic-single-export-module": "error",
12409
+ "@sarj/no-offset-pagination": "error",
12410
+ "@sarj/no-positional-tuple-return": "error",
12411
+ "@sarj/no-repeated-string-literal": "error",
12412
+ "@sarj/no-restated-comment": "error",
12413
+ "@sarj/no-restated-jsdoc": "error",
12414
+ "@sarj/no-secret-in-log": "error",
12415
+ "@sarj/no-select-star": "error",
12416
+ "@sarj/no-sentinel-return-on-catch": "error",
12417
+ "@sarj/no-silent-promise-catch": "error",
12418
+ "@sarj/no-sleep-in-test-body": "error",
12419
+ "@sarj/no-string-concat-in-loop": "error",
12420
+ "@sarj/no-tautological-expect": "error",
12421
+ "@sarj/no-typed-doc-sections": "error",
12422
+ "@sarj/no-trailing-value-narration": "error",
12423
+ "@sarj/no-declaration-comment-wall": "error",
12424
+ "@sarj/no-union-in-comment": "error",
12425
+ "@sarj/no-type-member-comment-wall": "error",
12426
+ "@sarj/no-unnecessary-use-client": "error",
12427
+ "@sarj/no-unsafe-mock-casting": "error",
12428
+ "@sarj/no-zod-native-enum": "error",
12429
+ "@sarj/test-loops-over-literal-cases": "error",
12430
12430
  "@sarj/prefer-constant-time-secret-compare": "error",
12431
- "@sarj/prefer-discriminated-union": "warn",
12431
+ "@sarj/prefer-discriminated-union": "error",
12432
12432
  "@sarj/prefer-input-group-search": "error",
12433
- "@sarj/prefer-immutable-module-constant": "warn",
12434
- "@sarj/prefer-module-level-constant": "warn",
12435
- "@sarj/prefer-module-level-schema": "warn",
12436
- "@sarj/prefer-non-nullable-collection": "warn",
12437
- "@sarj/prefer-schema-for-api-payload": "warn",
12438
- "@sarj/prefer-semantic-colors": ["warn", { requireSemanticTokens: true }],
12439
- "@sarj/prefer-server-actions": "warn",
12440
- "@sarj/prefer-string-literal-union": "warn",
12441
- "@sarj/prefer-whole-object-assertion": "warn",
12442
- "@sarj/prefer-zod-enum": "warn",
12443
- "@sarj/prefer-zod-infer": "warn",
12444
- "@sarj/require-assert-never": "warn",
12445
- "@sarj/require-fetch-timeout": "warn",
12446
- "@sarj/require-interface-for-injected-service": "warn",
12433
+ "@sarj/prefer-immutable-module-constant": "error",
12434
+ "@sarj/prefer-module-level-constant": "error",
12435
+ "@sarj/prefer-module-level-schema": "error",
12436
+ "@sarj/prefer-non-nullable-collection": "error",
12437
+ "@sarj/prefer-schema-for-api-payload": "error",
12438
+ "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
12439
+ "@sarj/prefer-server-actions": "error",
12440
+ "@sarj/prefer-string-literal-union": "error",
12441
+ "@sarj/prefer-whole-object-assertion": "error",
12442
+ "@sarj/prefer-zod-enum": "error",
12443
+ "@sarj/prefer-zod-infer": "error",
12444
+ "@sarj/require-assert-never": "error",
12445
+ "@sarj/require-fetch-timeout": "error",
12446
+ "@sarj/require-interface-for-injected-service": "error",
12447
12447
  "@sarj/require-static-next-matcher": "error",
12448
12448
  "@sarj/require-zod-form-validation": "error",
12449
- "@sarj/store-insert-requires-on-conflict": "warn",
12450
- "@sarj/stepdown": "warn",
12451
- "@sarj/zod-naming-convention": "warn"
12449
+ "@sarj/store-insert-requires-on-conflict": "error",
12450
+ "@sarj/stepdown": "error",
12451
+ "@sarj/zod-naming-convention": "error"
12452
12452
  };
12453
12453
  var strictRules = {
12454
- "@sarj/duplicate-test-body": "warn",
12454
+ "@sarj/duplicate-test-body": "error",
12455
12455
  "@sarj/enforce-file-structure": "error",
12456
12456
  "@sarj/no-async-callback-in-wait-for": "error",
12457
12457
  "@sarj/no-client-side-data-fetching": "error",
@@ -12467,7 +12467,7 @@ var strictRules = {
12467
12467
  "@sarj/no-json-stringify-error": "error",
12468
12468
  "@sarj/no-log-only-catch": "error",
12469
12469
  "@sarj/no-long-comment": "error",
12470
- "@sarj/no-generic-single-export-module": "warn",
12470
+ "@sarj/no-generic-single-export-module": "error",
12471
12471
  "@sarj/no-offset-pagination": "error",
12472
12472
  "@sarj/no-positional-tuple-return": "error",
12473
12473
  "@sarj/no-raw-env": "error",
@@ -12491,14 +12491,14 @@ var strictRules = {
12491
12491
  "@sarj/no-unnecessary-use-client": "error",
12492
12492
  "@sarj/no-unsafe-mock-casting": "error",
12493
12493
  "@sarj/no-zod-native-enum": "error",
12494
- "@sarj/test-loops-over-literal-cases": "warn",
12494
+ "@sarj/test-loops-over-literal-cases": "error",
12495
12495
  "@sarj/prefer-constant-time-secret-compare": "error",
12496
- "@sarj/prefer-discriminated-union": "warn",
12496
+ "@sarj/prefer-discriminated-union": "error",
12497
12497
  "@sarj/prefer-input-group-search": "error",
12498
- "@sarj/prefer-immutable-module-constant": "warn",
12498
+ "@sarj/prefer-immutable-module-constant": "error",
12499
12499
  "@sarj/prefer-module-level-constant": "error",
12500
12500
  "@sarj/prefer-module-level-schema": "error",
12501
- "@sarj/prefer-non-nullable-collection": "warn",
12501
+ "@sarj/prefer-non-nullable-collection": "error",
12502
12502
  "@sarj/prefer-schema-for-api-payload": "error",
12503
12503
  "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
12504
12504
  "@sarj/prefer-server-actions": "error",
@@ -12506,13 +12506,13 @@ var strictRules = {
12506
12506
  "@sarj/prefer-whole-object-assertion": "error",
12507
12507
  "@sarj/prefer-zod-enum": "error",
12508
12508
  "@sarj/prefer-zod-infer": "error",
12509
- "@sarj/require-assert-never": "warn",
12509
+ "@sarj/require-assert-never": "error",
12510
12510
  "@sarj/require-fetch-timeout": "error",
12511
- "@sarj/require-interface-for-injected-service": "warn",
12511
+ "@sarj/require-interface-for-injected-service": "error",
12512
12512
  "@sarj/require-static-next-matcher": "error",
12513
12513
  "@sarj/require-zod-form-validation": "error",
12514
12514
  "@sarj/store-insert-requires-on-conflict": "error",
12515
- "@sarj/stepdown": "warn",
12515
+ "@sarj/stepdown": "error",
12516
12516
  "@sarj/zod-naming-convention": "error"
12517
12517
  };
12518
12518
  var plugin = {
package/dist/index.d.cts CHANGED
@@ -77,7 +77,7 @@ interface RuleOptions {
77
77
  /**
78
78
  * @fileoverview _renames — every rule this plugin has renamed, old name to new; the old names no longer resolve, so this map is what says what to write instead.
79
79
  *
80
- * `sarj-standards repo sync-ledger` turns each entry into the consumer-facing ledger row.
80
+ * `sarj-standards maintain sync-ledger` turns each entry into the consumer-facing ledger row.
81
81
  *
82
82
  */
83
83
  declare const renamedRules: {
@@ -316,70 +316,70 @@ declare const rules: {
316
316
  /** Rules registered for application-profile configs but intentionally absent from general presets. */
317
317
  declare const applicationOnlyRules: readonly ["no-restricted-library-load", "prefer-native-random-uuid", "prefer-shadcn-primitives"];
318
318
  declare const recommendedRules: {
319
- readonly "@sarj/duplicate-test-body": "warn";
320
- readonly "@sarj/enforce-file-structure": "warn";
321
- readonly "@sarj/no-async-callback-in-wait-for": "warn";
322
- readonly "@sarj/no-client-side-data-fetching": "warn";
323
- readonly "@sarj/no-comment-cruft": "warn";
324
- readonly "@sarj/no-conditional-in-test": "warn";
325
- readonly "@sarj/no-cors-wildcard-with-credentials": "warn";
326
- readonly "@sarj/no-dynamic-sql": "warn";
327
- readonly "@sarj/no-fat-try-blocks": "warn";
328
- readonly "@sarj/no-hand-rolled-sleep": "warn";
319
+ readonly "@sarj/duplicate-test-body": "error";
320
+ readonly "@sarj/enforce-file-structure": "error";
321
+ readonly "@sarj/no-async-callback-in-wait-for": "error";
322
+ readonly "@sarj/no-client-side-data-fetching": "error";
323
+ readonly "@sarj/no-comment-cruft": "error";
324
+ readonly "@sarj/no-conditional-in-test": "error";
325
+ readonly "@sarj/no-cors-wildcard-with-credentials": "error";
326
+ readonly "@sarj/no-dynamic-sql": "error";
327
+ readonly "@sarj/no-fat-try-blocks": "error";
328
+ readonly "@sarj/no-hand-rolled-sleep": "error";
329
329
  readonly "@sarj/no-hand-rolled-spinner": "error";
330
- readonly "@sarj/no-insecure-random-id": "warn";
331
- readonly "@sarj/no-json-stringify-error": "warn";
332
- readonly "@sarj/no-log-only-catch": "warn";
333
- readonly "@sarj/no-long-comment": "warn";
334
- readonly "@sarj/no-generic-single-export-module": "warn";
335
- readonly "@sarj/no-offset-pagination": "warn";
336
- readonly "@sarj/no-positional-tuple-return": "warn";
337
- readonly "@sarj/no-repeated-string-literal": "warn";
338
- readonly "@sarj/no-restated-comment": "warn";
339
- readonly "@sarj/no-restated-jsdoc": "warn";
340
- readonly "@sarj/no-secret-in-log": "warn";
341
- readonly "@sarj/no-select-star": "warn";
342
- readonly "@sarj/no-sentinel-return-on-catch": "warn";
343
- readonly "@sarj/no-silent-promise-catch": "warn";
344
- readonly "@sarj/no-sleep-in-test-body": "warn";
345
- readonly "@sarj/no-string-concat-in-loop": "warn";
346
- readonly "@sarj/no-tautological-expect": "warn";
347
- readonly "@sarj/no-typed-doc-sections": "warn";
348
- readonly "@sarj/no-trailing-value-narration": "warn";
349
- readonly "@sarj/no-declaration-comment-wall": "warn";
350
- readonly "@sarj/no-union-in-comment": "warn";
351
- readonly "@sarj/no-type-member-comment-wall": "warn";
352
- readonly "@sarj/no-unnecessary-use-client": "warn";
353
- readonly "@sarj/no-unsafe-mock-casting": "warn";
354
- readonly "@sarj/no-zod-native-enum": "warn";
355
- readonly "@sarj/test-loops-over-literal-cases": "warn";
330
+ readonly "@sarj/no-insecure-random-id": "error";
331
+ readonly "@sarj/no-json-stringify-error": "error";
332
+ readonly "@sarj/no-log-only-catch": "error";
333
+ readonly "@sarj/no-long-comment": "error";
334
+ readonly "@sarj/no-generic-single-export-module": "error";
335
+ readonly "@sarj/no-offset-pagination": "error";
336
+ readonly "@sarj/no-positional-tuple-return": "error";
337
+ readonly "@sarj/no-repeated-string-literal": "error";
338
+ readonly "@sarj/no-restated-comment": "error";
339
+ readonly "@sarj/no-restated-jsdoc": "error";
340
+ readonly "@sarj/no-secret-in-log": "error";
341
+ readonly "@sarj/no-select-star": "error";
342
+ readonly "@sarj/no-sentinel-return-on-catch": "error";
343
+ readonly "@sarj/no-silent-promise-catch": "error";
344
+ readonly "@sarj/no-sleep-in-test-body": "error";
345
+ readonly "@sarj/no-string-concat-in-loop": "error";
346
+ readonly "@sarj/no-tautological-expect": "error";
347
+ readonly "@sarj/no-typed-doc-sections": "error";
348
+ readonly "@sarj/no-trailing-value-narration": "error";
349
+ readonly "@sarj/no-declaration-comment-wall": "error";
350
+ readonly "@sarj/no-union-in-comment": "error";
351
+ readonly "@sarj/no-type-member-comment-wall": "error";
352
+ readonly "@sarj/no-unnecessary-use-client": "error";
353
+ readonly "@sarj/no-unsafe-mock-casting": "error";
354
+ readonly "@sarj/no-zod-native-enum": "error";
355
+ readonly "@sarj/test-loops-over-literal-cases": "error";
356
356
  readonly "@sarj/prefer-constant-time-secret-compare": "error";
357
- readonly "@sarj/prefer-discriminated-union": "warn";
357
+ readonly "@sarj/prefer-discriminated-union": "error";
358
358
  readonly "@sarj/prefer-input-group-search": "error";
359
- readonly "@sarj/prefer-immutable-module-constant": "warn";
360
- readonly "@sarj/prefer-module-level-constant": "warn";
361
- readonly "@sarj/prefer-module-level-schema": "warn";
362
- readonly "@sarj/prefer-non-nullable-collection": "warn";
363
- readonly "@sarj/prefer-schema-for-api-payload": "warn";
364
- readonly "@sarj/prefer-semantic-colors": readonly ["warn", {
359
+ readonly "@sarj/prefer-immutable-module-constant": "error";
360
+ readonly "@sarj/prefer-module-level-constant": "error";
361
+ readonly "@sarj/prefer-module-level-schema": "error";
362
+ readonly "@sarj/prefer-non-nullable-collection": "error";
363
+ readonly "@sarj/prefer-schema-for-api-payload": "error";
364
+ readonly "@sarj/prefer-semantic-colors": readonly ["error", {
365
365
  readonly requireSemanticTokens: true;
366
366
  }];
367
- readonly "@sarj/prefer-server-actions": "warn";
368
- readonly "@sarj/prefer-string-literal-union": "warn";
369
- readonly "@sarj/prefer-whole-object-assertion": "warn";
370
- readonly "@sarj/prefer-zod-enum": "warn";
371
- readonly "@sarj/prefer-zod-infer": "warn";
372
- readonly "@sarj/require-assert-never": "warn";
373
- readonly "@sarj/require-fetch-timeout": "warn";
374
- readonly "@sarj/require-interface-for-injected-service": "warn";
367
+ readonly "@sarj/prefer-server-actions": "error";
368
+ readonly "@sarj/prefer-string-literal-union": "error";
369
+ readonly "@sarj/prefer-whole-object-assertion": "error";
370
+ readonly "@sarj/prefer-zod-enum": "error";
371
+ readonly "@sarj/prefer-zod-infer": "error";
372
+ readonly "@sarj/require-assert-never": "error";
373
+ readonly "@sarj/require-fetch-timeout": "error";
374
+ readonly "@sarj/require-interface-for-injected-service": "error";
375
375
  readonly "@sarj/require-static-next-matcher": "error";
376
376
  readonly "@sarj/require-zod-form-validation": "error";
377
- readonly "@sarj/store-insert-requires-on-conflict": "warn";
378
- readonly "@sarj/stepdown": "warn";
379
- readonly "@sarj/zod-naming-convention": "warn";
377
+ readonly "@sarj/store-insert-requires-on-conflict": "error";
378
+ readonly "@sarj/stepdown": "error";
379
+ readonly "@sarj/zod-naming-convention": "error";
380
380
  };
381
381
  declare const strictRules: {
382
- readonly "@sarj/duplicate-test-body": "warn";
382
+ readonly "@sarj/duplicate-test-body": "error";
383
383
  readonly "@sarj/enforce-file-structure": "error";
384
384
  readonly "@sarj/no-async-callback-in-wait-for": "error";
385
385
  readonly "@sarj/no-client-side-data-fetching": "error";
@@ -395,7 +395,7 @@ declare const strictRules: {
395
395
  readonly "@sarj/no-json-stringify-error": "error";
396
396
  readonly "@sarj/no-log-only-catch": "error";
397
397
  readonly "@sarj/no-long-comment": "error";
398
- readonly "@sarj/no-generic-single-export-module": "warn";
398
+ readonly "@sarj/no-generic-single-export-module": "error";
399
399
  readonly "@sarj/no-offset-pagination": "error";
400
400
  readonly "@sarj/no-positional-tuple-return": "error";
401
401
  readonly "@sarj/no-raw-env": "error";
@@ -419,14 +419,14 @@ declare const strictRules: {
419
419
  readonly "@sarj/no-unnecessary-use-client": "error";
420
420
  readonly "@sarj/no-unsafe-mock-casting": "error";
421
421
  readonly "@sarj/no-zod-native-enum": "error";
422
- readonly "@sarj/test-loops-over-literal-cases": "warn";
422
+ readonly "@sarj/test-loops-over-literal-cases": "error";
423
423
  readonly "@sarj/prefer-constant-time-secret-compare": "error";
424
- readonly "@sarj/prefer-discriminated-union": "warn";
424
+ readonly "@sarj/prefer-discriminated-union": "error";
425
425
  readonly "@sarj/prefer-input-group-search": "error";
426
- readonly "@sarj/prefer-immutable-module-constant": "warn";
426
+ readonly "@sarj/prefer-immutable-module-constant": "error";
427
427
  readonly "@sarj/prefer-module-level-constant": "error";
428
428
  readonly "@sarj/prefer-module-level-schema": "error";
429
- readonly "@sarj/prefer-non-nullable-collection": "warn";
429
+ readonly "@sarj/prefer-non-nullable-collection": "error";
430
430
  readonly "@sarj/prefer-schema-for-api-payload": "error";
431
431
  readonly "@sarj/prefer-semantic-colors": readonly ["error", {
432
432
  readonly requireSemanticTokens: true;
@@ -436,13 +436,13 @@ declare const strictRules: {
436
436
  readonly "@sarj/prefer-whole-object-assertion": "error";
437
437
  readonly "@sarj/prefer-zod-enum": "error";
438
438
  readonly "@sarj/prefer-zod-infer": "error";
439
- readonly "@sarj/require-assert-never": "warn";
439
+ readonly "@sarj/require-assert-never": "error";
440
440
  readonly "@sarj/require-fetch-timeout": "error";
441
- readonly "@sarj/require-interface-for-injected-service": "warn";
441
+ readonly "@sarj/require-interface-for-injected-service": "error";
442
442
  readonly "@sarj/require-static-next-matcher": "error";
443
443
  readonly "@sarj/require-zod-form-validation": "error";
444
444
  readonly "@sarj/store-insert-requires-on-conflict": "error";
445
- readonly "@sarj/stepdown": "warn";
445
+ readonly "@sarj/stepdown": "error";
446
446
  readonly "@sarj/zod-naming-convention": "error";
447
447
  };
448
448
  type FlatPreset = {
@@ -453,7 +453,7 @@ type FlatPreset = {
453
453
  declare const plugin: {
454
454
  readonly meta: {
455
455
  readonly name: "@sarj/eslint-plugin";
456
- readonly version: "10.0.0";
456
+ readonly version: "11.0.1";
457
457
  };
458
458
  readonly rules: {
459
459
  readonly "duplicate-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"duplicateTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
package/dist/index.d.ts CHANGED
@@ -77,7 +77,7 @@ interface RuleOptions {
77
77
  /**
78
78
  * @fileoverview _renames — every rule this plugin has renamed, old name to new; the old names no longer resolve, so this map is what says what to write instead.
79
79
  *
80
- * `sarj-standards repo sync-ledger` turns each entry into the consumer-facing ledger row.
80
+ * `sarj-standards maintain sync-ledger` turns each entry into the consumer-facing ledger row.
81
81
  *
82
82
  */
83
83
  declare const renamedRules: {
@@ -316,70 +316,70 @@ declare const rules: {
316
316
  /** Rules registered for application-profile configs but intentionally absent from general presets. */
317
317
  declare const applicationOnlyRules: readonly ["no-restricted-library-load", "prefer-native-random-uuid", "prefer-shadcn-primitives"];
318
318
  declare const recommendedRules: {
319
- readonly "@sarj/duplicate-test-body": "warn";
320
- readonly "@sarj/enforce-file-structure": "warn";
321
- readonly "@sarj/no-async-callback-in-wait-for": "warn";
322
- readonly "@sarj/no-client-side-data-fetching": "warn";
323
- readonly "@sarj/no-comment-cruft": "warn";
324
- readonly "@sarj/no-conditional-in-test": "warn";
325
- readonly "@sarj/no-cors-wildcard-with-credentials": "warn";
326
- readonly "@sarj/no-dynamic-sql": "warn";
327
- readonly "@sarj/no-fat-try-blocks": "warn";
328
- readonly "@sarj/no-hand-rolled-sleep": "warn";
319
+ readonly "@sarj/duplicate-test-body": "error";
320
+ readonly "@sarj/enforce-file-structure": "error";
321
+ readonly "@sarj/no-async-callback-in-wait-for": "error";
322
+ readonly "@sarj/no-client-side-data-fetching": "error";
323
+ readonly "@sarj/no-comment-cruft": "error";
324
+ readonly "@sarj/no-conditional-in-test": "error";
325
+ readonly "@sarj/no-cors-wildcard-with-credentials": "error";
326
+ readonly "@sarj/no-dynamic-sql": "error";
327
+ readonly "@sarj/no-fat-try-blocks": "error";
328
+ readonly "@sarj/no-hand-rolled-sleep": "error";
329
329
  readonly "@sarj/no-hand-rolled-spinner": "error";
330
- readonly "@sarj/no-insecure-random-id": "warn";
331
- readonly "@sarj/no-json-stringify-error": "warn";
332
- readonly "@sarj/no-log-only-catch": "warn";
333
- readonly "@sarj/no-long-comment": "warn";
334
- readonly "@sarj/no-generic-single-export-module": "warn";
335
- readonly "@sarj/no-offset-pagination": "warn";
336
- readonly "@sarj/no-positional-tuple-return": "warn";
337
- readonly "@sarj/no-repeated-string-literal": "warn";
338
- readonly "@sarj/no-restated-comment": "warn";
339
- readonly "@sarj/no-restated-jsdoc": "warn";
340
- readonly "@sarj/no-secret-in-log": "warn";
341
- readonly "@sarj/no-select-star": "warn";
342
- readonly "@sarj/no-sentinel-return-on-catch": "warn";
343
- readonly "@sarj/no-silent-promise-catch": "warn";
344
- readonly "@sarj/no-sleep-in-test-body": "warn";
345
- readonly "@sarj/no-string-concat-in-loop": "warn";
346
- readonly "@sarj/no-tautological-expect": "warn";
347
- readonly "@sarj/no-typed-doc-sections": "warn";
348
- readonly "@sarj/no-trailing-value-narration": "warn";
349
- readonly "@sarj/no-declaration-comment-wall": "warn";
350
- readonly "@sarj/no-union-in-comment": "warn";
351
- readonly "@sarj/no-type-member-comment-wall": "warn";
352
- readonly "@sarj/no-unnecessary-use-client": "warn";
353
- readonly "@sarj/no-unsafe-mock-casting": "warn";
354
- readonly "@sarj/no-zod-native-enum": "warn";
355
- readonly "@sarj/test-loops-over-literal-cases": "warn";
330
+ readonly "@sarj/no-insecure-random-id": "error";
331
+ readonly "@sarj/no-json-stringify-error": "error";
332
+ readonly "@sarj/no-log-only-catch": "error";
333
+ readonly "@sarj/no-long-comment": "error";
334
+ readonly "@sarj/no-generic-single-export-module": "error";
335
+ readonly "@sarj/no-offset-pagination": "error";
336
+ readonly "@sarj/no-positional-tuple-return": "error";
337
+ readonly "@sarj/no-repeated-string-literal": "error";
338
+ readonly "@sarj/no-restated-comment": "error";
339
+ readonly "@sarj/no-restated-jsdoc": "error";
340
+ readonly "@sarj/no-secret-in-log": "error";
341
+ readonly "@sarj/no-select-star": "error";
342
+ readonly "@sarj/no-sentinel-return-on-catch": "error";
343
+ readonly "@sarj/no-silent-promise-catch": "error";
344
+ readonly "@sarj/no-sleep-in-test-body": "error";
345
+ readonly "@sarj/no-string-concat-in-loop": "error";
346
+ readonly "@sarj/no-tautological-expect": "error";
347
+ readonly "@sarj/no-typed-doc-sections": "error";
348
+ readonly "@sarj/no-trailing-value-narration": "error";
349
+ readonly "@sarj/no-declaration-comment-wall": "error";
350
+ readonly "@sarj/no-union-in-comment": "error";
351
+ readonly "@sarj/no-type-member-comment-wall": "error";
352
+ readonly "@sarj/no-unnecessary-use-client": "error";
353
+ readonly "@sarj/no-unsafe-mock-casting": "error";
354
+ readonly "@sarj/no-zod-native-enum": "error";
355
+ readonly "@sarj/test-loops-over-literal-cases": "error";
356
356
  readonly "@sarj/prefer-constant-time-secret-compare": "error";
357
- readonly "@sarj/prefer-discriminated-union": "warn";
357
+ readonly "@sarj/prefer-discriminated-union": "error";
358
358
  readonly "@sarj/prefer-input-group-search": "error";
359
- readonly "@sarj/prefer-immutable-module-constant": "warn";
360
- readonly "@sarj/prefer-module-level-constant": "warn";
361
- readonly "@sarj/prefer-module-level-schema": "warn";
362
- readonly "@sarj/prefer-non-nullable-collection": "warn";
363
- readonly "@sarj/prefer-schema-for-api-payload": "warn";
364
- readonly "@sarj/prefer-semantic-colors": readonly ["warn", {
359
+ readonly "@sarj/prefer-immutable-module-constant": "error";
360
+ readonly "@sarj/prefer-module-level-constant": "error";
361
+ readonly "@sarj/prefer-module-level-schema": "error";
362
+ readonly "@sarj/prefer-non-nullable-collection": "error";
363
+ readonly "@sarj/prefer-schema-for-api-payload": "error";
364
+ readonly "@sarj/prefer-semantic-colors": readonly ["error", {
365
365
  readonly requireSemanticTokens: true;
366
366
  }];
367
- readonly "@sarj/prefer-server-actions": "warn";
368
- readonly "@sarj/prefer-string-literal-union": "warn";
369
- readonly "@sarj/prefer-whole-object-assertion": "warn";
370
- readonly "@sarj/prefer-zod-enum": "warn";
371
- readonly "@sarj/prefer-zod-infer": "warn";
372
- readonly "@sarj/require-assert-never": "warn";
373
- readonly "@sarj/require-fetch-timeout": "warn";
374
- readonly "@sarj/require-interface-for-injected-service": "warn";
367
+ readonly "@sarj/prefer-server-actions": "error";
368
+ readonly "@sarj/prefer-string-literal-union": "error";
369
+ readonly "@sarj/prefer-whole-object-assertion": "error";
370
+ readonly "@sarj/prefer-zod-enum": "error";
371
+ readonly "@sarj/prefer-zod-infer": "error";
372
+ readonly "@sarj/require-assert-never": "error";
373
+ readonly "@sarj/require-fetch-timeout": "error";
374
+ readonly "@sarj/require-interface-for-injected-service": "error";
375
375
  readonly "@sarj/require-static-next-matcher": "error";
376
376
  readonly "@sarj/require-zod-form-validation": "error";
377
- readonly "@sarj/store-insert-requires-on-conflict": "warn";
378
- readonly "@sarj/stepdown": "warn";
379
- readonly "@sarj/zod-naming-convention": "warn";
377
+ readonly "@sarj/store-insert-requires-on-conflict": "error";
378
+ readonly "@sarj/stepdown": "error";
379
+ readonly "@sarj/zod-naming-convention": "error";
380
380
  };
381
381
  declare const strictRules: {
382
- readonly "@sarj/duplicate-test-body": "warn";
382
+ readonly "@sarj/duplicate-test-body": "error";
383
383
  readonly "@sarj/enforce-file-structure": "error";
384
384
  readonly "@sarj/no-async-callback-in-wait-for": "error";
385
385
  readonly "@sarj/no-client-side-data-fetching": "error";
@@ -395,7 +395,7 @@ declare const strictRules: {
395
395
  readonly "@sarj/no-json-stringify-error": "error";
396
396
  readonly "@sarj/no-log-only-catch": "error";
397
397
  readonly "@sarj/no-long-comment": "error";
398
- readonly "@sarj/no-generic-single-export-module": "warn";
398
+ readonly "@sarj/no-generic-single-export-module": "error";
399
399
  readonly "@sarj/no-offset-pagination": "error";
400
400
  readonly "@sarj/no-positional-tuple-return": "error";
401
401
  readonly "@sarj/no-raw-env": "error";
@@ -419,14 +419,14 @@ declare const strictRules: {
419
419
  readonly "@sarj/no-unnecessary-use-client": "error";
420
420
  readonly "@sarj/no-unsafe-mock-casting": "error";
421
421
  readonly "@sarj/no-zod-native-enum": "error";
422
- readonly "@sarj/test-loops-over-literal-cases": "warn";
422
+ readonly "@sarj/test-loops-over-literal-cases": "error";
423
423
  readonly "@sarj/prefer-constant-time-secret-compare": "error";
424
- readonly "@sarj/prefer-discriminated-union": "warn";
424
+ readonly "@sarj/prefer-discriminated-union": "error";
425
425
  readonly "@sarj/prefer-input-group-search": "error";
426
- readonly "@sarj/prefer-immutable-module-constant": "warn";
426
+ readonly "@sarj/prefer-immutable-module-constant": "error";
427
427
  readonly "@sarj/prefer-module-level-constant": "error";
428
428
  readonly "@sarj/prefer-module-level-schema": "error";
429
- readonly "@sarj/prefer-non-nullable-collection": "warn";
429
+ readonly "@sarj/prefer-non-nullable-collection": "error";
430
430
  readonly "@sarj/prefer-schema-for-api-payload": "error";
431
431
  readonly "@sarj/prefer-semantic-colors": readonly ["error", {
432
432
  readonly requireSemanticTokens: true;
@@ -436,13 +436,13 @@ declare const strictRules: {
436
436
  readonly "@sarj/prefer-whole-object-assertion": "error";
437
437
  readonly "@sarj/prefer-zod-enum": "error";
438
438
  readonly "@sarj/prefer-zod-infer": "error";
439
- readonly "@sarj/require-assert-never": "warn";
439
+ readonly "@sarj/require-assert-never": "error";
440
440
  readonly "@sarj/require-fetch-timeout": "error";
441
- readonly "@sarj/require-interface-for-injected-service": "warn";
441
+ readonly "@sarj/require-interface-for-injected-service": "error";
442
442
  readonly "@sarj/require-static-next-matcher": "error";
443
443
  readonly "@sarj/require-zod-form-validation": "error";
444
444
  readonly "@sarj/store-insert-requires-on-conflict": "error";
445
- readonly "@sarj/stepdown": "warn";
445
+ readonly "@sarj/stepdown": "error";
446
446
  readonly "@sarj/zod-naming-convention": "error";
447
447
  };
448
448
  type FlatPreset = {
@@ -453,7 +453,7 @@ type FlatPreset = {
453
453
  declare const plugin: {
454
454
  readonly meta: {
455
455
  readonly name: "@sarj/eslint-plugin";
456
- readonly version: "10.0.0";
456
+ readonly version: "11.0.1";
457
457
  };
458
458
  readonly rules: {
459
459
  readonly "duplicate-test-body": _typescript_eslint_utils_ts_eslint.RuleModule<"duplicateTestBody", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
package/dist/index.js CHANGED
@@ -12346,7 +12346,7 @@ var rules = {
12346
12346
  };
12347
12347
  var meta = {
12348
12348
  name: "@sarj/eslint-plugin",
12349
- version: "10.0.0"
12349
+ version: "11.0.1"
12350
12350
  };
12351
12351
  var applicationOnlyRules = [
12352
12352
  "no-restricted-library-load",
@@ -12354,68 +12354,68 @@ var applicationOnlyRules = [
12354
12354
  "prefer-shadcn-primitives"
12355
12355
  ];
12356
12356
  var recommendedRules = {
12357
- "@sarj/duplicate-test-body": "warn",
12358
- "@sarj/enforce-file-structure": "warn",
12359
- "@sarj/no-async-callback-in-wait-for": "warn",
12360
- "@sarj/no-client-side-data-fetching": "warn",
12361
- "@sarj/no-comment-cruft": "warn",
12362
- "@sarj/no-conditional-in-test": "warn",
12363
- "@sarj/no-cors-wildcard-with-credentials": "warn",
12364
- "@sarj/no-dynamic-sql": "warn",
12365
- "@sarj/no-fat-try-blocks": "warn",
12366
- "@sarj/no-hand-rolled-sleep": "warn",
12357
+ "@sarj/duplicate-test-body": "error",
12358
+ "@sarj/enforce-file-structure": "error",
12359
+ "@sarj/no-async-callback-in-wait-for": "error",
12360
+ "@sarj/no-client-side-data-fetching": "error",
12361
+ "@sarj/no-comment-cruft": "error",
12362
+ "@sarj/no-conditional-in-test": "error",
12363
+ "@sarj/no-cors-wildcard-with-credentials": "error",
12364
+ "@sarj/no-dynamic-sql": "error",
12365
+ "@sarj/no-fat-try-blocks": "error",
12366
+ "@sarj/no-hand-rolled-sleep": "error",
12367
12367
  "@sarj/no-hand-rolled-spinner": "error",
12368
- "@sarj/no-insecure-random-id": "warn",
12369
- "@sarj/no-json-stringify-error": "warn",
12370
- "@sarj/no-log-only-catch": "warn",
12371
- "@sarj/no-long-comment": "warn",
12372
- "@sarj/no-generic-single-export-module": "warn",
12373
- "@sarj/no-offset-pagination": "warn",
12374
- "@sarj/no-positional-tuple-return": "warn",
12375
- "@sarj/no-repeated-string-literal": "warn",
12376
- "@sarj/no-restated-comment": "warn",
12377
- "@sarj/no-restated-jsdoc": "warn",
12378
- "@sarj/no-secret-in-log": "warn",
12379
- "@sarj/no-select-star": "warn",
12380
- "@sarj/no-sentinel-return-on-catch": "warn",
12381
- "@sarj/no-silent-promise-catch": "warn",
12382
- "@sarj/no-sleep-in-test-body": "warn",
12383
- "@sarj/no-string-concat-in-loop": "warn",
12384
- "@sarj/no-tautological-expect": "warn",
12385
- "@sarj/no-typed-doc-sections": "warn",
12386
- "@sarj/no-trailing-value-narration": "warn",
12387
- "@sarj/no-declaration-comment-wall": "warn",
12388
- "@sarj/no-union-in-comment": "warn",
12389
- "@sarj/no-type-member-comment-wall": "warn",
12390
- "@sarj/no-unnecessary-use-client": "warn",
12391
- "@sarj/no-unsafe-mock-casting": "warn",
12392
- "@sarj/no-zod-native-enum": "warn",
12393
- "@sarj/test-loops-over-literal-cases": "warn",
12368
+ "@sarj/no-insecure-random-id": "error",
12369
+ "@sarj/no-json-stringify-error": "error",
12370
+ "@sarj/no-log-only-catch": "error",
12371
+ "@sarj/no-long-comment": "error",
12372
+ "@sarj/no-generic-single-export-module": "error",
12373
+ "@sarj/no-offset-pagination": "error",
12374
+ "@sarj/no-positional-tuple-return": "error",
12375
+ "@sarj/no-repeated-string-literal": "error",
12376
+ "@sarj/no-restated-comment": "error",
12377
+ "@sarj/no-restated-jsdoc": "error",
12378
+ "@sarj/no-secret-in-log": "error",
12379
+ "@sarj/no-select-star": "error",
12380
+ "@sarj/no-sentinel-return-on-catch": "error",
12381
+ "@sarj/no-silent-promise-catch": "error",
12382
+ "@sarj/no-sleep-in-test-body": "error",
12383
+ "@sarj/no-string-concat-in-loop": "error",
12384
+ "@sarj/no-tautological-expect": "error",
12385
+ "@sarj/no-typed-doc-sections": "error",
12386
+ "@sarj/no-trailing-value-narration": "error",
12387
+ "@sarj/no-declaration-comment-wall": "error",
12388
+ "@sarj/no-union-in-comment": "error",
12389
+ "@sarj/no-type-member-comment-wall": "error",
12390
+ "@sarj/no-unnecessary-use-client": "error",
12391
+ "@sarj/no-unsafe-mock-casting": "error",
12392
+ "@sarj/no-zod-native-enum": "error",
12393
+ "@sarj/test-loops-over-literal-cases": "error",
12394
12394
  "@sarj/prefer-constant-time-secret-compare": "error",
12395
- "@sarj/prefer-discriminated-union": "warn",
12395
+ "@sarj/prefer-discriminated-union": "error",
12396
12396
  "@sarj/prefer-input-group-search": "error",
12397
- "@sarj/prefer-immutable-module-constant": "warn",
12398
- "@sarj/prefer-module-level-constant": "warn",
12399
- "@sarj/prefer-module-level-schema": "warn",
12400
- "@sarj/prefer-non-nullable-collection": "warn",
12401
- "@sarj/prefer-schema-for-api-payload": "warn",
12402
- "@sarj/prefer-semantic-colors": ["warn", { requireSemanticTokens: true }],
12403
- "@sarj/prefer-server-actions": "warn",
12404
- "@sarj/prefer-string-literal-union": "warn",
12405
- "@sarj/prefer-whole-object-assertion": "warn",
12406
- "@sarj/prefer-zod-enum": "warn",
12407
- "@sarj/prefer-zod-infer": "warn",
12408
- "@sarj/require-assert-never": "warn",
12409
- "@sarj/require-fetch-timeout": "warn",
12410
- "@sarj/require-interface-for-injected-service": "warn",
12397
+ "@sarj/prefer-immutable-module-constant": "error",
12398
+ "@sarj/prefer-module-level-constant": "error",
12399
+ "@sarj/prefer-module-level-schema": "error",
12400
+ "@sarj/prefer-non-nullable-collection": "error",
12401
+ "@sarj/prefer-schema-for-api-payload": "error",
12402
+ "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
12403
+ "@sarj/prefer-server-actions": "error",
12404
+ "@sarj/prefer-string-literal-union": "error",
12405
+ "@sarj/prefer-whole-object-assertion": "error",
12406
+ "@sarj/prefer-zod-enum": "error",
12407
+ "@sarj/prefer-zod-infer": "error",
12408
+ "@sarj/require-assert-never": "error",
12409
+ "@sarj/require-fetch-timeout": "error",
12410
+ "@sarj/require-interface-for-injected-service": "error",
12411
12411
  "@sarj/require-static-next-matcher": "error",
12412
12412
  "@sarj/require-zod-form-validation": "error",
12413
- "@sarj/store-insert-requires-on-conflict": "warn",
12414
- "@sarj/stepdown": "warn",
12415
- "@sarj/zod-naming-convention": "warn"
12413
+ "@sarj/store-insert-requires-on-conflict": "error",
12414
+ "@sarj/stepdown": "error",
12415
+ "@sarj/zod-naming-convention": "error"
12416
12416
  };
12417
12417
  var strictRules = {
12418
- "@sarj/duplicate-test-body": "warn",
12418
+ "@sarj/duplicate-test-body": "error",
12419
12419
  "@sarj/enforce-file-structure": "error",
12420
12420
  "@sarj/no-async-callback-in-wait-for": "error",
12421
12421
  "@sarj/no-client-side-data-fetching": "error",
@@ -12431,7 +12431,7 @@ var strictRules = {
12431
12431
  "@sarj/no-json-stringify-error": "error",
12432
12432
  "@sarj/no-log-only-catch": "error",
12433
12433
  "@sarj/no-long-comment": "error",
12434
- "@sarj/no-generic-single-export-module": "warn",
12434
+ "@sarj/no-generic-single-export-module": "error",
12435
12435
  "@sarj/no-offset-pagination": "error",
12436
12436
  "@sarj/no-positional-tuple-return": "error",
12437
12437
  "@sarj/no-raw-env": "error",
@@ -12455,14 +12455,14 @@ var strictRules = {
12455
12455
  "@sarj/no-unnecessary-use-client": "error",
12456
12456
  "@sarj/no-unsafe-mock-casting": "error",
12457
12457
  "@sarj/no-zod-native-enum": "error",
12458
- "@sarj/test-loops-over-literal-cases": "warn",
12458
+ "@sarj/test-loops-over-literal-cases": "error",
12459
12459
  "@sarj/prefer-constant-time-secret-compare": "error",
12460
- "@sarj/prefer-discriminated-union": "warn",
12460
+ "@sarj/prefer-discriminated-union": "error",
12461
12461
  "@sarj/prefer-input-group-search": "error",
12462
- "@sarj/prefer-immutable-module-constant": "warn",
12462
+ "@sarj/prefer-immutable-module-constant": "error",
12463
12463
  "@sarj/prefer-module-level-constant": "error",
12464
12464
  "@sarj/prefer-module-level-schema": "error",
12465
- "@sarj/prefer-non-nullable-collection": "warn",
12465
+ "@sarj/prefer-non-nullable-collection": "error",
12466
12466
  "@sarj/prefer-schema-for-api-payload": "error",
12467
12467
  "@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
12468
12468
  "@sarj/prefer-server-actions": "error",
@@ -12470,13 +12470,13 @@ var strictRules = {
12470
12470
  "@sarj/prefer-whole-object-assertion": "error",
12471
12471
  "@sarj/prefer-zod-enum": "error",
12472
12472
  "@sarj/prefer-zod-infer": "error",
12473
- "@sarj/require-assert-never": "warn",
12473
+ "@sarj/require-assert-never": "error",
12474
12474
  "@sarj/require-fetch-timeout": "error",
12475
- "@sarj/require-interface-for-injected-service": "warn",
12475
+ "@sarj/require-interface-for-injected-service": "error",
12476
12476
  "@sarj/require-static-next-matcher": "error",
12477
12477
  "@sarj/require-zod-form-validation": "error",
12478
12478
  "@sarj/store-insert-requires-on-conflict": "error",
12479
- "@sarj/stepdown": "warn",
12479
+ "@sarj/stepdown": "error",
12480
12480
  "@sarj/zod-naming-convention": "error"
12481
12481
  };
12482
12482
  var plugin = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "10.0.0",
3
+ "version": "11.0.1",
4
4
  "packageManager": "npm@11.19.0",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "build": "tsup",
37
37
  "dogfood": "npm run build && eslint --config eslint.dogfood.config.mjs src tests eslint.config.mjs eslint.dogfood.config.mjs tsup.config.ts vitest.config.ts --max-warnings 0 && node -e \"import('./dist/index.js').then(({default:p}) => console.log('dogfood: '+Object.keys(p.rules).length+' TypeScript rules, 0 diagnostics'))\"",
38
38
  "lint": "eslint . --max-warnings 0",
39
- "prepack": "npm run build && npm run verify-package",
39
+ "prepack": "npm run verify-package",
40
40
  "test": "vitest run",
41
41
  "typecheck": "tsc --noEmit",
42
42
  "verify-package": "node -e \"const fs=require('node:fs'),p=require('./package.json'),walk=x=>typeof x==='string'?[x]:x&&typeof x==='object'?Object.values(x).flatMap(walk):[];for(const file of new Set([p.main,p.module,p.types,...walk(p.exports)].filter(Boolean).map(x=>x.startsWith('./')?x.slice(2):x))){if(!file.startsWith('dist/'))throw new Error('export must live under dist: '+file);const stat=fs.statSync(file);if(!stat.isFile()||stat.size===0)throw new Error('missing or empty export: '+file)}\""
@@ -96,6 +96,7 @@
96
96
  "overrides": {
97
97
  "brace-expansion@^5.0.0": "5.0.9",
98
98
  "esbuild": "^0.28.1",
99
+ "nanoid@<3.3.17": "3.3.17",
99
100
  "eslint-plugin-react": {
100
101
  "eslint": "$eslint"
101
102
  }