@mettlecast/eslint-plugin-domain-module
v0.2.97
Published
ESLint plugin enforcing TIB Domain Module framework usage patterns. Prevents accidental AWS SDK, HTTP server, Postgres client, raw DB lifecycle calls, and cross-domain violations in domain handler files.
Readme
@mettlecast/eslint-plugin-domain-module
ESLint plugin enforcing TIB Domain Module framework usage patterns. Prevents accidental AWS SDK, HTTP server, Postgres client, raw DB lifecycle calls, and cross-domain violations in domain handler files.
Install
npm install --save-dev @mettlecast/eslint-plugin-domain-moduleConfigure in eslint.config.js:
import domainModulePlugin from '@mettlecast/eslint-plugin-domain-module';
export default [
{
files: ['src/domain/**/*.ts'],
...domainModulePlugin.configs.recommended,
},
];Rules
| Rule | Flags | Severity |
|---|---|---|
| no-raw-aws-sdk | Direct imports of @aws-sdk/* packages in domain handler files (Lambda, DynamoDB, S3, EventBridge, SQS, SNS, Secrets Manager, STS, Cognito, etc. — each maps to the correct ctx.* method) | error |
| no-raw-pg-client | Direct pg, pg-pool, pg-cursor, postgres, pg-protocol, pg-query-stream imports — use ctx.db | error |
| no-raw-db-client-release | Low-level client.release() / pool.end() / pgClient.release() / poolClient.release() calls — runtime owns pool lifecycle | error |
| no-raw-http-server | Framework imports (express, koa, fastify, aws-lambda) in domain handlers | error |
| no-raw-fetch | Raw fetch() calls in domain handlers — use ctx.fetch | error |
| require-define-primitive | Handler files without a top-level define*() call (missing domain primitive declaration) | warn |
| no-cross-domain-internal-import | Importing internal modules (not exported types) from another domain directory | error |
| flow-domain-ownership | Flow files in domains/{x}/flows/ must declare owningDomain: 'x' | error |
| zod-defaults-required | Top-level Zod input/output schemas must end in .default(...) | error |
| api-needs-fixture | Every API-exposed defineAction needs a sibling __tests__/<id>.fixture.json | error |
| prefer-result-over-throw | Domain handlers must return err({...}) instead of throwing | error |
| use-tanstack-router | Frontend must use TanStack Router | error |
| tanstack-query-options | TanStack Query data must be wrapped in queryOptions(...) | warn |
Recommended Config
The recommended preset enables all rules at their default severity levels:
domainModulePlugin.configs.recommended
// Results in:
// - no-raw-aws-sdk: error
// - no-raw-pg-client: error
// - no-raw-db-client-release: error
// - no-raw-http-server: error
// - no-raw-fetch: error
// - require-define-primitive: warn
// - no-cross-domain-internal-import: error
// - flow-domain-ownership: error
// - zod-defaults-required: error
// - api-needs-fixture: error
// - prefer-result-over-throw: error
// - use-tanstack-router: error
// - tanstack-query-options: warnExamples
Violations caught:
// ❌ no-raw-aws-sdk (Lambda) — use ctx.actions.call(...)
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
// ❌ no-raw-aws-sdk (DynamoDB) — use ctx.store.{get,put,query,...}
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
// ❌ no-raw-aws-sdk (S3) — use ctx.files.{get,put,...}
import { S3Client } from '@aws-sdk/client-s3';
// ❌ no-raw-pg-client — use ctx.db.query(...) / ctx.db.client.query(...)
import { Pool } from 'pg';
// ❌ no-raw-db-client-release — runtime owns pool lifecycle
pool.end();
client.release();
// ❌ no-raw-http-server
import express from 'express';
// ❌ require-define-primitive
export async function chargePayment(event, ctx) { /* ... */ }
// ❌ no-cross-domain-internal-import
import { internalFn } from '../payments/internal.js'; // not in payments/index.tsCorrect usage:
// ✓ Backend-to-backend calls go through the action envelope
await ctx.actions.call('payments.charge-card', { amount });
// ✓ Per-tenant DynamoDB
await ctx.store.put('user#123', { email: '[email protected]' });
// ✓ Per-tenant S3
await ctx.files.put('reports/q4.pdf', pdfBuffer);
// ✓ DB queries always carry Wave 5 session variables
await ctx.db.query('SELECT * FROM orders WHERE tenant = $1', [ctx.tenant.id]);
// ✓ Runtime releases the pool for you — do not call .release() / .end()
await ctx.db.release(); // only if you really need to release earlySee Also
@mettlecast/domain-runtime— handler definitions andDomainContext@mettlecast/domain-cli— validation and linting
