@doubleloop-io/oxlint-plugin-effect 0.1.0-rc.0 → 0.1.0-rc.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
@@ -18,9 +18,16 @@ Requires Node `^22.20.0 || >=24.8.0` and oxlint `^1.82.0`.
18
18
 
19
19
  ## Configuration
20
20
 
21
- Extend the `all` preset.
21
+ Extend the `all` preset. Prefer `oxlint.config.ts`, where `extends` takes config objects and the preset is imported by package name:
22
22
 
23
- With `.oxlintrc.json`, where `extends` takes file paths:
23
+ ```ts
24
+ import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
25
+ import { defineConfig } from "oxlint"
26
+
27
+ export default defineConfig({ extends: [dlEffectAll] })
28
+ ```
29
+
30
+ With `.oxlintrc.json`, `extends` takes file paths only, so the preset is reached through `node_modules`:
24
31
 
25
32
  ```json
26
33
  {
@@ -28,48 +35,45 @@ With `.oxlintrc.json`, where `extends` takes file paths:
28
35
  }
29
36
  ```
30
37
 
31
- With `oxlint.config.ts`, where `extends` takes config objects:
38
+ `all` enables every rule at `warn`, so adopting it never breaks a build: look at the warnings first, then raise to `error` the rules you want to enforce. A `recommended` preset with reasoned severities will come from measurements on real codebases.
32
39
 
33
- ```ts
34
- import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
35
- import { defineConfig } from "oxlint"
40
+ To pick rules without the preset, load the plugin by package name and list the rules:
36
41
 
37
- export default defineConfig({ extends: [dlEffectAll] })
42
+ ```json
43
+ {
44
+ "jsPlugins": ["@doubleloop-io/oxlint-plugin-effect"],
45
+ "rules": {
46
+ "dl-effect/no-effect-promise": "error",
47
+ "dl-effect/try-object-form": "error"
48
+ }
49
+ }
38
50
  ```
39
51
 
40
- `all` enables every rule at `warn`, so adopting it never breaks a build: look at the warnings first, then raise to `error` the rules you want to enforce. A `recommended` preset with reasoned severities will come from measurements on real codebases.
41
-
42
52
  ### Configuration options
43
53
 
44
54
  Two options tell the rules where your files are. Their defaults fit a Node CLI; on another layout (Next.js, a monorepo) set them before reading the warnings.
45
55
 
46
- - `entryFiles`: the files allowed to run Effects and import adapters. Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. Used by `run-only-at-entry` and `adapter-import-boundary`.
47
- - `testFiles`: the files exempt from the rules that tests legitimately break. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. Used by `adapter-import-boundary`, `no-effect-promise`, `provide-named-layer`, `no-provide-on-definition`, `error-handling-in-effect-fn-handler` and `anti-slop-no-service-constructor-imports`.
56
+ - `entryFiles`: the files allowed to run Effects and import adapters. Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. Used by `run-only-in-runner` and `adapter-import-boundary`.
57
+ - `testFiles`: the test files, exempt from the rules that tests legitimately break and the only files `prefer-effect-vitest` checks. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. Used by `run-only-in-runner`, `prefer-effect-vitest`, `adapter-import-boundary`, `no-effect-promise`, `provide-named-layer`, `no-provide-on-definition`, `error-handling-in-effect-fn-handler`, `all-errors-handled-at-boundary` and `anti-slop-no-service-constructor-imports`.
48
58
 
49
- An option **replaces** its default instead of extending it, and every rule that uses it must get the same value. With `oxlint.config.ts`, declare each option once and override the rules that use it after `extends`:
59
+ Set an option only where its default does not fit. It **replaces** the default instead of extending it, and every rule that uses it must get the same value. With `oxlint.config.ts`, declare it once and override the rules that use it after `extends`:
50
60
 
51
61
  ```ts
52
62
  import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
53
63
  import { defineConfig } from "oxlint"
54
64
 
55
65
  const entryFiles = ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"]
56
- const testFiles = ["**/*.test.ts", "e2e/**"]
57
66
 
58
67
  export default defineConfig({
59
68
  extends: [dlEffectAll],
60
69
  rules: {
61
- "dl-effect/run-only-at-entry": ["warn", { entryFiles }],
62
- "dl-effect/adapter-import-boundary": ["warn", { entryFiles, testFiles }],
63
- "dl-effect/no-effect-promise": ["warn", { testFiles }],
64
- "dl-effect/provide-named-layer": ["warn", { testFiles }],
65
- "dl-effect/no-provide-on-definition": ["warn", { testFiles }],
66
- "dl-effect/error-handling-in-effect-fn-handler": ["warn", { testFiles }],
67
- "dl-effect/anti-slop-no-service-constructor-imports": ["warn", { testFiles }],
70
+ "dl-effect/run-only-in-runner": ["warn", { entryFiles }],
71
+ "dl-effect/adapter-import-boundary": ["warn", { entryFiles }],
68
72
  },
69
73
  })
70
74
  ```
71
75
 
72
- With `.oxlintrc.json` the same overrides go under `rules`, repeating each list in every rule.
76
+ The package exports the defaults as `defaultEntryFiles` and `defaultTestFiles`: spread them to extend a default (`testFiles: [...defaultTestFiles, "e2e/**"]`), or reuse them in your own `overrides`. With `.oxlintrc.json` the overrides go under `rules`, repeating each list in every rule.
73
77
 
74
78
  The other options and every default are in [Default configuration](#default-configuration).
75
79
 
@@ -77,6 +81,14 @@ The other options and every default are in [Default configuration](#default-conf
77
81
 
78
82
  [`@effect/tsgo`](https://github.com/Effect-TS/tsgo) is the official Effect language service on top of the TypeScript Go compiler; its `effecttsgo` oxlint presets add more than 100 type-aware rules. This plugin does not need it, and its rules target what those presets do not check, so the two complement each other. To use both, follow its [Oxlint Setup guide](https://github.com/Effect-TS/tsgo/blob/main/docs/README.md) and put its preset in `extends` **before** `all`:
79
83
 
84
+ ```ts
85
+ import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
86
+ import { recommended as effectTsgoRecommended } from "@effect/tsgo/oxlint-presets"
87
+ import { defineConfig } from "oxlint"
88
+
89
+ export default defineConfig({ extends: [effectTsgoRecommended, dlEffectAll] })
90
+ ```
91
+
80
92
  ```json
