diff --git a/packages/core-eslint/base.js b/packages/core-eslint/base.js index e1607f2..351361e 100644 --- a/packages/core-eslint/base.js +++ b/packages/core-eslint/base.js @@ -103,4 +103,66 @@ export default [ "no-restricted-imports": "off", }, }, + // E1 โ€” Event handlers must not be re-exported. Wire them only inside the + // consumer feature's bind-production / bind-dev-seed (spec ยง 2.2 Rule E1). + // J โ€” Direct `payload.jobs.*` access is forbidden outside the integration + // layer. Use IJobQueue (from @repo/core-shared/jobs) instead. + { + files: ["**/*.{ts,tsx,mjs,cjs,js}"], + rules: { + "no-restricted-syntax": [ + "error", + { + selector: + "ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]", + message: + "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).", + }, + { + selector: + "ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]", + message: + "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).", + }, + { + selector: + "MemberExpression[object.type='MemberExpression'][object.object.type='Identifier'][object.object.name='payload'][object.property.type='Identifier'][object.property.name='jobs']", + message: + "Direct `payload.jobs.*` access is not allowed here. Use IJobQueue (from @repo/core-shared/jobs) instead. Allowed only in **/integrations/cms/jobs/** and **/core-shared/src/jobs/**.", + }, + ], + }, + }, + // J โ€” `payload.jobs.*` is allowed only in the integration layer. + // In these paths, no-restricted-syntax is narrowed to keep E1 active but + // drop the payload.jobs check. + // Note: "**/core-shared/src/jobs/**" does not match from within a package-local + // ESLint run because ESLint resolves globs relative to the config file location. + // The pattern is kept for documentation; in practice, the PayloadJobQueue class + // uses `this.payload.jobs.*` which the selector already ignores (it only catches + // bare `payload.jobs.*`). Any new file added there that does use bare `payload.jobs.*` + // would need this allowlist to be expressed as "**/jobs/payload-*" or similar. + { + files: [ + "**/integrations/cms/jobs/**", + "**/core-shared/src/jobs/**", + ], + rules: { + "no-restricted-syntax": [ + "error", + { + selector: + "ExportNamedDeclaration[source.value=/\\/events\\/handlers\\//]", + message: + "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).", + }, + { + selector: + "ExportAllDeclaration[source.value=/\\/events\\/handlers\\//]", + message: + "Event handlers (events/handlers/*.handler.ts) must not be re-exported. Wire them only inside the consumer feature's bind-production / bind-dev-seed (Rule E1).", + }, + ], + }, + }, ];