{"_id":"@mpsuesser/oxlint-plugin-effect","_rev":"11-982d925a375b745e52aec7b9c4e169cb","name":"@mpsuesser/oxlint-plugin-effect","dist-tags":{"latest":"0.5.0"},"versions":{"0.1.0":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.1.0","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.1.0","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"933fb2e5cbdc222f8057cd8d7e778e6b5a7b5fa5","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.1.0.tgz","fileCount":59,"integrity":"sha512-m0PfLW7i1/59w/pklJTnJT8HgQYyz0ri057Gp3u6dEyCbGWQBPa8vQLR6Hz9ekvc9Qw8ts8cdpHuhuXF7LLuKA==","signatures":[{"sig":"MEUCIQDtLAx81nghTeG1bah3pwzpMa4FqXLhXeKuaJsSmpzZgQIgQWZNP7cHIy8s7sZHBYYcnIH1R+qgFHiSaJkR9xFAsHs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":176936},"type":"module","$schema":"https://www.schemastore.org/package.json","exports":{".":{"import":"./src/index.ts"},"./oxlint":"./src/index.ts"},"gitHead":"14f99aab85dbc2237d8b95aeee090c79f16834b4","scripts":{"test":"vitest run","build":"vp build","check":"vp check --fix","prepare":"effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"mpsuesser","email":"mpsuesser@gmail.com"},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.15","effect":"4.0.0-beta.46"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"10.9.7","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"22.22.2","dependencies":{"effect":"4.0.0-beta.46","effect-oxlint":"^0.1.0","@oxlint/plugins":"^1.57.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.12","devDependencies":{"vitest":"^4.1.0","vite-plus":"^0.1.15","@types/bun":"^1.3.10","typescript":"^5.9.3","@effect/tsgo":"^0.1.1","@effect/vitest":"4.0.0-beta.46","@typescript/native-preview":"^7.0.0-dev.20260401.1"},"patchedDependencies":{"effect-oxlint@0.1.0":"patches/effect-oxlint@0.1.0.patch"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.1.0_1778566089023_0.6494320455590206","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.2.0","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.2.0","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"c4f77d5a06d5123ed17d9ce47f609f720ffdf602","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.2.0.tgz","fileCount":61,"integrity":"sha512-DLtDDXPft3Bd1fRtg2MlqIuus1hf826wj4Oal8FgfHxKVCbIi7Qgk8G28e31M7rp5ud1AAvp+ZX91OIpul5Mxw==","signatures":[{"sig":"MEUCIAD4dpKdKmgDwUGYPpgQzcYTfu24mYgch1EG0FzpmHBiAiEA1aJ4eXXb2/NUMr05VEHmqeJ9vWpXa4zU4foyYn/A96Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":277142},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2329056d35e2884d093b684cdcbe6709f4c739f4","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"mpsuesser","email":"mpsuesser@gmail.com"},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.70"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"11.12.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"25.9.0","dependencies":{"effect":"4.0.0-beta.70","effect-oxlint":"^0.3.0","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.70","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.2.0_1779532735954_0.7069587448005152","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.2.1","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.2.1","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"de1c1c13c85d04fb4561f1edbe1b14416c45d263","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.2.1.tgz","fileCount":61,"integrity":"sha512-SDVAzv80sc1Lu0uOHV+fBYjZ0kJPIKKe+ljV91WYo7iV8Kc5k+H75oslilKkQP2Wr8mUmsBt4vOgwZlMg4feqg==","signatures":[{"sig":"MEYCIQC4w+EBBEoqiJdcI+506oUJS6vN2JtPDDkRwauKWODbxQIhAP7hnc2y6QepO0XTyuo7SsEvE1w4pZ6YClb6o+T4v5jD","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":273730},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"3871bc99b7fbad6bebbd56c60a331d4a8430c76c","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"mpsuesser","email":"mpsuesser@gmail.com"},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.70"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"11.12.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"25.9.0","dependencies":{"effect":"4.0.0-beta.70","effect-oxlint":"^0.3.0","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.70","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.2.1_1779534413846_0.37823377539006864","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.2.2","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.2.2","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"4e6e886d5242226a5ef975795f834b4149444c5c","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.2.2.tgz","fileCount":61,"integrity":"sha512-vbAZQZka3pek43g8C9RtH4/ZGdXmgNwEOYLPN0r/eqR/R75pGcez4uP3rQ/vcq/9IBt3tkAaIf/+vtzk/mGWpA==","signatures":[{"sig":"MEYCIQCjEG757GwTHEqVgiprKi2j3TuBP8+uLWukVqeUVIUNfwIhAJEkH3uu9uKnPU8icIoR/U/mRBJAVee+cMGRpSBXMEuh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":274022},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"776dfdd15fbaef286e0a30014b1087e2a5a2601e","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"mpsuesser","email":"mpsuesser@gmail.com"},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.70"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"11.12.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"25.9.0","dependencies":{"effect":"4.0.0-beta.70","effect-oxlint":"^0.3.0","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.70","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.2.2_1779535185840_0.7170778635272423","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.2.3","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.2.3","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"a99a18d7c835ce31771c97970e027a3a4c0efb43","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.2.3.tgz","fileCount":61,"integrity":"sha512-nelJVOU1dL5CAMIHQe+YCSENUl83xH0zbj4pLp6dBi82/KerQdkOeXBpTXqM17C8cOaLLORtwTY9ldNbtXB9Mg==","signatures":[{"sig":"MEYCIQCJSgl/jVBDujCNcHjZvCmor1hee20dis6ZqIyzZil/9gIhAJbC2Hyu9jIJ0M20hIiKyBz+kLVeYMVm/KFmsjjq5ZHn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.2.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":274291},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"8e89416ba700201fd446e1d442f0dffc2ad0097d","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.78"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"11.16.0","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"24.16.0","dependencies":{"effect":"4.0.0-beta.78","effect-oxlint":"^0.3.2","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.2.3_1780955876718_0.7340203529640961","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.3.0","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.3.0","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"83c5805f08163d690e6b3dcc12ae6130cd18b95d","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.3.0.tgz","fileCount":66,"integrity":"sha512-2n09UMHywAo+oifH1Tp7o9+uvC9WBc78smQTRlWCusfQw86ECK+7H4ExJGU9WjvMiJzuIw+0Gw2FNUq9vUuqoA==","signatures":[{"sig":"MEUCIQDcfcLYkIfBbv0+y41x56kcuQl/65Vl+E2MuBWVnwVTmAIgWGPj9t/rmJPmHdhiH7KrTF5jD+CAh+zFk3BGUvrovu8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":318888},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"29ee3e783f11adc6c2e4c54edc32bbacec7b20b6","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.78"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"11.16.0","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"24.16.0","dependencies":{"effect":"4.0.0-beta.78","effect-oxlint":"^0.3.2","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.3.0_1780972093584_0.3197352022774813","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.4.0","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.4.0","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"c5389afb00756ad35b41766218066024056b53db","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.4.0.tgz","fileCount":65,"integrity":"sha512-WQsEjV5l1qhlbK2L7BSoLC57Lmsw3YUel6PMWSW7gqK+iQECWhJ9Ie2qV1Z1JmUpapcROsH8LMemAlhaS3Q1jw==","signatures":[{"sig":"MEQCIEz0w0jVDXJp4dFPqzF34C1q9NL0G/ws/THhv78E7WK3AiAfkZfcwzev+QLNeJZaPRpnM23eioVYhV/HPsBkeqm2jg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":321516},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"8621200b83b20c4e4c3c825db03b47e2ca1d70c9","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.100"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"12.0.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"24.18.0","dependencies":{"effect":"4.0.0-beta.100","effect-oxlint":"^0.3.3","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.4.0_1784697718176_0.6549090689115478","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.4.1","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.4.1","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"c67e2ce82ebb8c1e4bda8a0aaec369b6c75f682c","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.4.1.tgz","fileCount":65,"integrity":"sha512-wGiUc6MKaJbNxGqKWFV9ZGy018l9qDTyrhF0zY0MDy2nZxY0YP70w2H5Z8KM9XWHcjQqvxno46XgJh4RH7O7BQ==","signatures":[{"sig":"MEYCIQCU/o+p63oRILkJQTw3R8vYtgOVJPTu6KLj8HNfgQ3vCQIhAKB9XCmgBpqHJTLiJY8ktjW1KT85L1r0sjrK+8jNE1S/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":322198},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2c715b33731b74127f4443b6b77e58fc62e48b1b","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.100"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"12.0.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"24.18.0","dependencies":{"effect":"4.0.0-beta.100","effect-oxlint":"^0.3.3","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.4.1_1784698082274_0.5547288892341962","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.4.2","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.4.2","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"92ec844b8bd81bff781907f8280a5db6ea26c60c","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.4.2.tgz","fileCount":64,"integrity":"sha512-rH/ljy+9xvLQTL9ROJdvZTXKTbUP5t54g7wQuaY8cwuA1L4a7In5SmEjg3yFn73nbfKi0PWxvHNkyPVyvKoKtQ==","signatures":[{"sig":"MEUCIB9tfa4Do3YyGFzt2Pb65GL0sbgTJCiqOF8mHINwyVaWAiEA6t4aU2SR4c0Yno4/IQA5lpLNdr3ONxcG1omzaMmnPpc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.4.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":311883},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"4e63901b5c8e3d7c01bda944e59444f2319818fd","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.100"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"12.0.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"24.18.0","dependencies":{"effect":"4.0.0-beta.100","effect-oxlint":"^0.3.3","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.4.2_1784706644767_0.5624292594004325","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@mpsuesser/oxlint-plugin-effect","version":"0.4.3","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"author":{"name":"Marc Suesser"},"license":"MIT","_id":"@mpsuesser/oxlint-plugin-effect@0.4.3","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"dist":{"shasum":"09a8744a733d2f3860b764a17336c292cfe66267","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.4.3.tgz","fileCount":64,"integrity":"sha512-zUuShvX0nIq7UYnjk1mz7YDQMb6A9cJ7vWnDUBwicmiI87rCfqieYAvQC40Mr9ifzXal07VArrOeZw3Ysikzeg==","signatures":[{"sig":"MEQCICkrTY1pLSkZDLgI2NRUqGnx9rPiPsdskUwdvigPxTTfAiBPf1++4lsAx55FPneIduP4YvyjYWKI4pE0Acm1JODprg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.4.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":312025},"type":"module","types":"./dist/index.d.ts","$schema":"https://www.schemastore.org/package.json","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"9460e66d93e00fe35b1b05fd4d8160e1ce7d5efb","scripts":{"test":"vitest run","build":"tsdown","check":"vp check --fix","prepack":"bun run build","prepare":"bunx --bun effect-tsgo patch","typecheck":"tsgo"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"overrides":{"vite":"npm:@voidzero-dev/vite-plus-core@0.1.22","effect":"4.0.0-beta.100"},"repository":{"url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git","type":"git"},"_npmVersion":"12.0.1","description":"Oxlint JS plugin enforcing Effect-first development conventions","directories":{},"_nodeVersion":"24.18.0","dependencies":{"effect":"4.0.0-beta.100","effect-oxlint":"^0.3.3","@oxlint/plugins":"^1.66.0"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsdown":"^0.22.0","vitest":"^4.1.7","vite-plus":"^0.1.22","@types/bun":"^1.3.14","typescript":"^6.0.3","@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@typescript/native-preview":"^7.0.0-dev.20260523.1"},"_npmOperationalInternal":{"tmp":"tmp/oxlint-plugin-effect_0.4.3_1784827851323_0.8510091941014073","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"$schema":"https://www.schemastore.org/package.json","name":"@mpsuesser/oxlint-plugin-effect","version":"0.5.0","description":"Oxlint JS plugin enforcing Effect-first development conventions","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"license":"MIT","author":{"name":"Marc Suesser"},"repository":{"type":"git","url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git"},"type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./oxlint":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsdown","check":"vp check --fix","lint":"vp lint","test":"vitest run","typecheck":"tsgo","prepare":"bunx --bun effect-tsgo patch","prepack":"bun run build"},"dependencies":{"@oxlint/plugins":"^1.66.0","effect":"4.0.0-beta.100","effect-oxlint":"^0.3.3"},"devDependencies":{"@effect/tsgo":"^0.11.0","@effect/vitest":"4.0.0-beta.78","@types/bun":"^1.3.14","@typescript/native-preview":"^7.0.0-dev.20260523.1","oxlint":"1.66.0","tsdown":"^0.22.0","typescript":"^6.0.3","vite-plus":"^0.1.22","vitest":"^4.1.7"},"overrides":{"effect":"4.0.0-beta.100","vite":"npm:@voidzero-dev/vite-plus-core@0.1.22"},"packageManager":"bun@1.3.14","gitHead":"9cf2054aae56f179fc12e3db882ce053d6ca2775","_id":"@mpsuesser/oxlint-plugin-effect@0.5.0","_nodeVersion":"24.20.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-RksB0kBL3aEzMdSgozRgKjVc9++k8EsmoqS9QQ4+AS9TVBVcHyA+Q6dE9gcsRqYWbsGNQQ58STDJAwlwczsAqg==","shasum":"c70b386199f253ddaa0efd460879306da40e657f","tarball":"https://registry.npmjs.org/@mpsuesser/oxlint-plugin-effect/-/oxlint-plugin-effect-0.5.0.tgz","fileCount":88,"unpackedSize":496726,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@mpsuesser%2foxlint-plugin-effect@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID8yNOPF0lUgJHBGxTKumdXwukkulv1dFASnH4XD9t9wAiAnK0C0Ycnn9wxBMOP+zGrBwSVf+F2XOE/BMpXw25bYcQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:376ca9eb-2c36-4d8e-8f32-d77c67e1410d"}},"directories":{},"maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/oxlint-plugin-effect_0.5.0_1788783660633_0.45160543221789995"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-12T06:08:08.948Z","modified":"2026-09-07T12:21:01.115Z","0.1.0":"2026-05-12T06:08:09.162Z","0.2.0":"2026-05-23T10:38:56.087Z","0.2.1":"2026-05-23T11:06:53.989Z","0.2.2":"2026-05-23T11:19:45.999Z","0.2.3":"2026-06-08T21:57:56.853Z","0.3.0":"2026-06-09T02:28:13.729Z","0.4.0":"2026-07-22T05:21:58.318Z","0.4.1":"2026-07-22T05:28:02.421Z","0.4.2":"2026-07-22T07:50:44.917Z","0.4.3":"2026-07-23T17:30:51.535Z","0.5.0":"2026-09-07T12:21:00.767Z"},"bugs":{"url":"https://github.com/mpsuesser/oxlint-plugin-effect/issues"},"author":{"name":"Marc Suesser"},"license":"MIT","homepage":"https://github.com/mpsuesser/oxlint-plugin-effect","keywords":["effect","eslint","lint","linter","oxc","oxlint","oxlint-plugin","oxlintplugin","plugin","rules","typescript"],"repository":{"type":"git","url":"git+https://github.com/mpsuesser/oxlint-plugin-effect.git"},"description":"Oxlint JS plugin enforcing Effect-first development conventions","maintainers":[{"name":"mpsuesser","email":"mpsuesser@gmail.com"}],"readme":"# oxlint-plugin-effect\n\n[![npm](https://img.shields.io/npm/v/@mpsuesser/oxlint-plugin-effect)](https://www.npmjs.com/package/@mpsuesser/oxlint-plugin-effect)\n[![JSR](https://jsr.io/badges/@mpsuesser/oxlint-plugin-effect)](https://jsr.io/@mpsuesser/oxlint-plugin-effect)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)\n\nAn opinionated [oxlint](https://oxc.rs/docs/guide/usage/linter) plugin for [Effect v4](https://effect.website) that drives every module toward Effect services, typed error channels, and functional composition. It flags imperative patterns, raw Node APIs, untyped errors, and other non-idiomatic shapes at the lint layer so they never make it into review.\n\nThe plugin ships **73 rules** namespaced under `effect/`, including all 16 rules from [`dmmulroy/anti-slop`](https://github.com/dmmulroy/anti-slop). Rules are implemented with the [`effect-oxlint`](https://github.com/mpsuesser/effect-oxlint) SDK and run as standard oxlint custom rules.\n\n## Installation\n\n```sh\nnpm install @mpsuesser/oxlint-plugin-effect\n# or\nbun add @mpsuesser/oxlint-plugin-effect\n```\n\nUse the generated recommended config from `oxlint.config.ts`:\n\n```ts\nimport { defineConfig } from 'oxlint';\nimport effect from '@mpsuesser/oxlint-plugin-effect';\n\nexport default defineConfig({\n\textends: [effect.configs.recommended]\n});\n```\n\n`configs.recommended` registers the package through oxlint's `jsPlugins` field and enables all 73 rules at `error` severity. `configs.all` also includes every rule.\n\nTo override an individual rule, add a `rules` entry after the `extends` block:\n\n```ts\nimport { defineConfig } from 'oxlint';\nimport effect from '@mpsuesser/oxlint-plugin-effect';\n\nexport default defineConfig({\n\textends: [effect.configs.recommended],\n\trules: {\n\t\t'effect/avoid-native-object-helpers': 'off',\n\t\t'effect/avoid-direct-json': 'warn'\n\t}\n});\n```\n\nIf you use `.oxlintrc.json`, oxlint cannot import a package config object. Configure the JS plugin and any rules you want explicitly:\n\n```jsonc\n{\n\t\"jsPlugins\": [\"@mpsuesser/oxlint-plugin-effect\"],\n\t\"rules\": {\n\t\t\"effect/avoid-direct-json\": \"error\"\n\t}\n}\n```\n\nUse `oxlint.config.ts` when you want the full generated recommended config.\n\n## Rules at a glance\n\n### Anti-slop rules\n\nThese rules apply to JavaScript and TypeScript independently of Effect imports,\nexcept for the architecture policy expressed by `no-service-constructor-imports`.\nThey are part of the same `effect/` namespace and both generated presets.\n\n| Rule | What it catches / exceptions |\n| --- | --- |\n| `no-chained-type-assertions` | Nested `as` or angle-bracket assertions, including parenthesized chains. Chains made only of `as const` are allowed. |\n| `no-conditional-empty-object-spread` | Object spreads with a conditional empty-object branch. No autofix: omitting a property is different from assigning `undefined`. |\n| `no-known-value-widening` | Known expressions flowing into explicit `unknown`, `object`, anonymous-object, or open-dictionary targets, including known arguments passed to local `unknown` type predicates. Empty dictionary accumulators and finite-key `Record` targets are allowed. |\n| `no-module-mocking` | Vitest/Jest `mock`, `doMock`, and `unstable_mockModule` calls, including aliased imports and computed method names. Locally shadowed framework names are allowed. |\n| `no-object-parameters` | `object` inputs, unions containing it, and scoped/transparent generic aliases resolving to it. |\n| `no-reflect-apply` | Global `Reflect.apply`, including computed access. Locally shadowed `Reflect` bindings are allowed. |\n| `no-reflect-get` | Global `Reflect.get`, including computed access. Locally shadowed `Reflect` bindings are allowed. |\n| `no-runtime-typeof` | Runtime `typeof` narrowing. Comparisons with the string `\"undefined\"` are allowed; type-guard checks can be enabled with an option. Type-level `typeof` is unaffected. |\n| `no-service-constructor-imports` | Named `make<CapitalizedName>` imports from `./` or `../` modules outside `*.test.*` and `*.spec.*`. Package/path-alias imports, default imports, and static constructors are outside its scope. |\n| `no-shape-in-symbol-names` | Case-insensitive `shape` in locally owned identifiers, including private and JSX names. Static member access such as `schema.shape` is allowed. |\n| `no-unknown-parameters` | Explicit `unknown` parameters and unions containing it. The parameter named `cause` and the exact subject of a type predicate are allowed. |\n| `no-unknown-returns` | Explicit return contracts resolving to `unknown`, `Promise<unknown>`, or `PromiseLike<unknown>`, including scoped/transparent generic aliases. |\n| `no-unknown-type-aliases` | Scoped/transparent generic aliases resolving to `unknown`. |\n| `no-unsafe-dictionary-type` | Dictionary values based on `unknown`, `any`, `object`, `{}`, or semantic equivalents. Generic constraints such as `T extends Record<string, unknown>` are allowed. |\n| `no-widen-then-assert` | Immutable local flows that widen known evidence to `unknown`, `any`, `object`, or a broad record, then assert it back to a narrower type. |\n| `require-safety-comment-for-type-assertion` | Non-const assertions without a preceding, non-empty invariant justification. The default marker is `SAFETY:`. |\n\nThe analysis uses Oxlint's ESTree and lexical scopes, not a TypeScript type\nchecker. Same-file aliases, forward references, lexical shadowing, and\ntransparent generic aliases are resolved where documented. Imported types and\ncross-file call signatures are not inferred. `no-widen-then-assert` deliberately\nlimits its value-flow analysis to immutable bindings within a function boundary.\n\n### Replacing an anti-slop installation\n\nRemove the old `anti-slop` and `anti-slop-effect` registrations from `jsPlugins`.\nReplace both rule-name prefixes with `effect/`, keeping existing severity and\noption values. Register `@mpsuesser/oxlint-plugin-effect` once, or use the\nrecommended config above to enable the entire personal ruleset. There is no\nruntime dependency on anti-slop and no separate Effect entrypoint to register.\n\nFor example, an existing customized configuration becomes:\n\n```ts\nimport { defineConfig } from 'oxlint';\nimport effect from '@mpsuesser/oxlint-plugin-effect';\n\nexport default defineConfig({\n\textends: [effect.configs.recommended],\n\trules: {\n\t\t'effect/no-runtime-typeof': ['error', { allowInTypeGuards: true }],\n\t\t'effect/require-safety-comment-for-type-assertion': [\n\t\t\t'error',\n\t\t\t{ markers: ['INVARIANT', 'SAFETY'] }\n\t\t]\n\t}\n});\n```\n\n`allowInTypeGuards` defaults to `false` and permits checks directly within type\npredicate/assertion functions, not nested ordinary functions. `markers`\ndefaults to `['SAFETY']`; markers are literal strings (regex punctuation is\nescaped), trimmed before matching, and must be followed by `:` and a non-empty\njustification. Comments before exported declarations are recognized; comments\nafter an assertion do not justify it.\n\n```ts\n// Rejected: assertions fabricate evidence in two steps.\nconst user = input as unknown as User;\n\n// Rejected: the known \"start\" key is erased.\nconst handlers: Record<string, Handler> = { start: startHandler };\n\n// Preserve known keys while checking their values.\nconst preciseHandlers = { start: startHandler } satisfies Record<string, Handler>;\n\n// Accepted by require-safety-comment-for-type-assertion:\n// SAFETY: parseUserId checked the identifier before branding it.\nconst userId = parsedId as UserId;\n```\n\nSome existing rules are intentionally stricter or overlap. `casting-awareness`\nstill reports assertions even with a safety comment; turn it off if justified\nassertions are your chosen policy. `prefer-effect-is` can still report `typeof`\ninside guards even when `no-runtime-typeof` allows them. `avoid-any` overlaps\nwith chained assertions, and `avoid-object-type` addresses `Object`/`{}` rather\nthan the lowercase `object` parameter contract. Configure each independently.\n\n### Effect-first rules\n\n| Rule                                                                                                  | What it catches                                                                  |\n| ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |\n| [`avoid-any`](#avoid-any)                                                                             | `as any` and `as unknown as T` casts                                             |\n| [`avoid-data-tagged-error`](#avoid-data-tagged-error)                                                 | `Data.TaggedError` — use `Schema.TaggedErrorClass`                               |\n| [`avoid-direct-json`](#avoid-direct-json)                                                             | `JSON.parse` / `JSON.stringify` — use `Schema.fromJsonString`                    |\n| [`avoid-direct-tag-checks`](#avoid-direct-tag-checks)                                                 | `x._tag === \"...\"` checks — use `$is` / `$match` / `Match`                       |\n| [`avoid-expect-in-if`](#avoid-expect-in-if)                                                           | `expect(...)` nested inside `if` blocks in tests                                 |\n| [`avoid-mutable-state`](#avoid-mutable-state)                                                         | `let` bindings inside service / layer factories                                  |\n| [`avoid-native-fetch`](#avoid-native-fetch)                                                           | Native `fetch()` — use Effect `HttpClient`                                       |\n| [`avoid-native-object-helpers`](#avoid-native-object-helpers)                                         | `Object.keys` / `Object.entries` / `Object.fromEntries` etc.                     |\n| [`avoid-node-imports`](#avoid-node-imports)                                                           | Bare `node:*` imports in platform-agnostic code                                  |\n| [`avoid-non-null-assertion`](#avoid-non-null-assertion)                                               | The `!` non-null assertion operator                                              |\n| [`avoid-object-type`](#avoid-object-type)                                                             | `Object` and `{}` as types                                                       |\n| [`avoid-option-getorthrow`](#avoid-option-getorthrow)                                                 | `.getOrThrow` on `Option` / `Either` / `Result`                                  |\n| [`avoid-platform-coupling`](#avoid-platform-coupling)                                                 | `@effect/platform-bun` imports in binding packages                               |\n| [`avoid-process-env`](#avoid-process-env)                                                             | `process.env` — use a `Config` service                                           |\n| [`avoid-react-hooks`](#avoid-react-hooks)                                                             | `useState` / `useEffect` / `useReducer` — use Effect Atom VMs                    |\n| [`avoid-schema-suffix`](#avoid-schema-suffix)                                                         | Schema constants suffixed with `Schema`                                          |\n| [`avoid-sync-fs`](#avoid-sync-fs)                                                                     | `fs.readFileSync` and other synchronous `fs` calls                               |\n| [`avoid-try-catch`](#avoid-try-catch)                                                                 | `try` / `catch` in Effect code                                                   |\n| [`avoid-ts-ignore`](#avoid-ts-ignore)                                                                 | `@ts-ignore` / `@ts-expect-error` comments                                       |\n| [`avoid-untagged-errors`](#avoid-untagged-errors)                                                     | `new Error(...)` and `instanceof Error` for recoverable failures                 |\n| [`avoid-yield-ref`](#avoid-yield-ref)                                                                 | `yield* ref` / `yield* deferred` / `yield* fiber` (removed in v4)                |\n| [`casting-awareness`](#casting-awareness)                                                             | `as T` assertions (excluding `as const` / `as never`)                            |\n| [`context-tag-extends`](#context-tag-extends)                                                         | `Context.Tag` / `Context.GenericTag` / `Effect.Service` / legacy `ServiceMap.*`  |\n| [`effect-catchall-default`](#effect-catchall-default)                                                 | Blanket `Effect.catch` / `catchCause` swallowing all errors                      |\n| [`effect-promise-vs-trypromise`](#effect-promise-vs-trypromise)                                       | `Effect.promise` — prefer `Effect.tryPromise`                                    |\n| [`effect-run-in-body`](#effect-run-in-body)                                                           | `Effect.runSync` / `runPromise` / `runFork` outside entrypoints                  |\n| [`imperative-loops`](#imperative-loops)                                                               | `for` / `while` / `do…while` in domain code                                      |\n| [`maybe-prefix-requires-option`](#maybe-prefix-requires-option)                                       | `maybe*` named field that is not an `Option<T>`                                  |\n| [`no-barrel-imports`](#no-barrel-imports)                                                             | Named imports from the `effect` barrel package                                   |\n| [`no-effect-ignore-then-as`](#no-effect-ignore-then-as)                                               | Redundant `Effect.ignore` before `Effect.as`, or on known infallible primitives  |\n| [`no-length-comparison`](#no-length-comparison)                                                       | `.length === 0` and friends — use named string/array predicates                  |\n| [`no-opaque-instance-fields`](#no-opaque-instance-fields)                                             | Instance members on `Schema.Opaque` classes                                      |\n| [`prefer-arr-match`](#prefer-arr-match)                                                               | Manual empty / non-empty branching — use `Arr.match`                             |\n| [`prefer-arr-sort`](#prefer-arr-sort)                                                                 | `Array.prototype.sort` — use `Arr.sort` with an `Order`                          |\n| [`prefer-array-fromoption-over-option-match-empty`](#prefer-array-fromoption-over-option-match-empty) | `Option.match` that produces `[]` / `[v]`                                        |\n| [`prefer-duration-constructors`](#prefer-duration-constructors)                                       | Raw millisecond numbers passed to Effect timing APIs                             |\n| [`prefer-effect-fn`](#prefer-effect-fn)                                                               | `Effect.gen` bound to a const or used in a service method                        |\n| [`prefer-effect-is`](#prefer-effect-is)                                                               | `typeof x === \"string\"` — use `P.isString` and friends                           |\n| [`prefer-match-over-switch`](#prefer-match-over-switch)                                               | `switch` statements — use `Match.value`                                          |\n| [`prefer-namespace-imports`](#prefer-namespace-imports)                                               | Named imports from Effect submodules — use namespace imports                     |\n| [`prefer-option-over-null`](#prefer-option-over-null)                                                 | `T \\| null` / `T \\| undefined` union types                                       |\n| [`prefer-redacted-config`](#prefer-redacted-config)                                                   | `Config.string(\"apiKey\")` etc. for secret-looking keys                           |\n| [`require-effect-concurrency`](#require-effect-concurrency)                                           | `Effect.all` / `forEach` / `validate` without explicit `concurrency`             |\n| [`require-filter-metadata`](#require-filter-metadata)                                                 | `Schema.makeFilter` / `makeFilterGroup` missing identifier / title / description |\n| [`require-is-prefix-for-boolean-schema-field`](#require-is-prefix-for-boolean-schema-field)           | `Schema.Boolean` fields without a boolean predicate prefix                       |\n| [`require-schema-type-alias`](#require-schema-type-alias)                                             | Exported schema constant without a matching `export type` alias                  |\n| [`throw-in-effect-gen`](#throw-in-effect-gen)                                                         | `throw` inside `Effect.gen` / `Effect.fn` / `Effect.fnUntraced`                  |\n| [`use-clock-service`](#use-clock-service)                                                             | `new Date()` / `Date.now()` / `Date.UTC()` — use `Clock` / `DateTime`            |\n| [`use-command-executor-service`](#use-command-executor-service)                                       | `child_process` / `node:child_process` imports                                   |\n| [`use-console-service`](#use-console-service)                                                         | `console.*` — use `Effect.log*` / `Console`                                      |\n| [`use-filesystem-service`](#use-filesystem-service)                                                   | `fs` / `node:fs` / `fs/promises` imports                                         |\n| [`use-http-client-service`](#use-http-client-service)                                                 | `http` / `https` / `node:http` / `node:https` imports                            |\n| [`use-path-service`](#use-path-service)                                                               | `path` / `node:path` imports                                                     |\n| [`use-random-service`](#use-random-service)                                                           | `Math.random()` — use the `Random` service                                       |\n| [`use-temp-file-scoped`](#use-temp-file-scoped)                                                       | `os.tmpdir()` / unscoped `makeTempFile` / `makeTempDirectory`                    |\n| [`vm-in-wrong-file`](#vm-in-wrong-file)                                                               | View Model interfaces and layers outside `.vm.ts` files                          |\n| [`yield-in-for-loop`](#yield-in-for-loop)                                                             | `yield*` inside `for` loops — use `Effect.forEach`                               |\n\n---\n\n### `avoid-any`\n\n`as any` and `as unknown as T` casts erase type safety. Validate unknown data with `Schema.decodeUnknown*`, preserve types with generics, or fix the upstream type.\n\n```ts\n// ❌\nconst user = data as any;\nconst config = JSON.parse(raw) as unknown as Config;\n\n// ✅\nconst user = yield* Schema.decodeUnknown(User)(data);\nconst config = yield* Schema.decodeUnknownString(Config)(raw);\n```\n\n### `avoid-data-tagged-error`\n\n`Data.TaggedError` does not integrate with the Schema encode / decode pipeline. Use `Schema.TaggedErrorClass` so errors round-trip through RPC, serialization, and structured logging.\n\n```ts\n// ❌\nclass NotFound extends Data.TaggedError('NotFound')<{ id: string }> {}\n\n// ✅\nclass NotFound extends Schema.TaggedErrorClass<NotFound>('NotFound')(\n\t'NotFound',\n\t{ id: Schema.String }\n) {}\n```\n\n### `avoid-direct-json`\n\n`JSON.parse` produces `any` and `JSON.stringify` swallows schema shape. Use `Schema.fromJsonString(MySchema)` at typed boundaries or `Schema.UnknownFromJsonString` for unknown payloads.\n\n```ts\n// ❌\nconst user: User = JSON.parse(raw);\nconst body = JSON.stringify(payload);\n\n// ✅\nconst user = yield* Schema.decode(Schema.fromJsonString(User))(raw);\nconst body = yield* Schema.encode(Schema.fromJsonString(Payload))(payload);\n```\n\n### `avoid-direct-tag-checks`\n\nReading `_tag` directly couples call sites to the discriminant string. Use the auto-generated `$is` / `$match` helpers or `Match.value` so renaming a variant is a typed refactor.\n\n```ts\n// ❌\nif (result._tag === 'Success') return result.value;\nswitch (msg._tag) {\n\tcase 'Loaded':\n\t\t/* ... */\n}\n\n// ✅\nif (Result.$is('Success')(result)) return result.value;\nreturn Match.value(msg).pipe(\n\tMatch.tag('Loaded', (m) => /* ... */),\n\tMatch.exhaustive\n);\n```\n\n### `avoid-expect-in-if`\n\n`expect(...)` nested inside an `if` block silently passes when the condition is false. Narrow first, then assert.\n\n```ts\n// ❌\nif (result) {\n\texpect(result.id).toBe('abc');\n}\n\n// ✅\nexpect(result).toBeDefined();\nexpect(result.id).toBe('abc');\n```\n\n### `avoid-mutable-state`\n\n`let` bindings inside service or layer factories hide fiber-visible state and lifecycle behavior. Use `Ref`, `SynchronizedRef`, or `Effect.cached` so concurrent access is explicit. `let` inside pure helpers and narrow scopes is fine.\n\n```ts\n// ❌\nexport const CounterLive = Layer.effect(\n\tCounter,\n\tEffect.gen(function* () {\n\t\tlet count = 0;\n\t\treturn Counter.of({ inc: () => Effect.sync(() => count++) });\n\t})\n);\n\n// ✅\nexport const CounterLive = Layer.effect(\n\tCounter,\n\tEffect.gen(function* () {\n\t\tconst count = yield* Ref.make(0);\n\t\treturn Counter.of({ inc: () => Ref.update(count, (n) => n + 1) });\n\t})\n);\n```\n\n### `avoid-native-fetch`\n\nNative `fetch()` returns a `Promise<Response>` with untyped errors. Effect's `HttpClient` gives you typed errors, request / response schemas, and testable layer substitution.\n\n```ts\n// ❌\nconst res = await fetch('/api/users');\nconst users = await res.json();\n\n// ✅\nconst client = yield* HttpClient.HttpClient;\nconst users =\n\tyield* client\n\t\t.get('/api/users')\n\t\t.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(UserList)));\n```\n\n### `avoid-native-object-helpers`\n\n`Object.keys` returns `string[]` (not `keyof T`); `Object.entries` loses value types. Use the `effect/Record` helpers for type-safe equivalents.\n\n```ts\n// ❌\nconst keys = Object.keys(user);\nconst entries = Object.entries(config);\nconst obj = Object.fromEntries(pairs);\n\n// ✅\nimport * as R from 'effect/Record';\nconst keys = R.keys(user);\nconst entries = R.toEntries(config);\nconst obj = R.fromEntries(pairs);\n```\n\n### `avoid-node-imports`\n\n`node:*` imports tie domain code to a single runtime. Use `@effect/platform` abstractions so the same code runs on Node, Bun, Deno, and Workers. Dedicated rules cover the most common cases (`use-filesystem-service`, `use-path-service`, `use-command-executor-service`, `use-http-client-service`); this rule is the catch-all.\n\n```ts\n// ❌\nimport { createHash } from 'node:crypto';\nimport { Readable } from 'node:stream';\n\n// ✅  Provide a service or pass a `FromEffect`-shaped Layer\nimport { Crypto } from '@effect/platform/Crypto';\nimport { Stream } from 'effect/Stream';\n```\n\n### `avoid-non-null-assertion`\n\n`!` tells the compiler \"trust me\" and crashes at runtime when wrong. Model absence with `Option`, decode unknown shapes via `Schema.decodeUnknown*`, or guard at the boundary with `?.` / `??` / `Option.fromNullishOr`.\n\n```ts\n// ❌\nconst name = user!.profile!.displayName!;\n\n// ✅\nconst name = Option.fromNullishOr(user).pipe(\n\tOption.flatMapNullishOr((u) => u.profile?.displayName),\n\tOption.getOrElse(() => 'Anonymous')\n);\n```\n\n### `avoid-object-type`\n\n`Object` provides no type safety, and `{}` matches any non-nullish value (including `42` and `\"hi\"`). Use a specific interface, `Record<string, unknown>`, or a `Schema.Class`.\n\n```ts\n// ❌\nfunction merge(a: object, b: {}): object { ... }\n\n// ✅\nfunction merge<A extends Record<string, unknown>>(a: A, b: Partial<A>): A { ... }\n```\n\n### `avoid-option-getorthrow`\n\n`.getOrThrow` defeats the point of `Option` / `Either` / `Result` by throwing where the type promised a total handler. Use `match`, `getOrElse`, or `map`.\n\n```ts\n// ❌\nconst value = Option.getOrThrow(maybeUser);\n\n// ✅\nconst value = Option.match(maybeUser, {\n\tonNone: () => defaultUser,\n\tonSome: (u) => u\n});\n```\n\n### `avoid-platform-coupling`\n\nPackages under `packages/*/binding/` are the seam where platform-specific code lives — they import `@effect/platform-bun`, `@effect/platform-node`, etc. Code outside the `binding/` directory must stay platform-agnostic so it can run anywhere.\n\n```ts\n// ❌  packages/myapp/src/MyService.ts\nimport { BunHttpServer } from '@effect/platform-bun';\n\n// ✅  packages/myapp/binding/index.ts\nimport { BunHttpServer } from '@effect/platform-bun';\n```\n\n### `avoid-process-env`\n\n`process.env` is untyped, untested, and global. `Config.*` builds typed, layered, redactable configuration with default values and validation.\n\n```ts\n// ❌\nconst apiKey = process.env.API_KEY!;\nconst port = parseInt(process.env.PORT ?? '3000');\n\n// ✅\nconst apiKey = yield* Config.redacted('API_KEY');\nconst port = yield* Config.integer('PORT').pipe(Config.withDefault(3000));\n```\n\n### `avoid-react-hooks`\n\nReact hooks scatter state, effects, and rendering across a single component. VMs with Effect Atom keep state in atoms, effects in actions, and components as pure renderers.\n\n```ts\n// ❌\nfunction Profile({ id }: Props) {\n\tconst [user, setUser] = useState<User>();\n\tuseEffect(() => { fetchUser(id).then(setUser); }, [id]);\n\treturn <div>{user?.name}</div>;\n}\n\n// ✅\n// profile.vm.ts\nexport const userAtom = Atom.family((id: string) =>\n\tAtom.fn(Effect.fn('fetchUser')(function* () { ... }))\n);\n\n// profile.tsx\nfunction Profile({ id }: Props) {\n\tconst user = useAtomValue(userAtom(id));\n\treturn <div>{user.name}</div>;\n}\n```\n\n### `avoid-schema-suffix`\n\nSchema constants represent a domain type, not \"a schema for a type.\" Name them after the concept (`User`) rather than the construction (`UserSchema`) — this matches how `Schema.Class` is named and keeps types and instances grep-symmetric.\n\n```ts\n// ❌\nconst UserSchema = Schema.Struct({ id: Schema.String });\n\n// ✅\nconst User = Schema.Struct({ id: Schema.String });\nexport type User = typeof User.Type;\n```\n\n### `avoid-sync-fs`\n\nSynchronous `fs` calls block the event loop. Use `FileSystem` from `@effect/platform` for async, composable, testable file I/O.\n\n```ts\n// ❌\nconst text = fs.readFileSync(path, 'utf8');\nfs.writeFileSync(path, data);\n\n// ✅\nconst fs = yield* FileSystem.FileSystem;\nconst text = yield* fs.readFileString(path);\nyield* fs.writeFileString(path, data);\n```\n\n### `avoid-try-catch`\n\n`try` / `catch` discards the failure type and forces every caller to inspect a generic `unknown`. Use `Effect.try` or `Effect.tryPromise` with `Schema.TaggedErrorClass` to keep errors in the typed channel.\n\n```ts\n// ❌\ntry {\n\treturn JSON.parse(raw);\n} catch (e) {\n\treturn null;\n}\n\n// ✅\nreturn (\n\tyield* Effect.try({\n\t\ttry: () => JSON.parse(raw),\n\t\tcatch: () => new ParseFailed({ raw })\n\t})\n);\n```\n\n### `avoid-ts-ignore`\n\n`@ts-ignore` and `@ts-expect-error` mask real bugs and silently rot when the underlying type changes. Fix the type at the source instead.\n\n```ts\n// ❌\n// @ts-ignore\nconst result = someApi.experimental.method();\n\n// ✅\n// Augment the third-party type or wrap in a typed adapter\ndeclare module 'some-api' {\n\tinterface ExperimentalApi {\n\t\tmethod(): Result;\n\t}\n}\n```\n\n### `avoid-untagged-errors`\n\n`new Error(...)` and `instanceof Error` make every failure interchangeable. `Schema.TaggedErrorClass` gives each domain failure mode a tag that `catchTag` / `catchTags` can discriminate at the type level. Use `Schema.isSchemaError` when narrowing schema failures.\n\n```ts\n// ❌\nthrow new Error('User not found');\nif (err instanceof Error) return null;\n\n// ✅\nclass UserNotFound extends Schema.TaggedErrorClass<UserNotFound>(\n\t'UserNotFound'\n)('UserNotFound', { id: Schema.String }) {}\n\nyield* Effect.fail(new UserNotFound({ id }));\nyield* effect.pipe(Effect.catchTag('UserNotFound', () => Effect.succeed(null)));\n```\n\n### `avoid-yield-ref`\n\nDirect `yield* ref` / `yield* deferred` / `yield* fiber` / `yield* latch` was removed in Effect v4. Use the explicit method calls.\n\n```ts\n// ❌\nconst value = yield* counter;\nconst result = yield* deferred;\n\n// ✅\nconst value = yield* Ref.get(counter);\nconst result = yield* Deferred.await(deferred);\nconst exit = yield* Fiber.join(fiber);\nyield* Latch.await(latch);\n```\n\n### `casting-awareness`\n\nEvery `as T` assertion is a checkpoint: is the cast redundant? Can generics or `Schema.decode` replace it? Does the upstream type need fixing? `as const` and `as never` are always allowed; everything else gets flagged so the reviewer notices.\n\n```ts\n// ❌\nconst items = (data as Array<User>).filter((u) => u.active);\n\n// ✅\nconst items =\n\tyield* Schema.decodeUnknown(Schema.Array(User))(data).pipe(\n\t\tEffect.map(Arr.filter((u) => u.active))\n\t);\n\n// ✅  as const is fine\nconst STATUSES = ['Pending', 'Active', 'Closed'] as const;\n```\n\n### `context-tag-extends`\n\n`class FooTag extends Context.Tag(...)`, `Context.GenericTag`, `Effect.Service`, and the legacy `ServiceMap.*` aliases were all removed or superseded in Effect v4. Define services with `Context.Service` and name them directly — no `*Tag` suffix.\n\n```ts\n// ❌\nclass UserRepoTag extends Context.Tag('UserRepo')<UserRepoTag, Service>() {}\nconst UserRepo = Context.GenericTag<Service>('UserRepo');\nclass UserRepo extends Effect.Service<Service>()('UserRepo', { ... }) {}\n\n// ✅\nclass UserRepo extends Context.Service<UserRepo, Service>()('UserRepo') {}\n```\n\n### `effect-catchall-default`\n\nBlanket `Effect.catch` / `Effect.catchCause` returning a default value silently swallows every failure mode — including ones you didn't know about. Use `catchTag` / `catchTags` for targeted recovery.\n\n```ts\n// ❌\neffect.pipe(Effect.catchAll(() => Effect.succeed(defaultUser)));\n\n// ✅\neffect.pipe(\n\tEffect.catchTags({\n\t\tUserNotFound: () => Effect.succeed(defaultUser),\n\t\tNetworkError: (e) => Effect.fail(e) // re-raise the rest\n\t})\n);\n```\n\n### `effect-promise-vs-trypromise`\n\n`Effect.promise` treats rejections as defects (unrecoverable). `Effect.tryPromise` captures them in the typed error channel so callers can `catchTag` them.\n\n```ts\n// ❌\nconst user = yield* Effect.promise(() => fetchUser(id));\n\n// ✅\nconst user =\n\tyield* Effect.tryPromise({\n\t\ttry: () => fetchUser(id),\n\t\tcatch: (cause) => new FetchFailed({ cause })\n\t});\n```\n\n### `effect-run-in-body`\n\n`Effect.runSync` / `runPromise` / `runFork` collapse the program down to a concrete value. Keep them at the boundary (`main.ts`, the test harness, the HTTP route handler) and return `Effect` values everywhere else.\n\n```ts\n// ❌  inside a service method\nconst get = (id: string) => {\n\tconst user = Effect.runSync(fetchUser(id));\n\treturn user;\n};\n\n// ✅\nconst get = (id: string) => fetchUser(id);\n```\n\n### `imperative-loops`\n\n`for`, `while`, and `do…while` over collections obscure the intent of the transformation. Use `Arr.map`, `Arr.filter`, `Arr.filterMap`, `Arr.reduce`, or `Effect.forEach` so the operation is on the page.\n\n```ts\n// ❌\nconst names: Array<string> = [];\nfor (const user of users) {\n\tif (user.active) names.push(user.name);\n}\n\n// ✅\nconst names = Arr.filterMap(users, (u) =>\n\tu.active ? Option.some(u.name) : Option.none()\n);\n```\n\n### `no-barrel-imports`\n\nNamed imports from the `effect` barrel pull the entire module graph and break tree-shaking. Import from the submodule instead.\n\n```ts\n// ❌\nimport { Effect, Array as Arr, Option } from 'effect';\n\n// ✅\nimport * as Effect from 'effect/Effect';\nimport * as Arr from 'effect/Array';\nimport * as Option from 'effect/Option';\n```\n\n### `no-opaque-instance-fields`\n\n`Schema.Opaque` classes are pure type-level wrappers — they have no runtime identity beyond the underlying schema. Adding instance methods or fields turns them into something the type system can no longer treat as opaque.\n\n```ts\n// ❌\nclass UserId extends Schema.Opaque<UserId>()(Schema.String) {\n\tgreet() {\n\t\treturn `Hello ${this.toString()}`;\n\t}\n}\n\n// ✅\nclass UserId extends Schema.Opaque<UserId>()(Schema.String) {}\nconst greet = (id: UserId) => `Hello ${id}`;\n```\n\n### `prefer-arr-match`\n\nManual `.length === 0` / `.length > 0` branching obscures the empty vs non-empty intent. `Arr.match` makes both branches explicit and gives you the non-empty array witness in the body.\n\n```ts\n// ❌\nif (items.length === 0) return placeholder;\nreturn list(items);\n\n// ✅\nreturn Arr.match(items, {\n\tonEmpty: () => placeholder,\n\tonNonEmpty: (xs) => list(xs)\n});\n```\n\n### `prefer-arr-sort`\n\n`Array.prototype.sort` mutates in place, sorts lexicographically by default, and has no notion of an `Order`. `Arr.sort` is immutable and composes with `Order` combinators.\n\n```ts\n// ❌\nconst sorted = [...users].sort((a, b) => a.age - b.age);\n\n// ✅\nconst sorted = Arr.sort(\n\tusers,\n\tOrder.mapInput(Order.number, (u: User) => u.age)\n);\n```\n\n### `prefer-duration-constructors`\n\nRaw millisecond literals passed to `Effect.sleep`, `Schedule.spaced`, etc. read poorly. `Duration.seconds`, `Duration.minutes`, `Duration.millis` keep units at the call site.\n\n```ts\n// ❌\nyield* Effect.sleep(5000);\nconst sched = Schedule.spaced(60_000);\n\n// ✅\nyield* Effect.sleep(Duration.seconds(5));\nconst sched = Schedule.spaced(Duration.minutes(1));\n```\n\n### `prefer-effect-fn`\n\n`Effect.gen(function*() { ... })` assigned to a `const`, or used as a service method, lacks an attached span name. `Effect.fn(\"name\")(function*() { ... })` adds automatic tracing.\n\n```ts\n// ❌\nconst getUser = (id: string) => Effect.gen(function* () { ... });\n\nconst make = Effect.gen(function* () {\n\treturn UserRepo.of({\n\t\tget: (id) => Effect.gen(function* () { ... })\n\t});\n});\n\n// ✅\nconst getUser = Effect.fn('getUser')(function* (id: string) { ... });\n\nconst make = Effect.gen(function* () {\n\treturn UserRepo.of({\n\t\tget: Effect.fn('UserRepo.get')(function* (id) { ... })\n\t});\n});\n```\n\n### `prefer-effect-is`\n\n`typeof x === \"string\"` is non-composable and doesn't narrow union types as cleanly as Effect's `Predicate` helpers.\n\n```ts\n// ❌\nif (typeof value === 'string') return value;\nif (typeof n === 'number' && n > 0) return n;\n\n// ✅\nimport * as P from 'effect/Predicate';\nif (P.isString(value)) return value;\nif (P.isNumber(n) && n > 0) return n;\n```\n\n### `prefer-match-over-switch`\n\n`switch` is not exhaustive (TypeScript can't prove every case is handled) and doesn't compose with pipe. `Match.value` is exhaustive, expression-level, and pipe-friendly.\n\n```ts\n// ❌\nswitch (status) {\n\tcase 'Pending':\n\t\treturn spinner();\n\tcase 'Active':\n\t\treturn view();\n\tcase 'Closed':\n\t\treturn summary();\n}\n\n// ✅\nreturn Match.value(status).pipe(\n\tMatch.when('Pending', () => spinner()),\n\tMatch.when('Active', () => view()),\n\tMatch.when('Closed', () => summary()),\n\tMatch.exhaustive\n);\n```\n\n### `prefer-namespace-imports`\n\nNamed imports from `effect/*` submodules break tree-shaking and diverge from Effect's canonical idiom. Use namespace imports with the canonical alias (`Effect`, `Arr` for `effect/Array`, `Option`, `R` for `effect/Record`, etc.).\n\n```ts\n// ❌\nimport { map, filter } from 'effect/Array';\nimport { Array } from 'effect';\n\n// ✅\nimport * as Arr from 'effect/Array';\nimport * as Effect from 'effect/Effect';\n```\n\n### `prefer-option-over-null`\n\n`T | null` / `T | undefined` doesn't compose: every caller has to repeat the null check. `Option<T>` ships `map`, `flatMap`, `match`, `getOrElse`, and friends.\n\n```ts\n// ❌\nfunction find(id: string): User | null { ... }\n\n// ✅\nfunction find(id: string): Option.Option<User> { ... }\n```\n\n### `prefer-redacted-config`\n\nConfiguration keys whose name conventionally identifies a secret (`apiKey`, `authToken`, `password`, `privateKey`, `dsn`, etc.) must be loaded as `Config.redacted(...)` (or wrapped in `Schema.Redacted` inside a `Config.schema`) so the value stays masked in logs and `toString`.\n\n```ts\n// ❌\nconst apiKey = yield* Config.string('apiKey');\nconst cfg =\n\tyield* Config.schema(\n\t\tSchema.Struct({\n\t\t\tapiKey: Schema.String\n\t\t})\n\t);\n\n// ✅\nconst apiKey = yield* Config.redacted('apiKey');\nconst cfg =\n\tyield* Config.schema(\n\t\tSchema.Struct({\n\t\t\tapiKey: Schema.Redacted(Schema.String)\n\t\t})\n\t);\n```\n\n### `require-effect-concurrency`\n\n`Effect.all`, `Effect.forEach`, `Effect.validate`, and friends silently default to sequential execution. Sequential is sometimes correct — but it's a concurrency decision, so it should be reviewable at the call site.\n\n```ts\n// ❌\nyield* Effect.forEach(ids, fetchUser);\n\n// ✅\nyield* Effect.forEach(ids, fetchUser, { concurrency: 'unbounded' });\nyield* Effect.forEach(ids, fetchUser, { concurrency: 4 });\nyield* Effect.forEach(ids, fetchUser, { concurrency: 1 }); // explicit sequential\n```\n\n### `require-filter-metadata`\n\n`Schema.makeFilter` and `Schema.makeFilterGroup` produce reusable validators. Without `identifier`, `title`, and `description` they show up in error messages and OpenAPI docs as opaque blobs.\n\n```ts\n// ❌\nconst PositiveInt = Schema.makeFilter((n: number) => n > 0);\n\n// ✅\nconst PositiveInt = Schema.makeFilter((n: number) => n > 0, {\n\tidentifier: 'PositiveInt',\n\ttitle: 'Positive integer',\n\tdescription: 'A whole number strictly greater than zero.'\n});\n```\n\n### `require-schema-type-alias`\n\nExported `Schema.Struct` / `Schema.TaggedStruct` / `Schema.Literals` constants don't carry a TypeScript type at the value name. Pair them with `export type Foo = typeof Foo.Type` so importers can refer to the inferred type.\n\n```ts\n// ❌\nexport const User = Schema.Struct({ id: Schema.String });\n\n// ✅\nexport const User = Schema.Struct({ id: Schema.String });\nexport type User = typeof User.Type;\n```\n\n### `throw-in-effect-gen`\n\n`throw` inside `Effect.gen` / `Effect.fn` / `Effect.fnUntraced` lands in the unrecoverable defect channel. Use `yield* Effect.fail(new MyError(...))` (or `yield* new MyTaggedError({ ... })`) to keep failures typed. The `try:` arm of `Effect.tryPromise` / `Effect.try` is excluded — that's the point of `try:`.\n\n```ts\n// ❌\nEffect.gen(function* () {\n\tif (!user) throw new Error('User missing');\n\treturn user;\n});\n\n// ✅\nEffect.gen(function* () {\n\tif (!user) return yield* Effect.fail(new UserMissing({ id }));\n\treturn user;\n});\n```\n\n### `use-clock-service`\n\n`new Date()` / `Date.now()` / `Date.UTC()` are non-deterministic and untestable. Use the `Clock` service or the `DateTime` module so tests can freeze time.\n\n```ts\n// ❌\nconst now = new Date();\nconst ms = Date.now();\n\n// ✅\nconst now = yield* DateTime.now;\nconst ms = yield* Clock.currentTimeMillis;\n```\n\n### `use-command-executor-service`\n\n`child_process` / `node:child_process` ties code to Node's process model and yields untyped errors. Use `ChildProcessSpawner` + `ChildProcess` from `effect/unstable/process`, or `Command` + `CommandExecutor` from `@effect/platform`, for typed, scoped, composable process spawning.\n\n```ts\n// ❌\nimport { spawn } from 'node:child_process';\nconst proc = spawn('git', ['status']);\n\n// ✅\nimport { Command } from '@effect/platform';\nconst status = yield* Command.make('git', 'status').pipe(Command.string);\n```\n\n### `use-console-service`\n\n`console.*` writes to stdout / stderr without spans, structured fields, or test capture. Use `Effect.logInfo` / `logError` / `logWarning` / `logDebug` (preferred), or the `Console` service.\n\n```ts\n// ❌\nconsole.log('Fetched user', user.id);\nconsole.error('Failed', err);\n\n// ✅\nyield* Effect.logInfo('Fetched user').pipe(\n\tEffect.annotateLogs('userId', user.id)\n);\nyield* Effect.logError('Failed').pipe(Effect.annotateLogs('cause', err));\n```\n\n### `use-filesystem-service`\n\n`fs`, `node:fs`, and `fs/promises` tie code to Node's runtime. The `FileSystem` service from `@effect/platform` is portable, layer-substitutable, and integrates with `Stream`, `Scope`, and the rest of Effect.\n\n```ts\n// ❌\nimport * as fs from 'node:fs/promises';\nconst text = await fs.readFile(path, 'utf8');\n\n// ✅\nimport { FileSystem } from '@effect/platform';\nconst fs = yield* FileSystem.FileSystem;\nconst text = yield* fs.readFileString(path);\n```\n\n### `use-http-client-service`\n\nDirect `http` / `https` imports give you a raw socket and untyped errors. Use `HttpClient`, `HttpClientRequest`, and `HttpClientResponse` for typed responses, automatic retries, and testable layer substitution.\n\n```ts\n// ❌\nimport * as https from 'node:https';\nhttps.get(url, (res) => { ... });\n\n// ✅\nconst client = yield* HttpClient.HttpClient;\nconst json = yield* client.get(url).pipe(\n\tEffect.flatMap(HttpClientResponse.schemaBodyJson(Payload))\n);\n```\n\n### `use-path-service`\n\n`node:path` is Posix-or-Windows-flavored depending on the runtime. The `Path` service from `@effect/platform` is explicit about which variant you're using and is testable / mockable.\n\n```ts\n// ❌\nimport * as path from 'node:path';\nconst full = path.join(dir, name);\n\n// ✅\nimport { Path } from '@effect/platform';\nconst path_ = yield* Path.Path;\nconst full = path_.join(dir, name);\n```\n\n### `use-random-service`\n\n`Math.random()` is non-deterministic; tests can't pin it. The `Random` service threads a seed through the program so `Random.nextInt` is reproducible.\n\n```ts\n// ❌\nconst n = Math.floor(Math.random() * 100);\n\n// ✅\nconst n = yield* Random.nextIntBetween(0, 100);\n```\n\n### `use-temp-file-scoped`\n\n`os.tmpdir()` and unscoped `FileSystem.makeTempFile` / `makeTempDirectory` leak temp files when the program crashes. Use `FileSystem.makeTempFileScoped` / `makeTempDirectoryScoped` so cleanup is tied to the `Scope`.\n\n```ts\n// ❌\nimport { tmpdir } from 'node:os';\nconst dir = path.join(tmpdir(), 'work');\n\n// ✅\nconst fs = yield* FileSystem.FileSystem;\nconst dir = yield* fs.makeTempDirectoryScoped();\n```\n\n### `vm-in-wrong-file`\n\nView Model interfaces and their layers belong in `.vm.ts` files. Co-locating them with the component flattens the seam between rendering and state management — and the seam is the whole point of the VM pattern.\n\n```ts\n// ❌  profile.tsx\nexport interface ProfileVM { ... }\nexport const ProfileVMLive = Layer.effect(...);\n\n// ✅  profile.vm.ts\nexport interface ProfileVM { ... }\nexport const ProfileVMLive = Layer.effect(...);\n```\n\n### `yield-in-for-loop`\n\n`yield*` inside a `for` loop forces sequential execution and hides the iteration intent. `Effect.forEach` is declarative and parallelizable.\n\n```ts\n// ❌\nfor (const id of ids) {\n\tyield* fetchUser(id);\n}\n\n// ✅\nyield* Effect.forEach(ids, fetchUser, { concurrency: 'unbounded' });\n```\n\n---\n\n### `maybe-prefix-requires-option`\n\nA field named `maybeX` promises an `Option<X>`. Using the prefix with a plain `T | null`, `T | undefined`, or a Schema optional/nullable field breaks reader expectations.\n\n`maybe*` names should be typed as `Option<T>` in TypeScript and as `Schema.Option(...)` / `Schema.OptionFromNullishOr(...)` in Schema structs. If the value really is nullable rather than optional, rename it to `nullableX`.\n\n### `no-effect-ignore-then-as`\n\n`Effect.ignore` discards both the success value and the error channel. When it appears immediately before `Effect.as(...)`, the `as` already discards the success value, so `ignore` only erases failures silently.\n\nThe rule also flags `Effect.ignore` on known infallible primitives where there is no error channel to ignore.\n\n### `no-length-comparison`\n\nManual `.length === 0`, `.length > 0`, and related checks hide whether the value is a string or an array. Prefer named predicates such as `Str.isEmpty`, `Str.isNonEmpty`, `Arr.isReadonlyArrayEmpty`, `Arr.isReadonlyArrayNonEmpty`, or branch with `Arr.match`.\n\n### `prefer-array-fromoption-over-option-match-empty`\n\n`Option<A>` to `ReadonlyArray<A>` is `Array.fromOption`. Spelling that as `Option.match({ onNone: () => [], onSome: (v) => [v] })` obscures the intent.\n\n### `require-is-prefix-for-boolean-schema-field`\n\nBoolean Schema fields should read as predicates at call sites. Use prefixes such as `is*`, `has*`, `can*`, `should*`, `was*`, or `will*` for `Schema.Boolean` fields.\n\n## Suppression\n\nAll rules respect oxlint's standard disable directives:\n\n```ts\n// oxlint-disable-next-line effect/<rule-name> -- reason\n\n/* oxlint-disable effect/<rule-name> -- reason */\n// ... block ...\n/* oxlint-enable effect/<rule-name> */\n```\n\nA trailing `-- <reason>` comment is encouraged for any suppression that lives longer than a single PR review.\n\n## Development\n\n```sh\nbun install\nbun test          # run the test suite (429 tests across 57 rules)\nbun run check     # format + lint + typecheck\n```\n\nEach rule lives in `src/rules/<rule-name>.ts` with a sibling test in `test/rules/<rule-name>.test.ts`. The rule SDK is documented at [`effect-oxlint`](https://github.com/mpsuesser/effect-oxlint).\n\nThe same rule set is also expressed as a [`pi-effect-harness`](https://github.com/mpsuesser/pi-effect-harness) pattern catalog for ast-grep — the two implementations are kept in alignment.\n\n## License\n\nMIT. Anti-slop-derived rules and fixtures retain their upstream MIT notice in\n[`licenses/anti-slop.txt`](./licenses/anti-slop.txt); see\n[`licenses/README.md`](./licenses/README.md) for provenance.\n","readmeFilename":"README.md"}