81
93
  {
82
94
  "extends": [
@@ -86,14 +98,6 @@ The other options and every default are in [Default configuration](#default-conf
86
98
  }
87
99
  ```
88
100
 
89
- ```ts
90
- import dlEffectAll from "@doubleloop-io/oxlint-plugin-effect/presets/all"
91
- import { recommended as effectTsgoRecommended } from "@effect/tsgo/oxlint-presets"
92
- import { defineConfig } from "oxlint"
93
-
94
- export default defineConfig({ extends: [effectTsgoRecommended, dlEffectAll] })
95
- ```
96
-
97
101
  ## Rules
98
102
 
99
103
  What each rule reports and why is in [`RULES.md`](RULES.md).
@@ -103,7 +107,7 @@ What each rule reports and why is in [`RULES.md`](RULES.md).
103
107
  | Error channel | [`no-bare-error-in-effect`](RULES.md#no-bare-error-in-effect), [`try-object-form`](RULES.md#try-object-form), [`no-effect-promise`](RULES.md#no-effect-promise), [`no-parameterless-catch-all`](RULES.md#no-parameterless-catch-all), [`error-handling-in-effect-fn-handler`](RULES.md#error-handling-in-effect-fn-handler), [`anti-slop-no-manual-effect-error-tag`](RULES.md#anti-slop-no-manual-effect-error-tag) |
104
108
  | Tagged values | [`no-manual-discriminant-comparison`](RULES.md#no-manual-discriminant-comparison), [`no-manual-discriminant-construction`](RULES.md#no-manual-discriminant-construction) |
105
109
  | Services & Layers | [`service-port-shape-only`](RULES.md#service-port-shape-only), [`service-key-matches-name`](RULES.md#service-key-matches-name), [`service-built-with-of`](RULES.md#service-built-with-of), [`provide-named-layer`](RULES.md#provide-named-layer), [`no-provide-on-definition`](RULES.md#no-provide-on-definition), [`anti-slop-no-service-constructor-imports`](RULES.md#anti-slop-no-service-constructor-imports) |
106
- | Runtime & entrypoint | [`run-only-at-entry`](RULES.md#run-only-at-entry), [`boundary-entrypoint-shape`](RULES.md#boundary-entrypoint-shape) |
110
+ | Runtime & entrypoint | [`run-only-in-runner`](RULES.md#run-only-in-runner), [`all-errors-handled-at-boundary`](RULES.md#all-errors-handled-at-boundary) |
107
111
  | Design & boundaries | [`adapter-import-boundary`](RULES.md#adapter-import-boundary), [`no-transport-types-in-generator`](RULES.md#no-transport-types-in-generator) |
108
112
  | Effectful functions | [`prefer-effect-fn`](RULES.md#prefer-effect-fn), [`untraced-recursion`](RULES.md#untraced-recursion), [`single-style-per-function`](RULES.md#single-style-per-function) |
109
113
  | Code shape | [`no-nested-match`](RULES.md#no-nested-match), [`no-yielded-match`](RULES.md#no-yielded-match), [`no-inline-yield-star`](RULES.md#no-inline-yield-star), [`no-effect-return-type`](RULES.md#no-effect-return-type), [`no-asymmetric-match-handlers`](RULES.md#no-asymmetric-match-handlers), [`anti-slop-prefer-effect-match`](RULES.md#anti-slop-prefer-effect-match) |
@@ -143,8 +147,7 @@ const discriminants = ["_tag"]
143
147
  const secretFields = ["password", "secret", "apiKey", "accessToken", "refreshToken", "idToken"]
144
148
 
145
149
  export default defineConfig({
146
- // The package exports no entry point: point at the plugin file.
147
- jsPlugins: ["./node_modules/@doubleloop-io/oxlint-plugin-effect/dist/index.js"],
150
+ jsPlugins: ["@doubleloop-io/oxlint-plugin-effect"],
148
151
  // Every rule at `warn`, to measure noise without blocking.
149
152
  rules: {
150
153
  // Error channel
@@ -153,6 +156,7 @@ export default defineConfig({
153
156
  "dl-effect/no-effect-promise": ["warn", { testFiles }],
154
157
  "dl-effect/no-parameterless-catch-all": "warn",
155
158
  "dl-effect/error-handling-in-effect-fn-handler": ["warn", { testFiles }],
159
+ "dl-effect/all-errors-handled-at-boundary": ["warn", { testFiles }],
156
160
  "dl-effect/anti-slop-no-manual-effect-error-tag": "warn",
157
161
  // Tagged values
158
162
  "dl-effect/no-manual-discriminant-comparison": ["warn", { discriminants }],
@@ -165,8 +169,9 @@ export default defineConfig({
165
169
  "dl-effect/no-provide-on-definition": ["warn", { testFiles }],
166
170
  "dl-effect/anti-slop-no-service-constructor-imports": ["warn", { testFiles }],
167
171
  // Runtime & entrypoint
168
- "dl-effect/run-only-at-entry": ["warn", { entryFiles }],
169
- "dl-effect/boundary-entrypoint-shape": "warn",
172
+ "dl-effect/run-only-in-runner": ["warn", { entryFiles, testFiles }],
173
+ "dl-effect/prefer-effect-vitest": ["warn", { testFiles }],
174
+ "dl-effect/all-errors-handled-at-boundary": "warn",
170
175
  // Design & boundaries
171
176
  "dl-effect/adapter-import-boundary": ["warn", { entryFiles, testFiles }],
172
177
  "dl-effect/no-transport-types-in-generator": ["warn", { transportTypes }],
@@ -204,8 +209,9 @@ Supported oxlint is the `peerDependencies` range, `^1.82.0`; CI tests the tarbal
204
209
  - `pnpm check`: format check, typecheck, lint and tests, writing nothing. The tests are the `RuleTester`s from `oxlint/plugins-dev` with `node --test`, plus an alignment check of `RULES.md` with one section per rule.
205
210
  - `pnpm build`: compiles to `dist/` and generates `dist/presets/all.json` from `presets/all.ts` (the rule list comes from the plugin).
206
211
  - `pnpm pack:check`: checks the built preset, then the tarball `npm pack` would publish: only the files in `files`, no internal names, `publint --strict`.
207
- - `pnpm smoke-test`: installs the tarball in `smoke-fixture/` with pnpm and checks the diagnostics of oxlint with both config forms.
212
+ - `pnpm smoke-test`: installs the tarball in `smoke-fixture/` with pnpm and checks the diagnostics of oxlint with each config form: `oxlint.config.ts` and `.oxlintrc.json` with the preset, `.oxlintrc.json` with the plugin by package name.
208
213
  - `pnpm build-and-pack`: `check`, `build`, `pack:check` and `smoke-test`; run it after every change.
209
- - `docs/consumer-loop.md`: how to run a consumer's checks against the checkout, without a release.
214
+ - `pnpm build:w`: reruns `pnpm build`, preset included, on every change to `index.ts`, `rules/`, `presets/`, `shared/` or `tsconfig.build.json`; a failed build waits for the next change.
215
+ - `pnpm consumer:link <consumer>`: after `pnpm build`, replaces the pnpm symlinks of the plugin in every workspace package of `<consumer>` with links to the checkout, so the consumer's own lint runs the checkout's rules; no tracked file of the consumer changes. `pnpm consumer:unlink <consumer>` restores them with `pnpm install --frozen-lockfile`, as does any `pnpm install` in the consumer.
210
216
 
211
217
  A new rule must be registered in `index.ts` and documented in `RULES.md` under a `` ### `rule-name` `` heading; `pnpm build` adds it to the preset.
package/RULES.md CHANGED
@@ -1,47 +1,48 @@
1
1
  # `@doubleloop-io/oxlint-plugin-effect` rules
2
2
 
3
- The plugin's 30 rules. The `Pxx` identifiers come from the origin catalog; the patterns no rule checks are in [`skill/reference/effect-patterns.md`](skill/reference/effect-patterns.md).
3
+ The plugin's 31 rules. The `Pxx` identifiers come from the origin catalog; the patterns no rule checks are in [`skill/reference/effect-patterns.md`](skill/reference/effect-patterns.md).
4
4
 
5
5
  ## Rules index
6
6
 
7
- | Rule | Pattern | Severity |
8
- | --------------------------------------------------------------------------------------- | -------- | -------- |
9
- | [`service-port-shape-only`](#service-port-shape-only) | P01 | `error` |
10
- | [`adapter-import-boundary`](#adapter-import-boundary) | P02 | `error` |
11
- | [`run-only-at-entry`](#run-only-at-entry) | P04, P22 | `error` |
12
- | [`no-bare-error-in-effect`](#no-bare-error-in-effect) | P05 | `error` |
13
- | [`try-object-form`](#try-object-form) | P08 | `error` |
14
- | [`no-effect-promise`](#no-effect-promise) | P08 | `warn` |
15
- | [`no-parameterless-catch-all`](#no-parameterless-catch-all) | P09 | `error` |
16
- | [`redacted-secret-names`](#redacted-secret-names) | P13 | `warn` |
17
- | [`prefer-effect-fn`](#prefer-effect-fn) | P17 | `warn` |
18
- | [`untraced-recursion`](#untraced-recursion) | P17 | `warn` |
19
- | [`single-style-per-function`](#single-style-per-function) | P24 | `error` |
20
- | [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler) | P25 | `warn` |
21
- | [`no-transport-types-in-generator`](#no-transport-types-in-generator) | P26 | `error` |
22
- | [`boundary-entrypoint-shape`](#boundary-entrypoint-shape) | P27 | `warn` |
23
- | [`provide-named-layer`](#provide-named-layer) | P28 | `error` |
24
- | [`no-provide-on-definition`](#no-provide-on-definition) | P28 | `error` |
25
- | [`no-nested-match`](#no-nested-match) | P30 | `warn` |
26
- | [`no-inline-yield-star`](#no-inline-yield-star) | P32 | `warn` |
27
- | [`no-effect-return-type`](#no-effect-return-type) | P33 | `warn` |
28
- | [`no-asymmetric-match-handlers`](#no-asymmetric-match-handlers) | P34 | `warn` |
29
- | [`exported-schema-identifier`](#exported-schema-identifier) | P35 | — |
30
- | [`no-manual-discriminant-comparison`](#no-manual-discriminant-comparison) | P36 | — |
31
- | [`no-manual-discriminant-construction`](#no-manual-discriminant-construction) | P37 | — |
32
- | [`service-key-matches-name`](#service-key-matches-name) | P38 | — |
33
- | [`no-inline-schema-codec`](#no-inline-schema-codec) | P39 | — |
34
- | [`no-yielded-match`](#no-yielded-match) | P40 | — |
35
- | [`service-built-with-of`](#service-built-with-of) | P41 | — |
36
- | [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports) | — | — |
37
- | [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag) | — | — |
38
- | [`anti-slop-prefer-effect-match`](#anti-slop-prefer-effect-match) | — | — |
7
+ | Rule | Pattern | Severity |
8
+ | --------------------------------------------------------------------------------------- | ------- | -------- |
9
+ | [`service-port-shape-only`](#service-port-shape-only) | P01 | `error` |
10
+ | [`adapter-import-boundary`](#adapter-import-boundary) | P02 | `error` |
11
+ | [`run-only-in-runner`](#run-only-in-runner) | P04 | `error` |
12
+ | [`no-bare-error-in-effect`](#no-bare-error-in-effect) | P05 | `error` |
13
+ | [`try-object-form`](#try-object-form) | P08 | `error` |
14
+ | [`no-effect-promise`](#no-effect-promise) | P08 | `warn` |
15
+ | [`no-parameterless-catch-all`](#no-parameterless-catch-all) | P09 | `error` |
16
+ | [`redacted-secret-names`](#redacted-secret-names) | P13 | `warn` |
17
+ | [`prefer-effect-fn`](#prefer-effect-fn) | P17 | `warn` |
18
+ | [`untraced-recursion`](#untraced-recursion) | P17 | `warn` |
19
+ | [`prefer-effect-vitest`](#prefer-effect-vitest) | P22 | — |
20
+ | [`single-style-per-function`](#single-style-per-function) | P24 | `error` |
21
+ | [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler) | P25 | `warn` |
22
+ | [`no-transport-types-in-generator`](#no-transport-types-in-generator) | P26 | `error` |
23
+ | [`all-errors-handled-at-boundary`](#all-errors-handled-at-boundary) | P27 | `warn` |
24
+ | [`provide-named-layer`](#provide-named-layer) | P28 | `error` |
25
+ | [`no-provide-on-definition`](#no-provide-on-definition) | P28 | `error` |
26
+ | [`no-nested-match`](#no-nested-match) | P30 | `warn` |
27
+ | [`no-inline-yield-star`](#no-inline-yield-star) | P32 | `warn` |
28
+ | [`no-effect-return-type`](#no-effect-return-type) | P33 | `warn` |
29
+ | [`no-asymmetric-match-handlers`](#no-asymmetric-match-handlers) | P34 | `warn` |
30
+ | [`exported-schema-identifier`](#exported-schema-identifier) | P35 | — |
31
+ | [`no-manual-discriminant-comparison`](#no-manual-discriminant-comparison) | P36 | — |
32
+ | [`no-manual-discriminant-construction`](#no-manual-discriminant-construction) | P37 | — |
33
+ | [`service-key-matches-name`](#service-key-matches-name) | P38 | — |
34
+ | [`no-inline-schema-codec`](#no-inline-schema-codec) | P39 | — |
35
+ | [`no-yielded-match`](#no-yielded-match) | P40 | — |
36
+ | [`service-built-with-of`](#service-built-with-of) | P41 | — |
37
+ | [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports) | — | — |
38
+ | [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag) | — | — |
39
+ | [`anti-slop-prefer-effect-match`](#anti-slop-prefer-effect-match) | — | — |
39
40
 
40
41
  The `all` preset enables them all at `warn`. Severity is the one each rule was kept at zero noise with on an Effect 4 codebase: a data point about its stability, not a recommendation. `—` means not yet measured.
41
42
 
42
43
  ## Common limits
43
44
 
44
- - **Syntax only**: no rule uses type information.
45
+ - **Syntax only**: no rule uses type information, and only [`prefer-effect-vitest`](#prefer-effect-vitest) looks past the file, to find whether `@effect/vitest` is installed.
45
46
  - **Literal names**: `Effect.gen` is recognized, `import { Effect as E }` or `import * as L from "effect/Layer"` slip through. On code that uses aliases, silence is not compliance.
46
47
  - **Effect 4**: the rules also recognize some v3 APIs (`catchAll`, `Effect.Service`, `Context.Tag`), but they are written and tested against v4.
47
48
 
@@ -49,8 +50,8 @@ The `all` preset enables them all at `warn`. Severity is the one each rule was k
49
50
 
50
51
  When the reason for an exclusion is part of the rule, the rule itself recognizes the files, through an option that **replaces** the defaults (it does not extend them). Globs are matched against the path relative to oxlint's working directory, with `/` on every platform.
51
52
 
52
- - **`entryFiles`**, used by [`run-only-at-entry`](#run-only-at-entry) and [`adapter-import-boundary`](#adapter-import-boundary). Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. `index.ts` is excluded on purpose: it is usually a barrel, and exempting it would let adapters reach the rest of the application through re-exports.
53
- - **`testFiles`**, used by [`adapter-import-boundary`](#adapter-import-boundary), [`no-effect-promise`](#no-effect-promise), [`provide-named-layer`](#provide-named-layer), [`no-provide-on-definition`](#no-provide-on-definition), [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler) and [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports). Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. A helper directory such as `src/testing/` does not count as tests: it is support code.
53
+ - **`entryFiles`**, used by [`run-only-in-runner`](#run-only-in-runner) and [`adapter-import-boundary`](#adapter-import-boundary). Default `**/main.ts`, `**/cli.ts`, `**/bin/**`. `index.ts` is excluded on purpose: it is usually a barrel, and exempting it would let adapters reach the rest of the application through re-exports.
54
+ - **`testFiles`**, used by [`run-only-in-runner`](#run-only-in-runner), [`adapter-import-boundary`](#adapter-import-boundary), [`no-effect-promise`](#no-effect-promise), [`provide-named-layer`](#provide-named-layer), [`no-provide-on-definition`](#no-provide-on-definition), [`error-handling-in-effect-fn-handler`](#error-handling-in-effect-fn-handler), [`no-bare-error-in-effect`](#no-bare-error-in-effect), [`single-style-per-function`](#single-style-per-function), [`all-errors-handled-at-boundary`](#all-errors-handled-at-boundary) and [`anti-slop-no-service-constructor-imports`](#anti-slop-no-service-constructor-imports). Those rules skip test files, except `no-bare-error-in-effect`, which skips only primitive failures there, and `single-style-per-function`, which treats their helpers like tests; [`prefer-effect-vitest`](#prefer-effect-vitest) checks only them. Default `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test/**`, `**/tests/**`. A helper directory such as `src/testing/` does not count as tests: it is support code.
54
55
 
55
56
  The defaults come from a Node CLI. On another layout (Next.js, a monorepo) they must be reconfigured before reading the numbers, and every rule sharing an option must be reconfigured:
56
57
 
@@ -60,7 +61,7 @@ const entryFiles = ["apps/web/src/app/**/route.ts", "apps/web/src/runtime.ts"]
60
61
  export default defineConfig({
61
62
  extends: [dlEffectAll],
62
63
  rules: {
63
- "dl-effect/run-only-at-entry": ["warn", { entryFiles }],
64
+ "dl-effect/run-only-in-runner": ["warn", { entryFiles }],
64
65
  "dl-effect/adapter-import-boundary": ["warn", { entryFiles }],
65
66
  },
66
67
  })
@@ -199,20 +200,16 @@ const issues = yield * IssueService
199
200
 
200
201
  - **Pattern**: — · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: —
201
202
 
202
- ### `run-only-at-entry`
203
+ ### `run-only-in-runner`
203
204
 
204
205
  <sup>[↑ rules index](#rules-index)</sup>
205
206
 
206
- **Intent**: Effects run only at the entry point, and tests run them through the test runtime.
207
+ **Intent**: Effects run only in the program's runner, the entry point.
207
208
 
208
209
  ```ts
209
210
  // no
210
211
  export const check = () => Effect.runPromise(pingDb(url))
211
212
 
212
- test("…", async () => {
213
- await Effect.runPromise(authorize({ role: "Manager" }))
214
- })
215
-
216
213
  // yes: in main.ts
217
214
  NodeRuntime.runMain(
218
215
  run(process.argv.slice(2)).pipe(Effect.provide(NodeServices.layer)) satisfies Effect.Effect<
@@ -220,18 +217,41 @@ NodeRuntime.runMain(
220
217
  never
221
218
  >,
222
219
  )
223
-
224
- // yes: in a test, fakes as Layers
225
- it.effect("…", () => authorize({ role: "Manager" }).pipe(Effect.provide(sheetWith(row))))
226
220
  ```
227
221
 
228
222
  **Trigger**: every reference, not just calls, to `Effect.run*`, `NodeRuntime.runMain`, `BunRuntime.runMain` and `ManagedRuntime.make` outside entry files.
229
223
 
230
- **Exemptions and blind spots**: test files are not exempt; a module-level `ManagedRuntime` in a test is the typical catch. Without `@effect/vitest`, a single test runner module that provides the test Layer goes in `entryFiles`, and each test returns one call to it; the test runtime, `TestClock` included, is lost.
224
+ **Exemptions and blind spots**: test files, checked by [`prefer-effect-vitest`](#prefer-effect-vitest) instead.
225
+
226
+ **Why**: logger, Layers and defect handling are configured in one place.
227
+
228
+ - **Pattern**: P04 · **Options**: `entryFiles`, `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
229
+
230
+ ### `prefer-effect-vitest`
231
+
232
+ <sup>[↑ rules index](#rules-index)</sup>
233
+
234
+ **Intent**: tests hand their Effects to `@effect/vitest` instead of running them.
235
+
236
+ ```ts
237
+ // no
238
+ test("…", async () => {
239
+ await Effect.runPromise(authorize({ role: "Manager" }))
240
+ })
241
+
242
+ // yes: fakes as Layers
243
+ import { effect } from "@effect/vitest"
244
+
245
+ effect("…", () => authorize({ role: "Manager" }).pipe(Effect.provide(sheetWith(row))))
246
+ ```
247
+
248
+ **Trigger**: the same references as [`run-only-in-runner`](#run-only-in-runner), inside test files only.
249
+
250
+ **Exemptions and blind spots**: projects where `@effect/vitest` cannot be found in a `node_modules` from the test file up, since the remedy is out of reach there; scattered runs in their tests go unreported. Every file outside `testFiles`, test helpers such as `src/testing/` included.
231
251
 
232
- **Why**: logger, Layers and defect handling are configured in one place. In tests, the test runtime brings `TestClock` and fakes replace services where the code reads them.
252
+ **Why**: the test runtime brings `TestClock` and a scope per test, and fixtures provided as Layers replace services where the code reads them.
233
253
 
234
- - **Pattern**: P04, P22 · **Options**: `entryFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
254
+ - **Pattern**: P22 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: —
235
255
 
236
256
  ## Errors
237
257
 
@@ -259,11 +279,11 @@ Effect.fail(new SheetTabNotFoundError({ spreadsheetId, message: "tab not found"
259
279
 
260
280
  **Trigger**: a `throw` directly inside a generator passed to `Effect.gen`/`Effect.fn`; `Effect.fail` with a literal or a template.
261
281
 
262
- **Exemptions and blind spots**: a `throw` inside a nested callback. `Effect.fail(new Error(…))` and `class X extends Error`: see `global-error-in-effect-failure` and `extends-native-error` (effecttsgo).
282
+ **Exemptions and blind spots**: `Effect.fail` with a literal in test files, where a synthetic failure feeds code generic over the error; a `throw` in a test generator is still reported. A `throw` inside a nested callback. `Effect.fail(new Error(…))` and `class X extends Error`: see `global-error-in-effect-failure` and `extends-native-error` (effecttsgo).
263
283
 
264
284
  **Why**: the typed error channel discriminates only on tags. A `throw` in the generator becomes a defect, outside the error channel.
265
285
 
266
- - **Pattern**: P05 · **Options**: none · **Fix**: none · **Severity**: `error`
286
+ - **Pattern**: P05 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
267
287
 
268
288
  ### `try-object-form`
269
289
 
@@ -386,7 +406,7 @@ Match.value(payment).pipe(
386
406
 
387
407
  **Trigger**: an equality comparison between a discriminant (also `["type"]`) and a string literal, or a `switch` on a discriminant. The discriminants are the names in `discriminants`.
388
408
 
389
- **Exemptions and blind spots**: comparisons against a variable rather than a literal. On `_tag`, the handlers covered by [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag): the two never fire on the same node. Guard clauses such as `if (x.type === "a") return …` fire too; on `_tag` use `Predicate.isTagged`. An exhaustive ts-pattern match is an equally valid remedy. A name added to `discriminants` also fires on objects outside closed unions (DOM events, external payloads): the list is a choice of the codebase.
409
+ **Exemptions and blind spots**: comparisons against a variable rather than a literal. On `_tag`, the handlers covered by [`anti-slop-no-manual-effect-error-tag`](#anti-slop-no-manual-effect-error-tag): the two never fire on the same node. Guard clauses such as `if (x.type === "a") return …` fire too; on `_tag` use `Predicate.isTagged`. A `_tag` compared with a variant of Effect's own types (`Success`/`Failure` of `Result` and `Exit`, `Left`/`Right`, `Some`/`None`) is reported with their refinement as the remedy, such as `Result.isFailure`: those types cannot gain a variant; a `switch` is reported as usual. An exhaustive ts-pattern match is an equally valid remedy. A name added to `discriminants` also fires on objects outside closed unions (DOM events, external payloads): the list is a choice of the codebase.
390
410
 
391
411
  **Why**: `Match.tagsExhaustive`, `Match.discriminatorsExhaustive` and `$match` fail to compile when a variant is added; a comparison or a `switch` does not.
392
412
 
@@ -414,7 +434,7 @@ const event = PaymentStartedEvent.make({ id, payment })
414
434
 
415
435
  **Trigger**: an object literal with a discriminant property (also `"type"` or `["type"]`) whose value is a string literal. The discriminants are the names in `discriminants`.
416
436
 
417
- **Exemptions and blind spots**: values that are not literals (`"Ready" as const` included). Literals that are patterns or raw input, also nested: the arguments of `Match.when`, `Match.not`, of `.with(…)` on a ts-pattern `match(…)` chain, and the input of `Schema.decode*(X)(…)`/`Schema.encode*(X)(…)`. Transport payloads written as literals fire, and a name added to `discriminants` fires on objects outside closed unions: the list is a choice of the codebase. See `schema-struct-with-tag` (effecttsgo) for `Schema.Struct` with a `_tag` field.
437
+ **Exemptions and blind spots**: values that are not literals (`"Ready" as const` included). Literals that are patterns or raw input, also nested: the arguments of `Match.when`, `Match.not`, of `.with(…)` on a ts-pattern `match(…)` chain, the expected value of an `expect(…)` matcher or `expect.*` helper, the input of `Schema.decode*(X)(…)`/`Schema.encode*(X)(…)`, the argument of `JSON.stringify`, and an argument whose parameter is annotated `unknown` in a function declared in the same file (a fake API's raw body). Transport payloads written as literals fire, as does raw data passed to an imported function or through a variable, and a name added to `discriminants` fires on objects outside closed unions: the list is a choice of the codebase. See `schema-struct-with-tag` (effecttsgo) for `Schema.Struct` with a `_tag` field.
418
438
 
419
439
  **Why**: a literal skips the constructor, so it carries none of the class's prototype, equality or validation, and drifts silently when the type changes.
420
440
 
@@ -440,7 +460,7 @@ const Session = Schema.Struct({ userId: UserId, accessToken: Schema.Redacted(Sch
440
460
 
441
461
  **Trigger**:
442
462
 
443
- - a literal name read with `Config.*` that ends with `KEY`, `SECRET`, `TOKEN`, `PASSWORD`, `CREDENTIAL`, `CREDENTIALS` or `_JSON` (service account keys often travel as inline JSON), when it does not go through `Config.redacted` or `Config.schema(Schema.Redacted…(…), name)`;
463
+ - a literal name read with `Config.*` that ends with `KEY`, `SECRET`, `TOKEN`, `PASSWORD`, `CREDENTIAL`, `CREDENTIALS` or `_JSON` (service account keys often travel as inline JSON), when it does not go through `Config.Redacted` or `Config.schema(Schema.Redacted…(…), name)`;
444
464
  - a field of `Schema.Struct({…})` or `Schema.Class<X>("X")({…})` whose name is a `secretFields` entry or ends with it capitalized (`password` matches `dbPassword`), declared with a `Schema.*` constant such as `Schema.String`, also through `.pipe(…)`, `.check(…)`, `.annotate(…)` or `Schema.optional`/`optionalKey`/`NullOr`/`UndefinedOr`/`NullishOr`, with no `Schema.Redacted…` along the way.
445
465
 
446
466
  **Exemptions and blind spots**: a naming heuristic: secrets with other names slip through, non-secrets with these names must be suppressed. Fields declared with a named schema (`accessToken: AccessToken`) are not inspected; other constructors (`Schema.TaggedClass`, `Schema.TaggedStruct`, …) are not seen. `process.env` read outside `Config`: see `process-env` and `process-env-in-effect` (effecttsgo).
@@ -583,13 +603,13 @@ const createEvent = Effect.fn("events.create")(function* (request: Request) {
583
603
  })
584
604
  ```
585
605
 
586
- **Trigger**: `async`, `await` and `.then(` in a function that builds Effects: an Effect generator, or a function using an `Effect.*` member. In a `test`/`it` callback (`.only`, `.each(…)` and the like included) the mix is reported once, on the callback.
606
+ **Trigger**: `async`, `await` and `.then(` in a function that builds Effects: an Effect generator, or a function using an `Effect.*` member. A `test`/`it` callback (`.only`, `.each(…)` and the like included) or a `beforeAll`/`afterAll`/`beforeEach`/`afterEach` hook, and in test files any other function except an Effect generator, is reported once, on the function, when more than one of its `await`s, or any `.then(`, involves Effect: has an `Effect.*` member in its expression, nested callbacks and `Effect.run*` included.
587
607
 
588
- **Exemptions and blind spots**: `Effect.run*` alone does not make a function effectful: it is how an async boundary hands over to Effect, and where it may appear is decided by [`run-only-at-entry`](#run-only-at-entry). In tests the fix is a body that is one Effect (`it.effect`, or one call to the test runner), not a `tryPromise` per promise.
608
+ **Exemptions and blind spots**: `Effect.run*` alone does not make a function effectful: it is how an async boundary hands over to Effect, and where it may appear is decided by [`run-only-in-runner`](#run-only-in-runner). An `async` function that renders JSX itself, not only in nested callbacks, is a Server Component: how it turns failures into UI is left to the application. A test, hook or test helper may await promise-based fixtures (a container, an identity provider), then one Effect program, and assert on its result. A step that reaches Effect only through a helper (`await run(seed)`) is not counted.
589
609
 
590
610
  **Why**: errors travel on two channels, exceptions and Effect, and neither sees them all.
591
611
 
592
- - **Pattern**: P24 · **Options**: none · **Fix**: none · **Severity**: `error`
612
+ - **Pattern**: P24 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `error`
593
613
 
594
614
  ### `error-handling-in-effect-fn-handler`
595
615
 
@@ -616,9 +636,9 @@ const createEvent = Effect.fn("events.create")(
616
636
  )
617
637
  ```
618
638
 
619
- **Trigger**: inside a generator passed to `Effect.gen`/`Effect.fn`, nested callbacks included, calls to `Effect.catch*` (`catch`, `catchAll`, `catchTag`, `catchTags`, `catchIf`, `catchCause`, `catchDefect`, `catchReason`, …), `either`, `result`, `exit`, `match*` and `sandbox`.
639
+ **Trigger**: inside a generator passed to `Effect.gen`/`Effect.fn`, callbacks passed to `Effect.*` combinators included, calls to `Effect.catch*` (`catch`, `catchAll`, `catchTag`, `catchTags`, `catchIf`, `catchCause`, `catchDefect`, `catchReason`, …), `either`, `result`, `exit`, `match*` and `sandbox`.
620
640
 
621
- **Exemptions and blind spots**: pipeables of a nested `Effect.fn`. Test files, where the failure is the outcome under observation (`yield* Effect.result(op)` then `expect`). A `.pipe(Effect.catch…)` appended to an `Effect.gen` escapes; [`prefer-effect-fn`](#prefer-effect-fn) closes that gap.
641
+ **Exemptions and blind spots**: pipeables of a nested `Effect.fn`. Handlers inside the callback of an iteration (`forEach`, `filter`, `reduce`, `partition`, `validate`, `findFirst`, …): they handle each element on its own, and in the pipeables the first failure would stop the rest. Functions the generator only defines, such as an `HttpApiBuilder` handler or a service method: they are programs of their own. Test files, where the failure is the outcome under observation (`yield* Effect.result(op)` then `expect`). A `catch`, `catchTag`, `catchTags`, `catchIf` or `catchFilter` whose handlers only die (`Effect.die` passed directly or as the sole return): it is a selective `orDie`, declaring the case untracked rather than handling it; a die behind a wrapper or a condition is still reported. A `.pipe(Effect.catch…)` appended to an `Effect.gen` escapes; [`prefer-effect-fn`](#prefer-effect-fn) closes that gap.
622
642
 
623
643
  **Why**: translation becomes a single exhaustive point instead of catches scattered along the flow.
624
644
 
@@ -653,15 +673,15 @@ const createEvent = Effect.fn("events.create")(
653
673
  )
654
674
  ```
655
675
 
656
- **Trigger**: `new T(…)`, `T.method(…)` and `T(…)` inside an Effect generator, nested callbacks included, when `T` is a transport type.
676
+ **Trigger**: `new T(…)`, `T.method(…)` and `T(…)` inside an Effect generator, callbacks passed to `Effect.*` combinators included, when `T` is a transport type.
657
677
 
658
- **Exemptions and blind spots**: pipeables of `Effect.fn`, where the conversion must live. `return yield* new Unauthenticated(…)` is a declared failure. An early `return` of a domain value that actually signals an error is indistinguishable from a success.
678
+ **Exemptions and blind spots**: pipeables of `Effect.fn`, where the conversion must live. Functions the generator only defines, such as an HTTP handler it registers. `return yield* new Unauthenticated(…)` is a declared failure. An early `return` of a domain value that actually signals an error is indistinguishable from a success.
659
679
 
660
680
  **Why**: an early `return new Response(…)` on the happy path is an error branch disguised as success, and escapes exhaustive translation.
661
681
 
662
682
  - **Pattern**: P26 · **Options**: `transportTypes`, replaces the default `["Response", "NextResponse"]`; add your own transport DTOs · **Fix**: none · **Severity**: `error`
663
683
 
664
- ### `boundary-entrypoint-shape`
684
+ ### `all-errors-handled-at-boundary`
665
685
 
666
686
  <sup>[↑ rules index](#rules-index)</sup>
667
687
 
@@ -679,13 +699,13 @@ export const POST = (request: Request) =>
679
699
  const runBoundary = (effect: Effect.Effect<Response, never, never>) => Effect.runPromise(effect)
680
700
  ```
681
701
 
682
- **Trigger**: `Effect.runPromise`, `runSync`, `runFork`, `runCallback` and `NodeRuntime.runMain`/`BunRuntime.runMain` whose argument is neither `… satisfies Effect.Effect<A, never>` nor a parameter declared with that type.
702
+ **Trigger**: `Effect.runPromise`, `runSync`, `runFork` and `runCallback` whose argument is not `… satisfies Effect.Effect<A, never>`, a parameter declared with that type, or a program ending in `Effect.result`/`Effect.exit` (`Effect.result(program)`, `program.pipe(…, Effect.result)`).
683
703
 
684
- **Exemptions and blind spots**: the `*Exit` variants, which return failures as values. The rule checks the shape; `tsc` gives the guarantee.
704
+ **Exemptions and blind spots**: the `*Exit` variants, which return failures as values; `NodeRuntime.runMain`/`BunRuntime.runMain`, which log the Cause and exit with code 1, the right outcome for a process that cannot start; runners inside an `async` function that renders JSX itself, a Server Component, which hands failures to React rather than to an HTTP response; test files, where a rejected run fails the test with the error. The rule checks the shape; `tsc` gives the guarantee.
685
705
 
686
706
  **Why**: `Effect.runPromise` turns any error left over into a rejected promise, a generic 500. With `never`, a new untranslated error does not compile, and `R = never` forces all Layers to be provided.
687
707
 
688
- - **Pattern**: P27 · **Options**: none · **Fix**: none · **Severity**: `warn`
708
+ - **Pattern**: P27 · **Options**: `testFiles` ([File options](#file-options)) · **Fix**: none · **Severity**: `warn`
689
709
 
690
710
  ## Wiring and code shape
691
711
 
@@ -727,7 +747,7 @@ const printMissing = Effect.gen(function* () { … }).pipe(Effect.provide(missin
727
747
 
728
748
  **Trigger**: `Effect.provide` applied to `Effect.gen(…)` or to `Effect.fn(…)(…)`, in all three forms: with `.pipe`, as an `Effect.fn` pipeable, data-first.
729
749
 
730
- **Exemptions and blind spots**: test files. A context captured in an enclosing generator with `const ctx = yield* Effect.context<R>()` (also followed by `.pipe(…)`) and handed back to a method of the Layer being built: the caller chose the adapters when it built the Layer. A context obtained through a helper (`yield* contextWithoutSpan()`) is still reported. `provide` on a named program is allowed; `strict-effect-provide` (effecttsgo) reports it too.
750
+ **Exemptions and blind spots**: test files. A program passed directly to `Effect.run*`, also through `.pipe(…)` or data-first `Effect.*` combinators (`Effect.runPromise(Effect.gen(…).pipe(Effect.provide(layer), Effect.result))`): it is already being dispatched; through any other function it is still reported. A context captured in an enclosing generator with `const ctx = yield* Effect.context<R>()` (also followed by `.pipe(…)`) and handed back to a method of the Layer being built: the caller chose the adapters when it built the Layer. A context obtained through a helper (`yield* contextWithoutSpan()`) is still reported. `provide` on a named program is allowed; `strict-effect-provide` (effecttsgo) reports it too.
731
751
 
732
752
  **Why**: a program that provides its own Layers can no longer be wired to different adapters by its caller.
733
753
 
@@ -852,7 +872,7 @@ export const findPrice = (id: PriceId): Effect.Effect<Price, PriceNotFound> =>
852
872
 
853
873
  **Exemptions and blind spots**: module-level declarations with `export` (`export const`, `export function`, `export default`), unless `checkExported` is on; methods of an exported object, functions an exported function returns, and `export { f }` lists are still checked. Recursion, and returns of different shapes that need unifying (`Effect.succeed` and `Effect.fail`), recognized from the shape of the `return`s. An `import type { Effect }` used as `Effect<…>` slips through.
854
874
 
855
- **Why**: the error and requirement channels collect what comes from everything the function calls; an error or a service added deep down would change every caller's annotation. An exported signature is the module's contract, like a named type: there the annotation makes such a change visible where it is published.
875
+ **Why**: the error and requirement channels collect what comes from everything the function calls; an error or a service added deep down would change every caller's annotation. An exported signature is the module's contract, like a named type: there the annotation makes such a change visible where it is published. To check a returned object against a named type, annotate the callback that builds it (`Effect.map(CurrentSession, (session): OrganizationContext => ({ … }))`) or use `satisfies`.
856
876
 
857
877
  - **Pattern**: P33 · **Options**: `checkExported`, default `false`: also checks exported declarations · **Fix**: suggestion that removes the annotation · **Severity**: `warn`
858
878
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export { defaultEntryFiles } from "./shared/entry-files.ts";
2
+ export { defaultTestFiles } from "./shared/test-files.ts";
1
3
  export declare const pluginName = "dl-effect";
2
4
  export declare const pluginPath: string;
3
5
  declare const dlEffectPlugin: import("@oxlint/plugins").Plugin;
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { fileURLToPath } from "node:url";
2
2
  import { eslintCompatPlugin } from "@oxlint/plugins";
3
3
  import { adapterImportBoundaryRule } from "./rules/adapter-import-boundary.js";
4
+ import { allErrorsHandledAtBoundaryRule } from "./rules/all-errors-handled-at-boundary.js";
4
5
  import { antiSlopNoManualEffectErrorTagRule } from "./rules/anti-slop-no-manual-effect-error-tag.js";
5
6
  import { antiSlopNoServiceConstructorImportsRule } from "./rules/anti-slop-no-service-constructor-imports.js";
6
7
  import { antiSlopPreferEffectMatchRule } from "./rules/anti-slop-prefer-effect-match.js";
7
- import { boundaryEntrypointShapeRule } from "./rules/boundary-entrypoint-shape.js";
8
8
  import { errorHandlingInEffectFnHandlerRule } from "./rules/error-handling-in-effect-fn-handler.js";
9
9
  import { exportedSchemaIdentifierRule } from "./rules/exported-schema-identifier.js";
10
10
  import { noAsymmetricMatchHandlersRule } from "./rules/no-asymmetric-match-handlers.js";
@@ -21,15 +21,18 @@ import { noProvideOnDefinitionRule } from "./rules/no-provide-on-definition.js";
21
21
  import { noTransportTypesInGeneratorRule } from "./rules/no-transport-types-in-generator.js";
22
22
  import { noYieldedMatchRule } from "./rules/no-yielded-match.js";
23
23
  import { preferEffectFnRule } from "./rules/prefer-effect-fn.js";
24
+ import { preferEffectVitestRule } from "./rules/prefer-effect-vitest.js";
24
25
  import { provideNamedLayerRule } from "./rules/provide-named-layer.js";
25
26
  import { redactedSecretNamesRule } from "./rules/redacted-secret-names.js";
26
- import { runOnlyAtEntryRule } from "./rules/run-only-at-entry.js";
27
+ import { runOnlyInRunnerRule } from "./rules/run-only-in-runner.js";
27
28
  import { serviceBuiltWithOfRule } from "./rules/service-built-with-of.js";
28
29
  import { serviceKeyMatchesNameRule } from "./rules/service-key-matches-name.js";
29
30
  import { servicePortShapeOnlyRule } from "./rules/service-port-shape-only.js";
30
31
  import { singleStylePerFunctionRule } from "./rules/single-style-per-function.js";
31
32
  import { tryObjectFormRule } from "./rules/try-object-form.js";
32
33
  import { untracedRecursionRule } from "./rules/untraced-recursion.js";
34
+ export { defaultEntryFiles } from "./shared/entry-files.js";
35
+ export { defaultTestFiles } from "./shared/test-files.js";
33
36
  export const pluginName = "dl-effect";
34
37
  // Resolved at runtime so it points at this file both from the sources and from `dist/`.
35
38
  export const pluginPath = fileURLToPath(import.meta.url);
@@ -40,7 +43,7 @@ const dlEffectPlugin = eslintCompatPlugin({
40
43
  "anti-slop-no-service-constructor-imports": antiSlopNoServiceConstructorImportsRule,
41
44
  "anti-slop-prefer-effect-match": antiSlopPreferEffectMatchRule,
42
45
  "adapter-import-boundary": adapterImportBoundaryRule,
43
- "boundary-entrypoint-shape": boundaryEntrypointShapeRule,
46
+ "all-errors-handled-at-boundary": allErrorsHandledAtBoundaryRule,
44
47
  "error-handling-in-effect-fn-handler": errorHandlingInEffectFnHandlerRule,
45
48
  "exported-schema-identifier": exportedSchemaIdentifierRule,
46
49
  "no-asymmetric-match-handlers": noAsymmetricMatchHandlersRule,
@@ -57,9 +60,10 @@ const dlEffectPlugin = eslintCompatPlugin({
57
60
  "no-transport-types-in-generator": noTransportTypesInGeneratorRule,
58
61
  "no-yielded-match": noYieldedMatchRule,
59
62
  "prefer-effect-fn": preferEffectFnRule,
63
+ "prefer-effect-vitest": preferEffectVitestRule,
60
64
  "provide-named-layer": provideNamedLayerRule,
61
65
  "redacted-secret-names": redactedSecretNamesRule,
62
- "run-only-at-entry": runOnlyAtEntryRule,
66
+ "run-only-in-runner": runOnlyInRunnerRule,
63
67
  "service-built-with-of": serviceBuiltWithOfRule,
64
68
  "service-key-matches-name": serviceKeyMatchesNameRule,
65
69
  "service-port-shape-only": servicePortShapeOnlyRule,
@@ -7,7 +7,7 @@
7
7
  "dl-effect/anti-slop-no-service-constructor-imports": "warn",
8
8
  "dl-effect/anti-slop-prefer-effect-match": "warn",
9
9
  "dl-effect/adapter-import-boundary": "warn",
10
- "dl-effect/boundary-entrypoint-shape": "warn",
10
+ "dl-effect/all-errors-handled-at-boundary": "warn",
11
11
  "dl-effect/error-handling-in-effect-fn-handler": "warn",
12
12
  "dl-effect/exported-schema-identifier": "warn",
13
13
  "dl-effect/no-asymmetric-match-handlers": "warn",
@@ -24,9 +24,10 @@
24
24
  "dl-effect/no-transport-types-in-generator": "warn",
25
25
  "dl-effect/no-yielded-match": "warn",
26
26
  "dl-effect/prefer-effect-fn": "warn",
27
+ "dl-effect/prefer-effect-vitest": "warn",
27
28
  "dl-effect/provide-named-layer": "warn",
28
29
  "dl-effect/redacted-secret-names": "warn",
29
- "dl-effect/run-only-at-entry": "warn",
30
+ "dl-effect/run-only-in-runner": "warn",
30
31
  "dl-effect/service-built-with-of": "warn",
31
32
  "dl-effect/service-key-matches-name": "warn",
32
33
  "dl-effect/service-port-shape-only": "warn",
@@ -0,0 +1 @@
1
+ export declare const allErrorsHandledAtBoundaryRule: import("@oxlint/plugins").Rule;