async def component_coro_wrapper(coro_func: CoroutineFunction[T],
                                 *args: Any, commands: PipeEnd, events: PipeEnd, **kwargs: Any) -> T:
    """\
    This function wraps a component workload to conform to the required lifecycle.
    The following behavior is the passed coroutine function's responsibility:

    - it must send `Component.EVENT_START` to its owner after the task is initialized;
    - it must not send EOF on the event pipe;
    - when `Component.COMMAND_STOP` is received, it should either stop eventually, or send an event to its owner;
    - when it is cancelled, it should either stop eventually, or send an event to its owner.

    Also, any `Component.LifecycleError` raised will be wrapped in `Component.Failure`.
    """
    try:
        return await coro_func(*args, commands=commands, events=events, **kwargs)
    except Exception as err:
        raise Component.Failure(err) from None
    finally:
        try:
            await events.send(eof=True)
        except EOFError as err:
            raise Component.LifecycleError("component closed events pipe manually") from err