Files
gridpilot.gg/apps/website/eslint-rules/mutation-contract.js
2026-01-13 00:16:14 +01:00

73 lines
2.0 KiB
JavaScript

/**
* ESLint Rule: Mutation Contract
*
* Ensures mutations return Result type
*/
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Ensure mutations return Result type',
category: 'Mutation Contract',
recommended: true,
},
messages: {
wrongReturnType: 'Mutations must return Promise<Result<void, string>> - see apps/website/lib/contracts/Result.ts',
},
schema: [],
},
create(context) {
const filename = context.getFilename();
// Only apply to mutation files
if (!filename.includes('/lib/mutations/') || !filename.endsWith('.ts')) {
return {};
}
return {
MethodDefinition(node) {
if (node.key.type === 'Identifier' &&
node.key.name === 'execute' &&
node.value.type === 'FunctionExpression') {
const returnType = node.value.returnType;
// Check if it returns Promise<Result<...>>
if (!returnType ||
!returnType.typeAnnotation ||
!returnType.typeAnnotation.typeName ||
returnType.typeAnnotation.typeName.name !== 'Promise') {
context.report({
node,
messageId: 'wrongReturnType',
});
return;
}
// Check for Result type
const typeArgs = returnType.typeAnnotation.typeParameters;
if (!typeArgs || !typeArgs.params || typeArgs.params.length === 0) {
context.report({
node,
messageId: 'wrongReturnType',
});
return;
}
const resultType = typeArgs.params[0];
if (resultType.type !== 'TSTypeReference' ||
!resultType.typeName ||
(resultType.typeName.type === 'Identifier' && resultType.typeName.name !== 'Result')) {
context.report({
node,
messageId: 'wrongReturnType',
});
}
}
},
};
},
};